@@ -92,6 +92,41 @@ definitions:
-
name: ifi-change
type: u32
+ -
+ name: ifaddrmsg
+ type: struct
+ members:
+ -
+ name: ifa-family
+ type: u8
+ -
+ name: ifa-prefixlen
+ type: u8
+ -
+ name: ifa-flags
+ type: u8
+ -
+ name: ifa-scope
+ type: u8
+ -
+ name: ifa-index
+ type: u32
+ -
+ name: ifacacheinfo
+ type: struct
+ members:
+ -
+ name: ifa-prefered
+ type: u32
+ -
+ name: ifa-valid
+ type: u32
+ -
+ name: cstamp
+ type: u32
+ -
+ name: tstamp
+ type: u32
-
name: ifla-bridge-id
type: struct
@@ -2253,6 +2288,18 @@ attribute-sets:
-
name: tailroom
type: u16
+ -
+ name: ifmcaddr-attrs
+ attributes:
+ -
+ name: addr
+ type: binary
+ value: 7
+ -
+ name: cacheinfo
+ type: binary
+ struct: ifacacheinfo
+ value: 6
sub-messages:
-
@@ -2493,6 +2540,29 @@ operations:
reply:
value: 92
attributes: *link-stats-attrs
+ -
+ name: getmaddrs
+ doc: Get / dump IPv4/IPv6 multicast addresses.
+ attribute-set: ifmcaddr-attrs
+ fixed-header: ifaddrmsg
+ do:
+ request:
+ value: 58
+ attributes:
+ - ifa-family
+ - ifa-index
+ reply:
+ value: 58
+ attributes: &mcaddr-attrs
+ - addr
+ - cacheinfo
+ dump:
+ request:
+ value: 58
+ - ifa-family
+ reply:
+ value: 58
+ attributes: *mcaddr-attrs
mcast-groups:
list:
@@ -16,6 +16,7 @@
#include <linux/ip.h>
#include <linux/refcount.h>
#include <linux/sockptr.h>
+#include <net/addrconf.h>
#include <uapi/linux/igmp.h>
static inline struct igmphdr *igmp_hdr(const struct sk_buff *skb)
@@ -92,6 +93,16 @@ struct ip_mc_list {
struct rcu_head rcu;
};
+struct inet_fill_args {
+ u32 portid;
+ u32 seq;
+ int event;
+ unsigned int flags;
+ int netnsid;
+ int ifindex;
+ enum addr_type_t type;
+};
+
/* V3 exponential field decoding */
#define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
#define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
@@ -142,4 +153,7 @@ extern void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr);
int ip_mc_check_igmp(struct sk_buff *skb);
+int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev,
+ const struct ip_mc_list *im,
+ struct inet_fill_args *args);
#endif
@@ -107,15 +107,6 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
[IFA_PROTO] = { .type = NLA_U8 },
};
-struct inet_fill_args {
- u32 portid;
- u32 seq;
- int event;
- unsigned int flags;
- int netnsid;
- int ifindex;
-};
-
#define IN4_ADDR_HSIZE_SHIFT 8
#define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT)
@@ -1846,9 +1837,38 @@ static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
return 0;
}
-static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
- struct netlink_callback *cb, int *s_ip_idx,
- struct inet_fill_args *fillargs)
+static int in_dev_dump_ifmcaddr(struct in_device *in_dev, struct sk_buff *skb,
+ struct netlink_callback *cb, int *s_ip_idx,
+ struct inet_fill_args *fillargs)
+{
+ struct ip_mc_list *im;
+ int ip_idx = 0;
+ int err;
+
+ for (im = rcu_dereference(in_dev->mc_list);
+ im;
+ im = rcu_dereference(im->next_rcu)) {
+ if (ip_idx < *s_ip_idx) {
+ ip_idx++;
+ continue;
+ }
+ err = inet_fill_ifmcaddr(skb, in_dev->dev, im, fillargs);
+ if (err < 0)
+ goto done;
+
+ nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+ ip_idx++;
+ }
+ err = 0;
+ ip_idx = 0;
+done:
+ *s_ip_idx = ip_idx;
+ return err;
+}
+
+static int in_dev_dump_ifaddr(struct in_device *in_dev, struct sk_buff *skb,
+ struct netlink_callback *cb, int *s_ip_idx,
+ struct inet_fill_args *fillargs)
{
struct in_ifaddr *ifa;
int ip_idx = 0;
@@ -1874,6 +1894,23 @@ static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
return err;
}
+static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
+ struct netlink_callback *cb, int *s_ip_idx,
+ struct inet_fill_args *fillargs)
+{
+ switch (fillargs->type) {
+ case UNICAST_ADDR:
+ fillargs->event = RTM_NEWADDR;
+ return in_dev_dump_ifaddr(in_dev, skb, cb, s_ip_idx, fillargs);
+ case MULTICAST_ADDR:
+ fillargs->event = RTM_GETMULTICAST;
+ return in_dev_dump_ifmcaddr(in_dev, skb, cb, s_ip_idx,
+ fillargs);
+ default:
+ return 0;
+ }
+}
+
/* Combine dev_addr_genid and dev_base_seq to detect changes.
*/
static u32 inet_base_seq(const struct net *net)
@@ -1889,15 +1926,16 @@ static u32 inet_base_seq(const struct net *net)
return res;
}
-static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+static int inet_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
+ enum addr_type_t type)
{
const struct nlmsghdr *nlh = cb->nlh;
struct inet_fill_args fillargs = {
.portid = NETLINK_CB(cb->skb).portid,
.seq = nlh->nlmsg_seq,
- .event = RTM_NEWADDR,
.flags = NLM_F_MULTI,
.netnsid = -1,
+ .type = type,
};
struct net *net = sock_net(skb->sk);
struct net *tgt_net = net;
@@ -1949,6 +1987,20 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
return err;
}
+static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ enum addr_type_t type = UNICAST_ADDR;
+
+ return inet_dump_addr(skb, cb, type);
+}
+
+static int inet_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ enum addr_type_t type = MULTICAST_ADDR;
+
+ return inet_dump_addr(skb, cb, type);
+}
+
static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
u32 portid)
{
@@ -2845,6 +2897,8 @@ static const struct rtnl_msg_handler devinet_rtnl_msg_handlers[] __initconst = {
{.protocol = PF_INET, .msgtype = RTM_GETNETCONF,
.doit = inet_netconf_get_devconf, .dumpit = inet_netconf_dump_devconf,
.flags = RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED},
+ {.owner = THIS_MODULE, .protocol = PF_INET, .msgtype = RTM_GETMULTICAST,
+ .dumpit = inet_dump_ifmcaddr, .flags = RTNL_FLAG_DUMP_UNLOCKED},
};
void __init devinet_init(void)
@@ -1432,14 +1432,16 @@ static void ip_mc_hash_remove(struct in_device *in_dev,
*mc_hash = im->next_hash;
}
-static int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev,
- const struct ip_mc_list *im, int event)
+int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev,
+ const struct ip_mc_list *im,
+ struct inet_fill_args *args)
{
struct ifa_cacheinfo ci;
struct ifaddrmsg *ifm;
struct nlmsghdr *nlh;
- nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct ifaddrmsg), 0);
+ nlh = nlmsg_put(skb, args->portid, args->seq, args->event,
+ sizeof(struct ifaddrmsg), args->flags);
if (!nlh)
return -EMSGSIZE;
@@ -1468,6 +1470,9 @@ static int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev,
static void inet_ifmcaddr_notify(struct net_device *dev,
const struct ip_mc_list *im, int event)
{
+ struct inet_fill_args fillargs = {
+ .event = event,
+ };
struct net *net = dev_net(dev);
struct sk_buff *skb;
int err = -ENOMEM;
@@ -1479,7 +1484,7 @@ static void inet_ifmcaddr_notify(struct net_device *dev,
if (!skb)
goto error;
- err = inet_fill_ifmcaddr(skb, dev, im, event);
+ err = inet_fill_ifmcaddr(skb, dev, im, &fillargs);
if (err < 0) {
WARN_ON_ONCE(err == -EMSGSIZE);
nlmsg_free(skb);
@@ -36,6 +36,7 @@ TEST_PROGS += cmsg_so_priority.sh
TEST_PROGS += cmsg_time.sh cmsg_ipv6.sh
TEST_PROGS += netns-name.sh
TEST_PROGS += nl_netdev.py
+TEST_PROGS += rtnetlink.py
TEST_PROGS += srv6_end_dt46_l3vpn_test.sh
TEST_PROGS += srv6_end_dt4_l3vpn_test.sh
TEST_PROGS += srv6_end_dt6_l3vpn_test.sh
new file mode 100755
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+from lib.py import ksft_exit, ksft_run, ksft_ge, RtnlFamily
+import socket
+
+IPV4_ALL_HOSTS_MULTICAST = b'\xe0\x00\x00\x01'
+
+def dump_mcaddr_check(rtnl: RtnlFamily) -> None:
+ """
+ Verify that at least one interface has the IPv4 all-hosts multicast address.
+ At least the loopback interface should have this address.
+ """
+
+ addresses = rtnl.getmaddrs({"ifa-family": socket.AF_INET}, dump=True)
+
+ all_host_multicasts = [
+ addr for addr in addresses if addr['addr'] == IPV4_ALL_HOSTS_MULTICAST
+ ]
+
+ ksft_ge(len(all_host_multicasts), 1,
+ "No interface found with the IPv4 all-hosts multicast address")
+
+def main() -> None:
+ rtnl = RtnlFamily()
+ ksft_run([dump_mcaddr_check], args=(rtnl, ))
+ ksft_exit()
+
+if __name__ == "__main__":
+ main()