git: c8d4287e30d6 - main - mtest: Add support for exercising IPv4-mapped groups on IPv6 sockets.
Bruce M Simpson <[email protected]> Mon, 03 Aug 2026 15:47:40 +0000
| Newsgroups | gmane.os.freebsd.devel.cvs.src |
|---|---|
| Message-ID | <6a70b81c.3e2d0.a134af1__28045.3677218006$1785772193$gmane$org@gitrepo.freebsd.org> |
The branch main has been updated by bms: URL: https://cgit.FreeBSD.org/src/commit/?id=c8d4287e30d6dc2ed3d4378133758adabc0030a3 commit c8d4287e30d6dc2ed3d4378133758adabc0030a3 Author: Bruce M Simpson <[email protected]> AuthorDate: 2026-08-02 08:00:19 +0000 Commit: Bruce M Simpson <[email protected]> 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(