git: c8d4287e30d6 - main - mtest: Add support for exercising IPv4-mapped groups on IPv6 sockets.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 03 Aug 2026 15:47:40 UTC
The branch main has been updated by bms:
URL: https://cgit.FreeBSD.org/src/commit/?id=c8d4287e30d6dc2ed3d4378133758adabc0030a3
commit c8d4287e30d6dc2ed3d4378133758adabc0030a3
Author: Bruce M Simpson <bms@FreeBSD.org>
AuthorDate: 2026-08-02 08:00:19 +0000
Commit: Bruce M Simpson <bms@FreeBSD.org>
CommitDate: 2026-08-03 15:47:24 +0000
mtest: Add support for exercising IPv4-mapped groups on IPv6 sockets.
This is in lieu of a full Kyua/ATF regression test, as this is an optional
feature that was beyond the scope of IETF's normative references for IPv6
multicast; support has been strictly on a best-effort basis.
Two new commands are added to mtest(8):
u mcast-addr ifname - join IPv4-mapped group on IPv6 socket
v mcast-addr ifname - leave IPv4-mapped group on IPv6 socket
Add an internal helper function __in6_v4_to_v4mapped() to perform the
converse of the IN6_IS_ADDR_V4MAPPED() check to support this use case.
Whilst __in6_v4_to_v4mapped() returns its first argument as a convenience,
avoid the temptation to dereference a pointer to that which we already hold.
Strictly the use of sockunion_t within mtest(8) more generally is a form
of controlled type punning (aliasing). Use a temporary as we overwrite
contents of su; the resultant write would overlap memory locations.
PR: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=193246
Differential Revision: https://reviews.freebsd.org/D58589
---
usr.sbin/mtest/mtest.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/usr.sbin/mtest/mtest.c b/usr.sbin/mtest/mtest.c
index 941379b01bc9..e15068644043 100644
--- a/usr.sbin/mtest/mtest.c
+++ b/usr.sbin/mtest/mtest.c
@@ -104,6 +104,10 @@ typedef union mrequnion mrequnion_t;
#ifdef INET
static int __ifindex_to_primary_ip(const uint32_t, struct in_addr *);
#endif
+#if defined(INET) && defined(INET6)
+static struct in6_addr *
+ __in6_v4_to_v4mapped(struct in6_addr *, struct in_addr *);
+#endif
static uint32_t parse_cmd_args(sockunion_t *, sockunion_t *,
const char *, const char *, const char *);
static void process_file(char *, int, int);
@@ -143,6 +147,23 @@ su_cmp(const void *a, const void *b)
return (memcmp(sua, sub, sua->sa.sa_len));
}
+#if defined(INET) && defined(INET6)
+/*
+ * Internal: Express an IPv4 address within IPv6 as IPv4-mapped.
+ * Converse of IN6_IS_ADDR_V4MAPPED(a).
+ */
+static struct in6_addr *
+__in6_v4_to_v4mapped(struct in6_addr *a6 , struct in_addr *a4)
+{
+
+ a6->s6_addr32[0] = 0;
+ a6->s6_addr32[1] = 0;
+ a6->s6_addr32[2] = ntohl(0x0000ffff);
+ a6->s6_addr32[3] = a4->s_addr;
+ return (a6);
+}
+#endif /* defined(INET) && defined(INET6) */
+
#ifdef INET
/*
* Internal: Map an interface index to primary IPv4 address.
@@ -518,6 +539,55 @@ process_cmd(char *cmd, int s, int s6, FILE *fp __unused)
printf("-1\n");
break;
+ /*
+ * Exercise the non-IETF-ratified extension to RFC 3493 to
+ * join IPv4 groups as IPv4 mapped addresses on IPv6 sockets.
+ * Only ASM joins are supported; further use of the full-state API
+ * may result in undefined behaviour.
+ */
+ case 'u':
+ case 'v':
+ toptname = "";
+ sscanf(line, "%s %s", str1, str2);
+ ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
+ if (ifindex == 0) {
+ printf("-1\n");
+ break;
+ }
+ af = su.sa.sa_family;
+#if defined(INET) && defined(INET6)
+ if (af == AF_INET) {
+ struct in_addr gaddr;
+
+ gaddr = su.sin.sin_addr;
+ __in6_v4_to_v4mapped(&su.sin6.sin6_addr, &gaddr);
+ mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
+ mr.mr6.ipv6mr_interface = ifindex;
+ optname = (*cmd == 'u') ?
+ IPV6_JOIN_GROUP :
+ IPV6_LEAVE_GROUP;
+ toptname = (*cmd == 'u') ?
+ "IPV6_JOIN_GROUP" :
+ "IPV6_LEAVE_GROUP";
+ level = IPPROTO_IPV6;
+ optval = (void *)&mr.mr6;
+ optlen = sizeof(mr.mr6);
+ if (s6 < 0) {
+ warnc(EPROTONOSUPPORT, "setsockopt %s",
+ toptname);
+ } else if (setsockopt(s6, level, optname, optval,
+ optlen) == 0) {
+ printf("ok\n");
+ break;
+ } else {
+ warn("setsockopt %s", toptname);
+ }
+ }
+#endif /* defined(INET) && defined(INET6) */
+ /* FALLTHROUGH */
+ printf("-1\n");
+ break;
+
/*
* Set the socket to include or exclude filter mode, and
* add some sources to the filterlist, using the full-state API.
@@ -833,6 +903,8 @@ usage(void)
printf("j mcast-addr ifname [src-addr] - join IP multicast group\n");
printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n");
+ printf("u mcast-addr ifname - join IPv4-mapped group on IPv6 socket\n");
+ printf("v mcast-addr ifname - leave IPv4-mapped group on IPv6 socket\n");
printf(
"i mcast-addr ifname n - set n include mode src filter\n");
printf(