On Mon, Aug 03, 2026 at 10:00:18AM -0400, Mark Johnston wrote:
> On Mon, Aug 03, 2026 at 11:28:38AM +0000, Bruce M Simpson wrote:
> > The branch main has been updated by bms:
> > 
> > URL: 
> > https://cgit.FreeBSD.org/src/commit/?id=f9cdaeeeb9ad45a5e8c0cd32ff9443d245b64185
> > 
> > commit f9cdaeeeb9ad45a5e8c0cd32ff9443d245b64185
> > Author:     Bruce M Simpson <[email protected]>
> > AuthorDate: 2026-08-01 10:14:57 +0000
> > Commit:     Bruce M Simpson <[email protected]>
> > CommitDate: 2026-08-03 11:27:45 +0000
> > 
> >     netinet6: Pass IPv4-mapped ASM multicast joins/leaves to netinet.
> >     
> >     Add support for allowing IPv4 multicast groups to be joined on IPv6 
> > sockets,
> >     as a number of applications began to rely on this over the years, 
> > despite it
> >     only ever having been a convenience which appeared in Solaris & Linux 
> > over
> >     the course of the 00s decade. It is limited to any-source joins (ASM).
> >     
> >     To avoid further quibbling over the meaning of the term "undocumented" 
> > as it
> >     applies to this change, I have chosen to use the wording 
> > "non-IETF-ratified
> >     extension" in comments, with reference to the updated ip6(4) man page.
> >     
> >     PR:             https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=193246
> > ---
> >  sys/netinet/in_mcast.c   |   6 +--
> >  sys/netinet/in_var.h     |   5 +++
> >  sys/netinet6/in6_mcast.c | 112 
> > ++++++++++++++++++++++++++++++++++++++++++++++-
> >  3 files changed, 118 insertions(+), 5 deletions(-)
> > 
> > diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c
> > index 934cc71e745b..ad908b72c340 100644
> > --- a/sys/netinet/in_mcast.c
> > +++ b/sys/netinet/in_mcast.c
> > @@ -158,8 +158,6 @@ static void inm_release(struct in_multi *);
> >  static struct ip_moptions *
> >             inp_findmoptions(struct inpcb *);
> >  static int inp_get_source_filters(struct inpcb *, struct sockopt *);
> > -static int inp_join_group(struct inpcb *, struct sockopt *);
> > -static int inp_leave_group(struct inpcb *, struct sockopt *);
> >  static int inp_block_unblock_source(struct inpcb *, struct sockopt *);
> >  static int inp_set_multicast_if(struct inpcb *, struct sockopt *);
> >  static int inp_set_source_filters(struct inpcb *, struct sockopt *);
> > @@ -1884,7 +1882,7 @@ const struct in_addr *ina, const u_int index)
> >  /*
> >   * Join an IPv4 multicast group, possibly with a source.
> >   */
> > -static int
> > +int
> >  inp_join_group(struct inpcb *inp, struct sockopt *sopt)
> >  {
> >     struct group_source_req          gsr;
> > @@ -2208,7 +2206,7 @@ out_inp_unlocked:
> >  /*
> >   * Leave an IPv4 multicast group on an inpcb, possibly with a source.
> >   */
> > -static int
> > +int
> >  inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
> >  {
> >     struct epoch_tracker             et;
> > diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
> > index 99a628477155..e9db1efd9fe9 100644
> > --- a/sys/netinet/in_var.h
> > +++ b/sys/netinet/in_var.h
> > @@ -459,6 +459,11 @@ void   in_ifattach(void *, struct ifnet *);
> >  void       in_detachhead(struct rib_head *rh);
> >  #endif
> >  
> > +struct sockopt;
> > +
> > +int        inp_join_group(struct inpcb *, struct sockopt *);
> > +int        inp_leave_group(struct inpcb *, struct sockopt *);
> > +
> >  #endif /* _KERNEL */
> >  
> >  /* INET6 stuff */
> > diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c
> > index fad47cb0e69b..a204ad88a9a5 100644
> > --- a/sys/netinet6/in6_mcast.c
> > +++ b/sys/netinet6/in6_mcast.c
> > @@ -1,7 +1,7 @@
> >  /*-
> >   * SPDX-License-Identifier: BSD-3-Clause
> >   *
> > - * Copyright (c) 2009 Bruce Simpson.
> > + * Copyright (c) 2009-2026 Bruce Simpson.
> >   * All rights reserved.
> >   *
> >   * Redistribution and use in source and binary forms, with or without
> > @@ -34,6 +34,7 @@
> >   * Normative references: RFC 2292, RFC 3492, RFC 3542, RFC 3678, RFC 3810.
> >   */
> >  
> > +#include "opt_inet.h"
> >  #include "opt_inet6.h"
> >  
> >  #include <sys/param.h>
> > @@ -162,6 +163,9 @@ static struct ifnet *
> >  static int in6p_block_unblock_source(struct inpcb *, struct sockopt *);
> >  static int in6p_set_multicast_if(struct inpcb *, struct sockopt *);
> >  static int in6p_set_source_filters(struct inpcb *, struct sockopt *);
> > +#ifdef INET
> > +static int in6_v6_mreq_to_v4(struct ipv6_mreq *, struct ip_mreq *);
> > +#endif
> >  static int sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS);
> >  
> >  SYSCTL_DECL(_net_inet6_ip6);       /* XXX Not in any common header. */
> > @@ -1881,6 +1885,54 @@ in6p_lookup_mcast_ifp(const struct inpcb *inp, const 
> > struct sockaddr_in6 *gsin6)
> >     return (nh ? nh->nh_ifp : NULL);
> >  }
> >  
> > +#ifdef INET
> > +/*
> > + * Perform sockopt mreq argument conversion for IPv4-mapped groups.
> > + *
> > + * This function is required to support an extension to the behaviour
> > + * in RFC 3493 Sec 3.7, which was never formally proposed by any
> > + * contemporary IPv6 normative reference, but which is now required
> > + * by much application software using IPv6 sockets as a convenience.
> > + * Refer to manual page ip6(4) for further information.
> > + *
> > + * FUTURE: Use IPv4 source-address selection.
> > + */
> > +static int
> > +in6_v6_mreq_to_v4(struct ipv6_mreq *mreq, struct ip_mreq *mreq_v4)
> > +{
> > +   int                      error;
> > +   struct epoch_tracker     et;
> > +   struct ifnet            *ifp;
> > +   struct in_ifaddr        *ia;
> > +
> > +   NET_EPOCH_ENTER(et);
> > +
> > +   ifp = ifnet_byindex(mreq->ipv6mr_interface);
> > +   if (ifp == NULL) {
> > +           error = EADDRNOTAVAIL;
> > +           goto out;
> > +   }
> > +
> > +   /*
> > +    * Here, we do not compare the ifnet's primary IPv4 address with
> > +    * INADDR_ANY, to permit its use during system initialization.
> > +    * If this is not required, an appropriate check to screen it out
> > +    * should be added, e.g. in_nullhost(ia->ia_addr.sin_addr.s_addr).
> > +    */
> > +   ia = in_ifprimaryaddr(ifp);
> > +   if (ia == NULL) {
> > +           error = EADDRNOTAVAIL;
> > +           goto out;
> > +   }
> > +   mreq_v4->imr_interface.s_addr = IA_SIN(ia)->sin_addr.s_addr;
> > +   error = 0;
> > +
> > +out:
> > +   NET_EPOCH_EXIT(et);
> > +   return (error);
> > +}
> > +#endif /* INET */
> > +
> >  /*
> >   * Join an IPv6 multicast group, possibly with a source.
> >   *
> > @@ -1929,6 +1981,35 @@ in6p_join_group(struct inpcb *inp, struct sockopt 
> > *sopt)
> >                 sizeof(struct ipv6_mreq));
> >             if (error)
> >                     return (error);
> > +#ifdef INET
> > +           /*
> > +            * Support for the non-IETF-ratified extension to RFC 3493 to
> > +            * join IPv4 groups as IPv4 mapped addresses on IPv6 sockets.
> > +            */
> > +           if (IN6_IS_ADDR_V4MAPPED(&mreq.ipv6mr_multiaddr)) {
> > +                   struct ip_mreq mreq_v4;
> > +                   struct sockopt sopt_v4 = {
> > +                           .sopt_dir     = SOPT_SET,
> > +                           .sopt_level   = sopt->sopt_level,
> > +                           .sopt_name    = IP_ADD_MEMBERSHIP,
> > +                           .sopt_val     = &mreq_v4,
> > +                           .sopt_valsize = sizeof(mreq_v4),
> > +                           .sopt_rights  = sopt->sopt_rights,
> > +                           .sopt_td      = sopt->sopt_td
> > +                   };
> > +
> > +                   mreq_v4.imr_multiaddr.s_addr =
> > +                       mreq.ipv6mr_multiaddr.s6_addr32[3];
> > +                   if (mreq.ipv6mr_interface == 0)
> > +                           mreq_v4.imr_interface.s_addr = INADDR_ANY;
> > +                   else
> > +                           error = in6_v6_mreq_to_v4(&mreq, &mreq_v4);
> > +                   if (error)
> > +                           return error;
> > +
> > +                   return (inp_join_group(inp, &sopt_v4));
> 
> I'm not sure how this can work: inp_join_group() will invoke
> sooptcopyin() with sopt_val pointing to kernel memory and sopt_td
> non-NULL, so sooptcopyin() will try to copy in from a kernel address and
> inevitably return EFAULT.

I missed your follow up message, sorry for the noise.

Reply via email to