* [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps
2026-09-05 9:39 [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Yuyang Huang
@ 2026-09-05 9:39 ` Yuyang Huang
2026-09-08 15:56 ` David Ahern
2026-09-10 3:41 ` netdev-bot+sashiko
2026-09-05 9:39 ` [PATCH net-next 2/3] netlink: specs: rt-addr: document " Yuyang Huang
` (2 subsequent siblings)
3 siblings, 2 replies; 13+ messages in thread
From: Yuyang Huang @ 2026-09-05 9:39 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, David Ahern, Donald Hunter, Eric Dumazet,
Ido Schimmel, Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis,
Paolo Abeni, Sabrina Dubroca, Shuah Khan, Simon Horman,
Stanislav Fomichev, linux-kernel, linux-kselftest, netdev
RTM_GETMULTICAST dumps IPv4 and IPv6 multicast group memberships, but
the device multicast list (dev->mc) is only available through
/proc/net/dev_mcast, so "ip maddr show" still has to parse procfs for
its link-layer entries.
Handle RTM_GETMULTICAST dumps with ifa_family set to AF_PACKET and
report every entry of dev->mc in the existing ifaddrmsg format:
- IFA_MULTICAST carries the raw link-layer address
- IFA_MC_USERS carries the entry reference count
- IFA_F_PERMANENT marks entries added with SIOCADDMULTI
(netdev_hw_addr::global_use, "static" in "ip maddr")
- ifa_scope is RT_SCOPE_LINK
This covers every column of /proc/net/dev_mcast. Strict requests are
validated like the IPv4 dump, except that no attributes are accepted;
a non-zero ifa_index restricts the dump to that device. The dump runs
under RCU and netif_addr_lock_bh() and does not need RTNL.
Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
net/core/rtnetlink.c | 132 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 132 insertions(+)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 81c5a6104dea..5e83232c1504 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -4566,6 +4566,136 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len ? : ret;
}
+static int rtnl_fill_mcaddr(struct sk_buff *skb, const struct net_device *dev,
+ const struct netdev_hw_addr *ha, u32 portid,
+ u32 seq, unsigned int flags)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+
+ nlh = nlmsg_put(skb, portid, seq, RTM_GETMULTICAST, sizeof(*ifm),
+ flags);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ ifm = nlmsg_data(nlh);
+ ifm->ifa_family = AF_PACKET;
+ ifm->ifa_prefixlen = 0;
+ ifm->ifa_flags = ha->global_use ? IFA_F_PERMANENT : 0;
+ ifm->ifa_scope = RT_SCOPE_LINK;
+ ifm->ifa_index = dev->ifindex;
+
+ if (nla_put(skb, IFA_MULTICAST, dev->addr_len, ha->addr) ||
+ nla_put_u32(skb, IFA_MC_USERS, ha->refcount)) {
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+ }
+
+ nlmsg_end(skb, nlh);
+ return 0;
+}
+
+static int rtnl_dump_mcaddr_dev(struct net_device *dev, struct sk_buff *skb,
+ struct netlink_callback *cb, int *s_addr_idx,
+ unsigned int flags)
+{
+ struct netdev_hw_addr *ha;
+ int addr_idx = 0;
+ int err = 0;
+
+ netif_addr_lock_bh(dev);
+ netdev_for_each_mc_addr(ha, dev) {
+ if (addr_idx < *s_addr_idx) {
+ addr_idx++;
+ continue;
+ }
+ err = rtnl_fill_mcaddr(skb, dev, ha, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, flags);
+ if (err < 0)
+ break;
+ addr_idx++;
+ }
+ netif_addr_unlock_bh(dev);
+
+ *s_addr_idx = err < 0 ? addr_idx : 0;
+
+ return err;
+}
+
+static int rtnl_valid_dump_mcaddr_req(const struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack,
+ int *pifindex)
+{
+ struct ifaddrmsg *ifm;
+
+ ifm = nlmsg_payload(nlh, sizeof(*ifm));
+ if (!ifm) {
+ NL_SET_ERR_MSG(extack,
+ "Invalid header for multicast dump request");
+ return -EINVAL;
+ }
+
+ if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
+ NL_SET_ERR_MSG(extack,
+ "Invalid values in multicast dump header");
+ return -EINVAL;
+ }
+
+ if (nlmsg_attrlen(nlh, sizeof(*ifm))) {
+ NL_SET_ERR_MSG(extack,
+ "Invalid data after multicast dump header");
+ return -EINVAL;
+ }
+
+ *pifindex = ifm->ifa_index;
+
+ return 0;
+}
+
+static int rtnl_dump_mcaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ unsigned int flags = NLM_F_MULTI;
+ struct {
+ unsigned long ifindex;
+ int addr_idx;
+ } *ctx = (void *)cb->ctx;
+ struct net_device *dev;
+ int ifindex = 0;
+ int err = 0;
+
+ if (cb->strict_check) {
+ err = rtnl_valid_dump_mcaddr_req(cb->nlh, cb->extack,
+ &ifindex);
+ if (err < 0)
+ return err;
+ }
+
+ rcu_read_lock();
+
+ if (ifindex) {
+ cb->answer_flags |= NLM_F_DUMP_FILTERED;
+ flags |= NLM_F_DUMP_FILTERED;
+ dev = dev_get_by_index_rcu(net, ifindex);
+ if (!dev) {
+ err = -ENODEV;
+ goto out;
+ }
+ err = rtnl_dump_mcaddr_dev(dev, skb, cb, &ctx->addr_idx, flags);
+ goto out;
+ }
+
+ for_each_netdev_dump(net, dev, ctx->ifindex) {
+ err = rtnl_dump_mcaddr_dev(dev, skb, cb, &ctx->addr_idx,
+ flags);
+ if (err < 0)
+ break;
+ }
+out:
+ rcu_read_unlock();
+ return err;
+}
+
struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
unsigned int change,
u32 event, gfp_t flags, int *new_nsid,
@@ -7251,6 +7381,8 @@ static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst =
{.msgtype = RTM_SETSTATS, .doit = rtnl_stats_set},
{.msgtype = RTM_NEWLINKPROP, .doit = rtnl_newlinkprop},
{.msgtype = RTM_DELLINKPROP, .doit = rtnl_dellinkprop},
+ {.protocol = PF_PACKET, .msgtype = RTM_GETMULTICAST,
+ .dumpit = rtnl_dump_mcaddr, .flags = RTNL_FLAG_DUMP_UNLOCKED},
{.protocol = PF_BRIDGE, .msgtype = RTM_GETLINK,
.dumpit = rtnl_bridge_getlink},
{.protocol = PF_BRIDGE, .msgtype = RTM_DELLINK,
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps
2026-09-05 9:39 ` [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps Yuyang Huang
@ 2026-09-08 15:56 ` David Ahern
2026-09-09 1:15 ` Yuyang Huang
2026-09-10 3:41 ` netdev-bot+sashiko
1 sibling, 1 reply; 13+ messages in thread
From: David Ahern @ 2026-09-08 15:56 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, Donald Hunter, Eric Dumazet, Ido Schimmel,
Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis, Paolo Abeni,
Sabrina Dubroca, Shuah Khan, Simon Horman, Stanislav Fomichev,
linux-kernel, linux-kselftest, netdev
On 9/5/26 4:39 AM, Yuyang Huang wrote:
> +static int rtnl_dump_mcaddr(struct sk_buff *skb, struct netlink_callback *cb)
> +{
> + struct net *net = sock_net(skb->sk);
> + unsigned int flags = NLM_F_MULTI;
> + struct {
> + unsigned long ifindex;
> + int addr_idx;
> + } *ctx = (void *)cb->ctx;
> + struct net_device *dev;
> + int ifindex = 0;
> + int err = 0;
> +
> + if (cb->strict_check) {
this is new code. It should only work with strict checking.
> + err = rtnl_valid_dump_mcaddr_req(cb->nlh, cb->extack,
> + &ifindex);
> + if (err < 0)
> + return err;
> + }
> +
> + rcu_read_lock();
> +
> + if (ifindex) {
> + cb->answer_flags |= NLM_F_DUMP_FILTERED;
> + flags |= NLM_F_DUMP_FILTERED;
> + dev = dev_get_by_index_rcu(net, ifindex);
> + if (!dev) {
> + err = -ENODEV;
> + goto out;
> + }
> + err = rtnl_dump_mcaddr_dev(dev, skb, cb, &ctx->addr_idx, flags);
> + goto out;
> + }
> +
> + for_each_netdev_dump(net, dev, ctx->ifindex) {
> + err = rtnl_dump_mcaddr_dev(dev, skb, cb, &ctx->addr_idx,
> + flags);
> + if (err < 0)
> + break;
> + }
> +out:
> + rcu_read_unlock();
> + return err;
> +}
> +
> struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
> unsigned int change,
> u32 event, gfp_t flags, int *new_nsid,
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps
2026-09-08 15:56 ` David Ahern
@ 2026-09-09 1:15 ` Yuyang Huang
0 siblings, 0 replies; 13+ messages in thread
From: Yuyang Huang @ 2026-09-09 1:15 UTC (permalink / raw)
To: David Ahern
Cc: David S. Miller, Donald Hunter, Eric Dumazet, Ido Schimmel,
Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis, Paolo Abeni,
Sabrina Dubroca, Shuah Khan, Simon Horman, Stanislav Fomichev,
linux-kernel, linux-kselftest, netdev
On Wed, Sep 9, 2026 at 12:56 AM David Ahern <dsahern@kernel.org> wrote:
>
> On 9/5/26 4:39 AM, Yuyang Huang wrote:
> > +static int rtnl_dump_mcaddr(struct sk_buff *skb, struct netlink_callback *cb)
> > +{
> > + struct net *net = sock_net(skb->sk);
> > + unsigned int flags = NLM_F_MULTI;
> > + struct {
> > + unsigned long ifindex;
> > + int addr_idx;
> > + } *ctx = (void *)cb->ctx;
> > + struct net_device *dev;
> > + int ifindex = 0;
> > + int err = 0;
> > +
> > + if (cb->strict_check) {
>
> this is new code. It should only work with strict checking.
Thanks for the advice, I will fix it in patchset v2.
Thanks,
Yuyang
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps
2026-09-05 9:39 ` [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps Yuyang Huang
2026-09-08 15:56 ` David Ahern
@ 2026-09-10 3:41 ` netdev-bot+sashiko
2026-09-10 4:04 ` Yuyang Huang
1 sibling, 1 reply; 13+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 3:41 UTC (permalink / raw)
To: sigefriedhyy
Cc: davem, dsahern, donald.hunter, edumazet, idosch, kuba, kuniyu,
nickgarlis, pabeni, sd, shuah, horms, sdf.kernel, linux-kernel,
linux-kselftest, netdev
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 2 · Low: 2
- [Medium] rtnl_dump_mcaddr()/rtnl_dump_mcaddr_dev()
(net/core/rtnetlink.c) resume a truncated dump purely positionally —…
- [Medium] rtnl_fill_mcaddr() (net/core/rtnetlink.c) copies dev->addr_len
bytes out of ha->addr[MAX_ADDR_LEN] into the IFA_MULTICAST attribute:…
- [Low] Claimed uAPI/YAML divergence: Documentation/netlink/specs/rt-
addr.yaml documents op `getmulticast` as 'Get / dump IPv4/IPv6…
- [Low] Claimed spec/implementation mismatch: rt-addr.yaml lists
`cacheinfo` in the getmulticast reply attribute set (mcaddr-attrs) and…
--
rtnetlink: add AF_PACKET multicast dumps
This adds a PF_PACKET RTM_GETMULTICAST dump handler that walks dev->mc for
every device in the netns and reports each netdev_hw_addr in the existing
ifaddrmsg format with IFA_MULTICAST and IFA_MC_USERS. The dump runs under
RCU plus netif_addr_lock_bh() and is registered with
RTNL_FLAG_DUMP_UNLOCKED.
A few questions below.
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index 81c5a6104dea1..5e83232c15048 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -4566,6 +4566,136 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
> return skb->len ? : ret;
> }
>
> +static int rtnl_fill_mcaddr(struct sk_buff *skb, const struct net_device *dev,
> + const struct netdev_hw_addr *ha, u32 portid,
> + u32 seq, unsigned int flags)
> +{
[ ... ]
> + if (nla_put(skb, IFA_MULTICAST, dev->addr_len, ha->addr) ||
> + nla_put_u32(skb, IFA_MC_USERS, ha->refcount)) {
[Severity: Medium]
Can this nla_put() copy uninitialized slab bytes into IFA_MULTICAST when
dev->addr_len grew after the entry was created?
netdev_hw_addr entries are allocated with plain kmalloc() and only
addr_len bytes are initialized, and the length used at creation time is
not recorded anywhere:
net/core/dev_addr_lists.c:__hw_addr_create() {
alloc_size = sizeof(*ha);
if (alloc_size < L1_CACHE_BYTES)
alloc_size = L1_CACHE_BYTES;
ha = kmalloc(alloc_size, GFP_ATOMIC);
if (!ha)
return NULL;
memcpy(ha->addr, addr, addr_len);
...
}
__dev_mc_add() passes dev->addr_len as it is at add time:
net/core/dev_addr_lists.c:__dev_mc_add() {
err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
NETDEV_HW_ADDR_T_MULTICAST, global, false,
0, false);
}
while rtnl_fill_mcaddr() emits the current dev->addr_len. One sequence
that produces a mismatch:
1. create a tap device (ARPHRD_ETHER, addr_len 6)
2. SIOCADDMULTI via dev_ifsioc() -> dev_mc_add_global(), which only
requires netif_device_present(), so the device may be down; 6 bytes
of ha->addr are initialized
3. TUNSETLINK with ARPHRD_TUNNEL6 in drivers/net/tun.c:
tun->dev->type = (int) arg;
tun->dev->addr_len = tun_get_addr_len(tun->dev->type);
which raises addr_len to 16 and does not purge dev->mc
4. run the new RTM_GETMULTICAST/AF_PACKET dump
Would step 4 then emit 16 bytes out of a 6-byte-initialized ha->addr,
handing 10 bytes of never-written slab memory to user space? The read
stays inside ha->addr[MAX_ADDR_LEN], so this is a disclosure of
uninitialized memory rather than an out-of-bounds access.
The same bytes are already visible through /proc/net/dev_mcast, which
prints "%*phN" with dev->addr_len, so the underlying unzeroed tail is
older than this patch, but this adds a second path to it. Would it make
sense to either bound the emitted length or zero the address tail in
__hw_addr_create()?
> + nlmsg_cancel(skb, nlh);
> + return -EMSGSIZE;
> + }
> +
> + nlmsg_end(skb, nlh);
> + return 0;
> +}
> +
> +static int rtnl_dump_mcaddr_dev(struct net_device *dev, struct sk_buff *skb,
> + struct netlink_callback *cb, int *s_addr_idx,
> + unsigned int flags)
> +{
> + struct netdev_hw_addr *ha;
> + int addr_idx = 0;
> + int err = 0;
> +
> + netif_addr_lock_bh(dev);
> + netdev_for_each_mc_addr(ha, dev) {
> + if (addr_idx < *s_addr_idx) {
> + addr_idx++;
> + continue;
> + }
> + err = rtnl_fill_mcaddr(skb, dev, ha, NETLINK_CB(cb->skb).portid,
> + cb->nlh->nlmsg_seq, flags);
> + if (err < 0)
> + break;
> + addr_idx++;
> + }
> + netif_addr_unlock_bh(dev);
> +
> + *s_addr_idx = err < 0 ? addr_idx : 0;
[Severity: Medium]
Should this dump also maintain a generation counter and call
nl_dump_check_consistent() so interrupted dumps get NLM_F_DUMP_INTR?
Resume state here is purely positional (ctx->ifindex plus ctx->addr_idx)
and neither RTNL nor netif_addr_lock is held between two invocations of
the callback. netlink_dump() turns the -EMSGSIZE into a continued dump:
net/netlink/af_netlink.c:netlink_dump() {
if (nlk->dump_done_errno == -EMSGSIZE && skb->len)
nlk->dump_done_errno = skb->len;
}
so entries added to or removed from dev->mc in that window shift the
index and entries can be silently repeated or skipped.
cb->seq is never assigned in rtnl_dump_mcaddr(), so the check in
nl_dump_check_consistent():
if (cb->prev_seq && cb->seq != cb->prev_seq)
nlh->nlmsg_flags |= NLM_F_DUMP_INTR;
can never fire and user space has no way to notice the loss. The AF_INET
sibling of the same message type does both halves: inet_dump_addr() sets
cb->seq = inet_base_seq(tgt_net);
and in_dev_dump_ifmcaddr() calls nl_dump_check_consistent(cb,
nlmsg_hdr(skb)) for every emitted message.
> +
> + return err;
> +}
[ ... ]
> +static int rtnl_dump_mcaddr(struct sk_buff *skb, struct netlink_callback *cb)
> +{
[ ... ]
> + for_each_netdev_dump(net, dev, ctx->ifindex) {
> + err = rtnl_dump_mcaddr_dev(dev, skb, cb, &ctx->addr_idx,
> + flags);
> + if (err < 0)
> + break;
> + }
[Severity: Medium]
Related to the above, can a stale ctx->addr_idx be applied to the wrong
device here?
On -EMSGSIZE, rtnl_dump_mcaddr_dev() keeps the offset in ctx->addr_idx and
the break skips the ifindex++ of for_each_netdev_dump(), so ctx->ifindex
still names the interrupted device. If that device is unregistered before
the dump resumes:
include/linux/netdevice.h:
#define for_each_netdev_dump(net, d, ifindex) \
for (; (d = xa_find(&(net)->dev_by_index, &ifindex, \
ULONG_MAX, XA_PRESENT)); ifindex++)
xa_find() with XA_PRESENT returns the next device that does exist, and the
non-zero ctx->addr_idx is passed unchanged into rtnl_dump_mcaddr_dev(), so
if (addr_idx < *s_addr_idx) {
addr_idx++;
continue;
}
would skip the first ctx->addr_idx multicast entries of an unrelated
device. Should ctx->addr_idx be reset when the resumed ifindex does not
match the recorded one?
> +out:
> + rcu_read_unlock();
> + return err;
> +}
> +
[ ... ]
> @@ -7251,6 +7381,8 @@ static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst =
> {.msgtype = RTM_SETSTATS, .doit = rtnl_stats_set},
> {.msgtype = RTM_NEWLINKPROP, .doit = rtnl_newlinkprop},
> {.msgtype = RTM_DELLINKPROP, .doit = rtnl_dellinkprop},
> + {.protocol = PF_PACKET, .msgtype = RTM_GETMULTICAST,
> + .dumpit = rtnl_dump_mcaddr, .flags = RTNL_FLAG_DUMP_UNLOCKED},
[Severity: Low]
This isn't a bug, but as of this commit alone the getmulticast op in
Documentation/netlink/specs/rt-addr.yaml still reads "Get / dump
IPv4/IPv6 multicast addresses." while the same op now also serves
AF_PACKET requests with link-layer address semantics.
The follow-up patch in the series, "netlink: specs: rt-addr: document
AF_PACKET multicast dumps", rewrites that doc to cover ifa-family
selecting AF_INET, AF_INET6 or AF_PACKET and the permanent-flag meaning,
so the spec and the implementation do agree at the end of the series.
Only noting it in case the two patches are ever applied separately.
> {.protocol = PF_BRIDGE, .msgtype = RTM_GETLINK,
> .dumpit = rtnl_bridge_getlink},
> {.protocol = PF_BRIDGE, .msgtype = RTM_DELLINK,
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260905093932.43726-1-sigefriedhyy%40gmail.com
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps
2026-09-10 3:41 ` netdev-bot+sashiko
@ 2026-09-10 4:04 ` Yuyang Huang
0 siblings, 0 replies; 13+ messages in thread
From: Yuyang Huang @ 2026-09-10 4:04 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: davem, dsahern, donald.hunter, edumazet, idosch, kuba, kuniyu,
nickgarlis, pabeni, sd, shuah, horms, sdf.kernel, linux-kernel,
linux-kselftest, netdev
The comment in this sashiko round is on an old patchset. I think the
following comment is worth fixing.
> Should ctx->addr_idx be reset when the resumed ifindex does not
> match the recorded one?
Good catch, yes. I'll reset it in v4 when the resumed device is not
the one the dump stopped at. inet_dump_addr() has the same issue with
ip_idx, I'll send a separate fix for it.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH net-next 2/3] netlink: specs: rt-addr: document AF_PACKET multicast dumps
2026-09-05 9:39 [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Yuyang Huang
2026-09-05 9:39 ` [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps Yuyang Huang
@ 2026-09-05 9:39 ` Yuyang Huang
2026-09-05 9:39 ` [PATCH net-next 3/3] selftests: net: test " Yuyang Huang
2026-09-07 12:51 ` [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Nicolas Dichtel
3 siblings, 0 replies; 13+ messages in thread
From: Yuyang Huang @ 2026-09-05 9:39 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, David Ahern, Donald Hunter, Eric Dumazet,
Ido Schimmel, Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis,
Paolo Abeni, Sabrina Dubroca, Shuah Khan, Simon Horman,
Stanislav Fomichev, linux-kernel, linux-kselftest, netdev
Mention that ifa-family AF_PACKET dumps link-layer multicast addresses
and what the permanent flag means for them.
Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
Documentation/netlink/specs/rt-addr.yaml | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Documentation/netlink/specs/rt-addr.yaml b/Documentation/netlink/specs/rt-addr.yaml
index 0ecbd24c890c..2a2310cb0df0 100644
--- a/Documentation/netlink/specs/rt-addr.yaml
+++ b/Documentation/netlink/specs/rt-addr.yaml
@@ -168,7 +168,12 @@ operations:
attributes: *ifaddr-all
-
name: getmulticast
- doc: Get / dump IPv4/IPv6 multicast addresses.
+ doc: |
+ Get / dump multicast addresses. ifa-family selects the address
+ family: AF_INET or AF_INET6 for the IP multicast groups joined on
+ a device, AF_PACKET for the link-layer multicast addresses in the
+ device filter. Link-layer entries added with SIOCADDMULTI are
+ reported with the permanent flag set.
attribute-set: addr-attrs
fixed-header: ifaddrmsg
do:
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 3/3] selftests: net: test AF_PACKET multicast dumps
2026-09-05 9:39 [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Yuyang Huang
2026-09-05 9:39 ` [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps Yuyang Huang
2026-09-05 9:39 ` [PATCH net-next 2/3] netlink: specs: rt-addr: document " Yuyang Huang
@ 2026-09-05 9:39 ` Yuyang Huang
2026-09-10 3:41 ` netdev-bot+sashiko
2026-09-07 12:51 ` [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Nicolas Dichtel
3 siblings, 1 reply; 13+ messages in thread
From: Yuyang Huang @ 2026-09-05 9:39 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, David Ahern, Donald Hunter, Eric Dumazet,
Ido Schimmel, Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis,
Paolo Abeni, Sabrina Dubroca, Shuah Khan, Simon Horman,
Stanislav Fomichev, linux-kernel, linux-kselftest, netdev
Dump the link-layer multicast addresses of a dummy device and verify
that ifa_index restricts the dump to that device, that the all-hosts
address joined on link up is listed without IFA_F_PERMANENT and that an
address added with SIOCADDMULTI is listed with IFA_F_PERMANENT and
IFA_MC_USERS. Skip when the kernel does not support AF_PACKET dumps.
Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
tools/testing/selftests/net/rtnetlink.py | 58 ++++++++++++++++++++++--
1 file changed, 55 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/net/rtnetlink.py b/tools/testing/selftests/net/rtnetlink.py
index 5cc3ebdcf08d..94340dac0e21 100755
--- a/tools/testing/selftests/net/rtnetlink.py
+++ b/tools/testing/selftests/net/rtnetlink.py
@@ -1,17 +1,21 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
+import errno
import socket
import struct
import time
from lib.py import bkg, ip, ksft_exit, ksft_run, ksft_eq, ksft_ge, ksft_true, KsftSkipEx
-from lib.py import ksft_not_in, ksft_not_none
-from lib.py import CmdExitFailure, NetNS, NetNSEnter, RtnlAddrFamily, RtnlRouteFamily
+from lib.py import ksft_in, ksft_not_in, ksft_not_none
+from lib.py import CmdExitFailure, NetNS, NetNSEnter, NlError, RtnlAddrFamily, RtnlRouteFamily
from lib.py import defer
IPV4_ALL_HOSTS_MULTICAST = b'\xe0\x00\x00\x01'
IPV4_TEST_MULTICAST = b'\xef\x01\x01\x01'
IPV6_TEST_MULTICAST = bytes.fromhex('ff020000000000000000000000000123')
+ETH_ALL_HOSTS_MULTICAST = bytes.fromhex('01005e000001')
+ETH_TEST_MULTICAST_STR = '01:00:5e:01:01:01'
+ETH_TEST_MULTICAST = bytes.fromhex(ETH_TEST_MULTICAST_STR.replace(':', ''))
def _users_for(rtnl: RtnlAddrFamily, family: int, grp: bytes, ifindex: int):
@@ -105,6 +109,53 @@ def dump_mcaddr6_check() -> None:
s2.close()
+def dump_mcaddr_l2_check() -> None:
+ """
+ Verify link-layer multicast addresses in an AF_PACKET RTM_GETMULTICAST
+ dump: the ifa-index filter, mc-users and the permanent flag.
+ """
+
+ with NetNS() as ns:
+ with NetNSEnter(str(ns)):
+ for ifname in ("dummy1", "dummy2"):
+ ip(f"link add name {ifname} type dummy")
+ ip(f"link set {ifname} up")
+ dev_idx = socket.if_nametoindex("dummy1")
+ ip(f"maddr add {ETH_TEST_MULTICAST_STR} dev dummy1")
+
+ rtnl = RtnlAddrFamily()
+ try:
+ addresses = rtnl.getmulticast(
+ {"ifa-family": socket.AF_PACKET, "ifa-index": dev_idx},
+ dump=True)
+ except NlError as e:
+ if e.error == errno.EOPNOTSUPP:
+ raise KsftSkipEx(
+ "kernel does not support AF_PACKET multicast dump")
+ raise
+
+ # dummy2 has entries as well, only dummy1 may be listed
+ ksft_eq({addr['ifa-index'] for addr in addresses}, {dev_idx},
+ "AF_PACKET multicast dump ignored ifa-index filter")
+
+ entries = {addr['multicast']: addr for addr in addresses}
+
+ # Bringing an Ethernet device up joins 224.0.0.1, which maps
+ # to 01:00:5e:00:00:01 in the device multicast list.
+ ksft_in(ETH_ALL_HOSTS_MULTICAST, entries,
+ "dummy1 does not have the all-hosts link-layer address")
+ ksft_not_in('permanent',
+ entries[ETH_ALL_HOSTS_MULTICAST]['ifa-flags'],
+ "protocol entry is permanent")
+
+ ksft_in(ETH_TEST_MULTICAST, entries,
+ "dummy1 does not have the SIOCADDMULTI address")
+ ksft_eq(entries[ETH_TEST_MULTICAST]['mc-users'], 1,
+ "unexpected mc-users for the SIOCADDMULTI address")
+ ksft_in('permanent', entries[ETH_TEST_MULTICAST]['ifa-flags'],
+ "SIOCADDMULTI entry is not permanent")
+
+
def ipv4_devconf_notify() -> None:
"""
Configure an interface and set ipv4-devconf values through netlink
@@ -315,7 +366,8 @@ def ipv6_route_del_reason_absent() -> None:
def main() -> None:
- ksft_run([dump_mcaddr_check, dump_mcaddr6_check, ipv4_devconf_notify,
+ ksft_run([dump_mcaddr_check, dump_mcaddr6_check, dump_mcaddr_l2_check,
+ ipv4_devconf_notify,
ipv6_route_del_reason_expired,
ipv6_route_del_reason_ra_withdrawn,
ipv6_route_del_reason_absent])
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH net-next 3/3] selftests: net: test AF_PACKET multicast dumps
2026-09-05 9:39 ` [PATCH net-next 3/3] selftests: net: test " Yuyang Huang
@ 2026-09-10 3:41 ` netdev-bot+sashiko
2026-09-10 4:05 ` Yuyang Huang
0 siblings, 1 reply; 13+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 3:41 UTC (permalink / raw)
To: sigefriedhyy
Cc: davem, dsahern, donald.hunter, edumazet, idosch, kuba, kuniyu,
nickgarlis, pabeni, sd, shuah, horms, sdf.kernel, linux-kernel,
linux-kselftest, netdev
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 0 · Low: 1
- [Low] dump_mcaddr_l2_check() in
tools/testing/selftests/net/rtnetlink.py dereferences dictionary keys…
--
selftests: net: test AF_PACKET multicast dumps
This adds dump_mcaddr_l2_check() to the rtnetlink.py selftest, dumping the
link-layer multicast addresses of a dummy device over AF_PACKET and
checking the ifa-index filter, the all-hosts entry flags and the
SIOCADDMULTI entry flags plus mc-users. The case skips when the kernel
answers the dump with EOPNOTSUPP.
> diff --git a/tools/testing/selftests/net/rtnetlink.py b/tools/testing/selftests/net/rtnetlink.py
> index 5cc3ebdcf08de..94340dac0e217 100755
> --- a/tools/testing/selftests/net/rtnetlink.py
> +++ b/tools/testing/selftests/net/rtnetlink.py
[ ... ]
> @@ -105,6 +109,53 @@ def dump_mcaddr6_check() -> None:
> s2.close()
>
>
> +def dump_mcaddr_l2_check() -> None:
> + """
> + Verify link-layer multicast addresses in an AF_PACKET RTM_GETMULTICAST
> + dump: the ifa-index filter, mc-users and the permanent flag.
> + """
> +
> + with NetNS() as ns:
> + with NetNSEnter(str(ns)):
> + for ifname in ("dummy1", "dummy2"):
> + ip(f"link add name {ifname} type dummy")
> + ip(f"link set {ifname} up")
> + dev_idx = socket.if_nametoindex("dummy1")
> + ip(f"maddr add {ETH_TEST_MULTICAST_STR} dev dummy1")
> +
> + rtnl = RtnlAddrFamily()
> + try:
> + addresses = rtnl.getmulticast(
> + {"ifa-family": socket.AF_PACKET, "ifa-index": dev_idx},
> + dump=True)
> + except NlError as e:
> + if e.error == errno.EOPNOTSUPP:
> + raise KsftSkipEx(
> + "kernel does not support AF_PACKET multicast dump")
> + raise
> +
> + # dummy2 has entries as well, only dummy1 may be listed
> + ksft_eq({addr['ifa-index'] for addr in addresses}, {dev_idx},
> + "AF_PACKET multicast dump ignored ifa-index filter")
> +
> + entries = {addr['multicast']: addr for addr in addresses}
> +
> + # Bringing an Ethernet device up joins 224.0.0.1, which maps
> + # to 01:00:5e:00:00:01 in the device multicast list.
> + ksft_in(ETH_ALL_HOSTS_MULTICAST, entries,
> + "dummy1 does not have the all-hosts link-layer address")
> + ksft_not_in('permanent',
> + entries[ETH_ALL_HOSTS_MULTICAST]['ifa-flags'],
> + "protocol entry is permanent")
> +
> + ksft_in(ETH_TEST_MULTICAST, entries,
> + "dummy1 does not have the SIOCADDMULTI address")
> + ksft_eq(entries[ETH_TEST_MULTICAST]['mc-users'], 1,
> + "unexpected mc-users for the SIOCADDMULTI address")
> + ksft_in('permanent', entries[ETH_TEST_MULTICAST]['ifa-flags'],
> + "SIOCADDMULTI entry is not permanent")
[Severity: Low]
Can the entries[] lookups here raise KeyError in exactly the case the
preceding checks are meant to report?
ksft_in() in tools/testing/selftests/net/lib/py/ksft.py only records the
failure and returns:
def ksft_in(a, b, comment=""):
if a not in b:
_fail("Check failed", a, "not in", b, comment)
So when the dump does not contain 01:00:5e:00:00:01, execution continues
into:
entries[ETH_ALL_HOSTS_MULTICAST]['ifa-flags']
and the same applies to entries[ETH_TEST_MULTICAST] for the mc-users and
permanent checks if the SIOCADDMULTI address is missing.
ksft_run() catches this with "except BaseException" and prints a
traceback, so the case is reported as a failure, but the remaining
independent checks (mc-users == 1 and the permanent flag on the
SIOCADDMULTI entry) are never executed. Note also that the earlier
ksft_eq() on the ifa-index set does not stop the case either, so
addresses may legitimately be empty at this point.
Would it be worth following the convention already used elsewhere in this
file, where _users_for() and dump_mcaddr_check() test membership before
dereferencing:
if 'mc-users' not in matches[0]:
raise KsftSkipEx(...)
and ipv6_route_del_reason_expired() guards with ksft_not_none(attrs)
before touching attrs?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260905093932.43726-1-sigefriedhyy%40gmail.com
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH net-next 3/3] selftests: net: test AF_PACKET multicast dumps
2026-09-10 3:41 ` netdev-bot+sashiko
@ 2026-09-10 4:05 ` Yuyang Huang
0 siblings, 0 replies; 13+ messages in thread
From: Yuyang Huang @ 2026-09-10 4:05 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: davem, dsahern, donald.hunter, edumazet, idosch, kuba, kuniyu,
nickgarlis, pabeni, sd, shuah, horms, sdf.kernel, linux-kernel,
linux-kselftest, netdev
>- [Low] dump_mcaddr_l2_check() in
> tools/testing/selftests/net/rtnetlink.py dereferences dictionary keys…
Same issue reported in v3 thread, already answered there.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses
2026-09-05 9:39 [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Yuyang Huang
` (2 preceding siblings ...)
2026-09-05 9:39 ` [PATCH net-next 3/3] selftests: net: test " Yuyang Huang
@ 2026-09-07 12:51 ` Nicolas Dichtel
2026-09-08 2:56 ` Yuyang Huang
3 siblings, 1 reply; 13+ messages in thread
From: Nicolas Dichtel @ 2026-09-07 12:51 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, David Ahern, Donald Hunter, Eric Dumazet,
Ido Schimmel, Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis,
Paolo Abeni, Sabrina Dubroca, Shuah Khan, Simon Horman,
Stanislav Fomichev, linux-kernel, linux-kselftest, netdev
Le 05/09/2026 à 11:39, Yuyang Huang a écrit :
> "ip maddr show" prints three kinds of entries: link-layer, IPv4 and
> IPv6. The IPv4 and IPv6 ones can be read over netlink today: IPv6 has
> had RTM_GETMULTICAST for a long time and IPv4 got it in eb4e17a1d915
> ("netlink: support dumping IPv4 multicast addresses"), with IFA_MC_USERS
> added later so the user count no longer has to come from procfs.
> > The link-layer list is the missing piece. dev->mc, the addresses
> programmed into the device filter, is only exported via
> /proc/net/dev_mcast, so iproute2 still carries a procfs parser just for
These addresses could be listed with 'bridge fdb' (RTM_GETNEIGH on AF_BRIDGE).
Instead of having a new message to get the missing info (users), maybe it would
be better to update the current API.
> that. This series closes the gap so that "ip maddr show" can be served
> from rtnetlink alone.
>
> Patch 1 handles RTM_GETMULTICAST dumps with ifa_family set to AF_PACKET
FWIW, AF_PACKET seems strange to me. Isn't AF_UNSPEC (like for interface, cf
rtnl_fill_ifinfo) more appropriate?
Regards,
Nicolas
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses
2026-09-07 12:51 ` [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Nicolas Dichtel
@ 2026-09-08 2:56 ` Yuyang Huang
2026-09-08 9:23 ` Nicolas Dichtel
0 siblings, 1 reply; 13+ messages in thread
From: Yuyang Huang @ 2026-09-08 2:56 UTC (permalink / raw)
To: nicolas.dichtel
Cc: David S. Miller, David Ahern, Donald Hunter, Eric Dumazet,
Ido Schimmel, Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis,
Paolo Abeni, Sabrina Dubroca, Shuah Khan, Simon Horman,
Stanislav Fomichev, linux-kernel, linux-kselftest, netdev
> These addresses could be listed with 'bridge fdb' (RTM_GETNEIGH on AF_BRIDGE).
> Instead of having a new message to get the missing info (users), maybe it would
> be better to update the current API.
I looked at the FDB dump based on the suggestion and I don't think
that path works well. The missing users count is not the only problem:
It doesn't list dev->mc for every device. Only ndo_dflt_fdb_dump()
walks dev->mc, and rtnl_fdb_dump() calls it only for Ethernet devices
that have no ndo_fdb_dump of their own. So bridge, vxlan, macvlan and
IPoIB devices never show their multicast filter in "bridge fdb show",
while /proc/net/dev_mcast lists them. Fixing that means calling the
default dump for those devices too, which adds new entries to
"bridge fdb show" output on every one of them.
User space can't reliably tell the entries apart either. "ip maddr"
would keep NTF_SELF entries with a multicast lladdr, but it seems vxlan's own
FDB entries seems also carry NTF_SELF, so a multicast MAC added there as a
forwarding rule (bridge fdb add ... dev vxlan0 dst ...) would show up
as a device multicast address, while the real dev->mc of that vxlan
device is missing. And since a dump can't be limited to self entries,
on a host with bridges "ip maddr show" would receive the whole learned
FDB of every port and drop it.
It would still need a new uAPI. All self entries are NUD_PERMANENT, so
the users count and the SIOCADDMULTI (static) bit would be new NDA_*
attributes. On the other hand, this series adds no new attributes;
IFA_MULTICAST, IFA_MC_USERS and IFA_F_PERMANENT already exist for the
IPv4 and IPv6 dumps.
Covering this with RTM_GETNEIGH would need the default dump for
devices with their own ndo_fdb_dump, a way to tell filter entries from
a device's own
NTF_SELF FDB entries, a self-only request filter, and new NDA_*
attributes for users and the static bit. That is more new uAPI than
this series for the same result.
Therefore, I feel that my original proposal seems cleaner and causes
less chrun. But feel free to let me know if I have any
misunderstanding on the suggestion.
> FWIW, AF_PACKET seems strange to me. Isn't AF_UNSPEC (like for interface, cf
> rtnl_fill_ifinfo) more appropriate?
For address dumps AF_UNSPEC already means "all families" (RTM_GETADDR
goes through rtnl_dump_all()), so I'd rather keep it available for
RTM_GETMULTICAST. AF_PACKET is what iproute2 already uses for the link
family: "ip -0" sets preferred_family = AF_PACKET and sends it in
RTM_GETLINK, and ipmaddr.c has always tagged the /proc/net/dev_mcast
entries as AF_PACKET.
Thanks,
Yuyang
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses
2026-09-08 2:56 ` Yuyang Huang
@ 2026-09-08 9:23 ` Nicolas Dichtel
0 siblings, 0 replies; 13+ messages in thread
From: Nicolas Dichtel @ 2026-09-08 9:23 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, David Ahern, Donald Hunter, Eric Dumazet,
Ido Schimmel, Jakub Kicinski, Kuniyuki Iwashima, Nikolaos Gkarlis,
Paolo Abeni, Sabrina Dubroca, Shuah Khan, Simon Horman,
Stanislav Fomichev, linux-kernel, linux-kselftest, netdev
Le 08/09/2026 à 04:56, Yuyang Huang a écrit :
>> These addresses could be listed with 'bridge fdb' (RTM_GETNEIGH on AF_BRIDGE).
>> Instead of having a new message to get the missing info (users), maybe it would
>> be better to update the current API.
>
> I looked at the FDB dump based on the suggestion and I don't think
> that path works well. The missing users count is not the only problem:
>
> It doesn't list dev->mc for every device. Only ndo_dflt_fdb_dump()
> walks dev->mc, and rtnl_fdb_dump() calls it only for Ethernet devices
> that have no ndo_fdb_dump of their own. So bridge, vxlan, macvlan and
> IPoIB devices never show their multicast filter in "bridge fdb show",
> while /proc/net/dev_mcast lists them. Fixing that means calling the
> default dump for those devices too, which adds new entries to
> "bridge fdb show" output on every one of them.
>
> User space can't reliably tell the entries apart either. "ip maddr"
> would keep NTF_SELF entries with a multicast lladdr, but it seems vxlan's own
> FDB entries seems also carry NTF_SELF, so a multicast MAC added there as a
> forwarding rule (bridge fdb add ... dev vxlan0 dst ...) would show up
> as a device multicast address, while the real dev->mc of that vxlan
> device is missing. And since a dump can't be limited to self entries,
> on a host with bridges "ip maddr show" would receive the whole learned
> FDB of every port and drop it.
>
> It would still need a new uAPI. All self entries are NUD_PERMANENT, so
> the users count and the SIOCADDMULTI (static) bit would be new NDA_*
> attributes. On the other hand, this series adds no new attributes;
> IFA_MULTICAST, IFA_MC_USERS and IFA_F_PERMANENT already exist for the
> IPv4 and IPv6 dumps.
>
> Covering this with RTM_GETNEIGH would need the default dump for
> devices with their own ndo_fdb_dump, a way to tell filter entries from
> a device's own
> NTF_SELF FDB entries, a self-only request filter, and new NDA_*
> attributes for users and the static bit. That is more new uAPI than
> this series for the same result.
>
> Therefore, I feel that my original proposal seems cleaner and causes
> less chrun. But feel free to let me know if I have any
> misunderstanding on the suggestion.
Ok, I agree, it seems easier to have another entry point.
>
>> FWIW, AF_PACKET seems strange to me. Isn't AF_UNSPEC (like for interface, cf
>> rtnl_fill_ifinfo) more appropriate?
>
> For address dumps AF_UNSPEC already means "all families" (RTM_GETADDR
> goes through rtnl_dump_all()), so I'd rather keep it available for
> RTM_GETMULTICAST. AF_PACKET is what iproute2 already uses for the link
> family: "ip -0" sets preferred_family = AF_PACKET and sends it in
> RTM_GETLINK, and ipmaddr.c has always tagged the /proc/net/dev_mcast
> entries as AF_PACKET.
I hadn't looked at iproute2. It uses AF_PACKET for link-layer addresses, so that
seems OK too.
Nicolas
^ permalink raw reply [flat|nested] 13+ messages in thread