Author: glebius
Date: Sat Feb 23 06:03:18 2019
New Revision: 344481
URL: https://svnweb.freebsd.org/changeset/base/344481

Log:
  Support struct ip_mreqn as argument for IP_ADD_MEMBERSHIP. Legacy support
  for struct ip_mreq remains in place.
  
  The struct ip_mreqn is Linux extension to classic BSD multicast API. It
  has extra field allowing to specify the interface index explicitly. In
  Linux it used as argument for IP_MULTICAST_IF and IP_ADD_MEMBERSHIP.
  FreeBSD kernel also declares this structure and supports it as argument
  to IP_MULTICAST_IF since r170613. So, we have structure declared but
  not fully supported, this confused third party application configure
  scripts.
  
  Code handling IP_ADD_MEMBERSHIP was mixed together with code for
  IP_ADD_SOURCE_MEMBERSHIP.  Bringing legacy and new structure support
  into the mess would made the "argument switcharoo" intolerable, so
  code was separated into its own switch case clause.
  
  MFC after:    3 months
  Differential Revision:        https://reviews.freebsd.org/D19276

Modified:
  head/share/man/man4/ip.4
  head/sys/netinet/in_mcast.c

Modified: head/share/man/man4/ip.4
==============================================================================
--- head/share/man/man4/ip.4    Sat Feb 23 04:24:44 2019        (r344480)
+++ head/share/man/man4/ip.4    Sat Feb 23 06:03:18 2019        (r344481)
@@ -28,7 +28,7 @@
 .\"     @(#)ip.4       8.2 (Berkeley) 11/30/93
 .\" $FreeBSD$
 .\"
-.Dd August 19, 2018
+.Dd February 22, 2019
 .Dt IP 4
 .Os
 .Sh NAME
@@ -571,32 +571,55 @@ To join a multicast group, use the
 .Dv IP_ADD_MEMBERSHIP
 option:
 .Bd -literal
-struct ip_mreq mreq;
-setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+struct ip_mreqn mreqn;
+setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn));
 .Ed
 .Pp
 where
-.Fa mreq
+.Fa mreqn
 is the following structure:
 .Bd -literal
-struct ip_mreq {
+struct ip_mreqn {
     struct in_addr imr_multiaddr; /* IP multicast address of group */
     struct in_addr imr_interface; /* local IP address of interface */
+    int            imr_ifindex;   /* interface index */
 }
 .Ed
 .Pp
-.Va imr_interface
-should be set to the
-.Tn IP
-address of a particular multicast-capable interface if
+.Va imr_ifindex
+should be set to the index of a particular multicast-capable interface if
 the host is multihomed.
-It may be set to
-.Dv INADDR_ANY
-to choose the default interface, although this is not recommended;
-this is considered to be the first interface corresponding
-to the default route.
-Otherwise, the first multicast-capable interface
-configured in the system will be used.
+If
+.Va imr_ifindex
+is non-zero, value of
+.Va imr_interface
+is ignored.
+Otherwise, if
+.Va imr_ifindex
+is 0, kernel will use IP address from
+.Va imr_interface
+to lookup the interface.
+Value of
+.Va imr_interface
+may be set to
+.Va INADDR_ANY
+to choose the default interface, although this is not recommended; this is
+considered to be the first interface corresponding to the default route.
+Otherwise, the first multicast-capable interface configured in the system
+will be used.
+.Pp
+Legacy
+.Vt "struct ip_mreq" ,
+that lacks
+.Va imr_ifindex
+field is also supported by
+.Dv IP_ADD_MEMBERSHIP
+setsockopt.
+In this case kernel would behave as if
+.Va imr_ifindex
+was set to zero:
+.Va imr_interface
+will be used to lookup interface.
 .Pp
 Prior to
 .Fx 7.0 ,

Modified: head/sys/netinet/in_mcast.c
==============================================================================
--- head/sys/netinet/in_mcast.c Sat Feb 23 04:24:44 2019        (r344480)
+++ head/sys/netinet/in_mcast.c Sat Feb 23 06:03:18 2019        (r344481)
@@ -2049,40 +2049,49 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt
        ssa->ss.ss_family = AF_UNSPEC;
 
        switch (sopt->sopt_name) {
-       case IP_ADD_MEMBERSHIP:
-       case IP_ADD_SOURCE_MEMBERSHIP: {
-               struct ip_mreq_source    mreqs;
+       case IP_ADD_MEMBERSHIP: {
+               struct ip_mreqn mreqn;
 
-               if (sopt->sopt_name == IP_ADD_MEMBERSHIP) {
-                       error = sooptcopyin(sopt, &mreqs,
-                           sizeof(struct ip_mreq),
-                           sizeof(struct ip_mreq));
-                       /*
-                        * Do argument switcharoo from ip_mreq into
-                        * ip_mreq_source to avoid using two instances.
-                        */
-                       mreqs.imr_interface = mreqs.imr_sourceaddr;
-                       mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
-               } else if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
-                       error = sooptcopyin(sopt, &mreqs,
-                           sizeof(struct ip_mreq_source),
-                           sizeof(struct ip_mreq_source));
-               }
+               if (sopt->sopt_valsize == sizeof(struct ip_mreqn))
+                       error = sooptcopyin(sopt, &mreqn,
+                           sizeof(struct ip_mreqn), sizeof(struct ip_mreqn));
+               else
+                       error = sooptcopyin(sopt, &mreqn,
+                           sizeof(struct ip_mreq), sizeof(struct ip_mreq));
                if (error)
                        return (error);
 
                gsa->sin.sin_family = AF_INET;
                gsa->sin.sin_len = sizeof(struct sockaddr_in);
-               gsa->sin.sin_addr = mreqs.imr_multiaddr;
+               gsa->sin.sin_addr = mreqn.imr_multiaddr;
+               if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
+                       return (EINVAL);
 
-               if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
-                       ssa->sin.sin_family = AF_INET;
-                       ssa->sin.sin_len = sizeof(struct sockaddr_in);
-                       ssa->sin.sin_addr = mreqs.imr_sourceaddr;
-               }
+               if (sopt->sopt_valsize == sizeof(struct ip_mreqn) &&
+                   mreqn.imr_ifindex != 0)
+                       ifp = ifnet_byindex(mreqn.imr_ifindex);
+               else
+                       ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
+                           mreqn.imr_address);
+               break;
+       }
+       case IP_ADD_SOURCE_MEMBERSHIP: {
+               struct ip_mreq_source    mreqs;
 
+               error = sooptcopyin(sopt, &mreqs, sizeof(struct ip_mreq_source),
+                           sizeof(struct ip_mreq_source));
+               if (error)
+                       return (error);
+
+               gsa->sin.sin_family = ssa->sin.sin_family = AF_INET;
+               gsa->sin.sin_len = ssa->sin.sin_len =
+                   sizeof(struct sockaddr_in);
+
+               gsa->sin.sin_addr = mreqs.imr_multiaddr;
                if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
                        return (EINVAL);
+
+               ssa->sin.sin_addr = mreqs.imr_sourceaddr;
 
                ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
                    mreqs.imr_interface);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to