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(

Reply via email to