The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/in_mcast.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2007 Bruce M. Simpson.
    3  * Copyright (c) 2005 Robert N. M. Watson.
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. The name of the author may not be used to endorse or promote
   15  *    products derived from this software without specific prior written
   16  *    permission.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 /*
   32  * IPv4 multicast socket, group, and socket option processing module.
   33  * Until further notice, this file requires INET to compile.
   34  * TODO: Make this infrastructure independent of address family.
   35  * TODO: Teach netinet6 to use this code.
   36  * TODO: Hook up SSM logic to IGMPv3/MLDv2.
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __FBSDID("$FreeBSD: releng/7.4/sys/netinet/in_mcast.c 181822 2008-08-18 08:11:48Z rwatson $");
   41 
   42 #include <sys/param.h>
   43 #include <sys/systm.h>
   44 #include <sys/kernel.h>
   45 #include <sys/malloc.h>
   46 #include <sys/mbuf.h>
   47 #include <sys/protosw.h>
   48 #include <sys/socket.h>
   49 #include <sys/socketvar.h>
   50 #include <sys/sysctl.h>
   51 
   52 #include <net/if.h>
   53 #include <net/if_dl.h>
   54 #include <net/route.h>
   55 
   56 #include <netinet/in.h>
   57 #include <netinet/in_systm.h>
   58 #include <netinet/in_pcb.h>
   59 #include <netinet/in_var.h>
   60 #include <netinet/ip_var.h>
   61 #include <netinet/igmp_var.h>
   62 
   63 #ifndef __SOCKUNION_DECLARED
   64 union sockunion {
   65         struct sockaddr_storage ss;
   66         struct sockaddr         sa;
   67         struct sockaddr_dl      sdl;
   68         struct sockaddr_in      sin;
   69 #ifdef INET6
   70         struct sockaddr_in6     sin6;
   71 #endif
   72 };
   73 typedef union sockunion sockunion_t;
   74 #define __SOCKUNION_DECLARED
   75 #endif /* __SOCKUNION_DECLARED */
   76 
   77 static MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group");
   78 static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options");
   79 static MALLOC_DEFINE(M_IPMSOURCE, "in_msource", "IPv4 multicast source filter");
   80 
   81 /*
   82  * The IPv4 multicast list (in_multihead and associated structures) are
   83  * protected by the global in_multi_mtx.  See in_var.h for more details.  For
   84  * now, in_multi_mtx is marked as recursible due to IGMP's calling back into
   85  * ip_output() to send IGMP packets while holding the lock; this probably is
   86  * not quite desirable.
   87  */
   88 struct in_multihead in_multihead;       /* XXX BSS initialization */
   89 struct mtx in_multi_mtx;
   90 MTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF | MTX_RECURSE);
   91 
   92 /*
   93  * Functions with non-static linkage defined in this file should be
   94  * declared in in_var.h:
   95  *  imo_match_group()
   96  *  imo_match_source()
   97  *  in_addmulti()
   98  *  in_delmulti()
   99  *  in_delmulti_locked()
  100  * and ip_var.h:
  101  *  inp_freemoptions()
  102  *  inp_getmoptions()
  103  *  inp_setmoptions()
  104  */
  105 static int      imo_grow(struct ip_moptions *);
  106 static int      imo_join_source(struct ip_moptions *, size_t, sockunion_t *);
  107 static int      imo_leave_source(struct ip_moptions *, size_t, sockunion_t *);
  108 static int      inp_change_source_filter(struct inpcb *, struct sockopt *);
  109 static struct ip_moptions *
  110                 inp_findmoptions(struct inpcb *);
  111 static int      inp_get_source_filters(struct inpcb *, struct sockopt *);
  112 static int      inp_join_group(struct inpcb *, struct sockopt *);
  113 static int      inp_leave_group(struct inpcb *, struct sockopt *);
  114 static int      inp_set_multicast_if(struct inpcb *, struct sockopt *);
  115 static int      inp_set_source_filters(struct inpcb *, struct sockopt *);
  116 static struct ifnet *
  117                 ip_multicast_if(struct in_addr *a);
  118 
  119 /*
  120  * Resize the ip_moptions vector to the next power-of-two minus 1.
  121  * May be called with locks held; do not sleep.
  122  */
  123 static int
  124 imo_grow(struct ip_moptions *imo)
  125 {
  126         struct in_multi         **nmships;
  127         struct in_multi         **omships;
  128         struct in_mfilter        *nmfilters;
  129         struct in_mfilter        *omfilters;
  130         size_t                    idx;
  131         size_t                    newmax;
  132         size_t                    oldmax;
  133 
  134         nmships = NULL;
  135         nmfilters = NULL;
  136         omships = imo->imo_membership;
  137         omfilters = imo->imo_mfilters;
  138         oldmax = imo->imo_max_memberships;
  139         newmax = ((oldmax + 1) * 2) - 1;
  140 
  141         if (newmax <= IP_MAX_MEMBERSHIPS) {
  142                 nmships = (struct in_multi **)realloc(omships,
  143                     sizeof(struct in_multi *) * newmax, M_IPMOPTS, M_NOWAIT);
  144                 nmfilters = (struct in_mfilter *)realloc(omfilters,
  145                     sizeof(struct in_mfilter) * newmax, M_IPMSOURCE, M_NOWAIT);
  146                 if (nmships != NULL && nmfilters != NULL) {
  147                         /* Initialize newly allocated source filter heads. */
  148                         for (idx = oldmax; idx < newmax; idx++) {
  149                                 nmfilters[idx].imf_fmode = MCAST_EXCLUDE;
  150                                 nmfilters[idx].imf_nsources = 0;
  151                                 TAILQ_INIT(&nmfilters[idx].imf_sources);
  152                         }
  153                         imo->imo_max_memberships = newmax;
  154                         imo->imo_membership = nmships;
  155                         imo->imo_mfilters = nmfilters;
  156                 }
  157         }
  158 
  159         if (nmships == NULL || nmfilters == NULL) {
  160                 if (nmships != NULL)
  161                         free(nmships, M_IPMOPTS);
  162                 if (nmfilters != NULL)
  163                         free(nmfilters, M_IPMSOURCE);
  164                 return (ETOOMANYREFS);
  165         }
  166 
  167         return (0);
  168 }
  169 
  170 /*
  171  * Add a source to a multicast filter list.
  172  * Assumes the associated inpcb is locked.
  173  */
  174 static int
  175 imo_join_source(struct ip_moptions *imo, size_t gidx, sockunion_t *src)
  176 {
  177         struct in_msource       *ims, *nims;
  178         struct in_mfilter       *imf;
  179 
  180         KASSERT(src->ss.ss_family == AF_INET, ("%s: !AF_INET", __func__));
  181         KASSERT(imo->imo_mfilters != NULL,
  182             ("%s: imo_mfilters vector not allocated", __func__));
  183 
  184         imf = &imo->imo_mfilters[gidx];
  185         if (imf->imf_nsources == IP_MAX_SOURCE_FILTER)
  186                 return (ENOBUFS);
  187 
  188         ims = imo_match_source(imo, gidx, &src->sa);
  189         if (ims != NULL)
  190                 return (EADDRNOTAVAIL);
  191 
  192         /* Do not sleep with inp lock held. */
  193         MALLOC(nims, struct in_msource *, sizeof(struct in_msource),
  194             M_IPMSOURCE, M_NOWAIT | M_ZERO);
  195         if (nims == NULL)
  196                 return (ENOBUFS);
  197 
  198         nims->ims_addr = src->ss;
  199         TAILQ_INSERT_TAIL(&imf->imf_sources, nims, ims_next);
  200         imf->imf_nsources++;
  201 
  202         return (0);
  203 }
  204 
  205 static int
  206 imo_leave_source(struct ip_moptions *imo, size_t gidx, sockunion_t *src)
  207 {
  208         struct in_msource       *ims;
  209         struct in_mfilter       *imf;
  210 
  211         KASSERT(src->ss.ss_family == AF_INET, ("%s: !AF_INET", __func__));
  212         KASSERT(imo->imo_mfilters != NULL,
  213             ("%s: imo_mfilters vector not allocated", __func__));
  214 
  215         imf = &imo->imo_mfilters[gidx];
  216         if (imf->imf_nsources == IP_MAX_SOURCE_FILTER)
  217                 return (ENOBUFS);
  218 
  219         ims = imo_match_source(imo, gidx, &src->sa);
  220         if (ims == NULL)
  221                 return (EADDRNOTAVAIL);
  222 
  223         TAILQ_REMOVE(&imf->imf_sources, ims, ims_next);
  224         FREE(ims, M_IPMSOURCE);
  225         imf->imf_nsources--;
  226 
  227         return (0);
  228 }
  229 
  230 /*
  231  * Find an IPv4 multicast group entry for this ip_moptions instance
  232  * which matches the specified group, and optionally an interface.
  233  * Return its index into the array, or -1 if not found.
  234  */
  235 size_t
  236 imo_match_group(struct ip_moptions *imo, struct ifnet *ifp,
  237     struct sockaddr *group)
  238 {
  239         sockunion_t      *gsa;
  240         struct in_multi **pinm;
  241         int               idx;
  242         int               nmships;
  243 
  244         gsa = (sockunion_t *)group;
  245 
  246         /* The imo_membership array may be lazy allocated. */
  247         if (imo->imo_membership == NULL || imo->imo_num_memberships == 0)
  248                 return (-1);
  249 
  250         nmships = imo->imo_num_memberships;
  251         pinm = &imo->imo_membership[0];
  252         for (idx = 0; idx < nmships; idx++, pinm++) {
  253                 if (*pinm == NULL)
  254                         continue;
  255 #if 0
  256                 printf("%s: trying ifp = %p, inaddr = %s ", __func__,
  257                     ifp, inet_ntoa(gsa->sin.sin_addr));
  258                 printf("against %p, %s\n",
  259                     (*pinm)->inm_ifp, inet_ntoa((*pinm)->inm_addr));
  260 #endif
  261                 if ((ifp == NULL || ((*pinm)->inm_ifp == ifp)) &&
  262                     (*pinm)->inm_addr.s_addr == gsa->sin.sin_addr.s_addr) {
  263                         break;
  264                 }
  265         }
  266         if (idx >= nmships)
  267                 idx = -1;
  268 
  269         return (idx);
  270 }
  271 
  272 /*
  273  * Find a multicast source entry for this imo which matches
  274  * the given group index for this socket, and source address.
  275  */
  276 struct in_msource *
  277 imo_match_source(struct ip_moptions *imo, size_t gidx, struct sockaddr *src)
  278 {
  279         struct in_mfilter       *imf;
  280         struct in_msource       *ims, *pims;
  281 
  282         KASSERT(src->sa_family == AF_INET, ("%s: !AF_INET", __func__));
  283         KASSERT(gidx != -1 && gidx < imo->imo_num_memberships,
  284             ("%s: invalid index %d\n", __func__, (int)gidx));
  285 
  286         /* The imo_mfilters array may be lazy allocated. */
  287         if (imo->imo_mfilters == NULL)
  288                 return (NULL);
  289 
  290         pims = NULL;
  291         imf = &imo->imo_mfilters[gidx];
  292         TAILQ_FOREACH(ims, &imf->imf_sources, ims_next) {
  293                 /*
  294                  * Perform bitwise comparison of two IPv4 addresses.
  295                  * TODO: Do the same for IPv6.
  296                  * Do not use sa_equal() for this as it is not aware of
  297                  * deeper structure in sockaddr_in or sockaddr_in6.
  298                  */
  299                 if (((struct sockaddr_in *)&ims->ims_addr)->sin_addr.s_addr ==
  300                     ((struct sockaddr_in *)src)->sin_addr.s_addr) {
  301                         pims = ims;
  302                         break;
  303                 }
  304         }
  305 
  306         return (pims);
  307 }
  308 
  309 /*
  310  * Join an IPv4 multicast group.
  311  */
  312 struct in_multi *
  313 in_addmulti(struct in_addr *ap, struct ifnet *ifp)
  314 {
  315         struct in_multi *inm;
  316 
  317         inm = NULL;
  318 
  319         IFF_LOCKGIANT(ifp);
  320         IN_MULTI_LOCK();
  321 
  322         IN_LOOKUP_MULTI(*ap, ifp, inm);
  323         if (inm != NULL) {
  324                 /*
  325                  * If we already joined this group, just bump the
  326                  * refcount and return it.
  327                  */
  328                 KASSERT(inm->inm_refcount >= 1,
  329                     ("%s: bad refcount %d", __func__, inm->inm_refcount));
  330                 ++inm->inm_refcount;
  331         } else do {
  332                 sockunion_t              gsa;
  333                 struct ifmultiaddr      *ifma;
  334                 struct in_multi         *ninm;
  335                 int                      error;
  336 
  337                 memset(&gsa, 0, sizeof(gsa));
  338                 gsa.sin.sin_family = AF_INET;
  339                 gsa.sin.sin_len = sizeof(struct sockaddr_in);
  340                 gsa.sin.sin_addr = *ap;
  341 
  342                 /*
  343                  * Check if a link-layer group is already associated
  344                  * with this network-layer group on the given ifnet.
  345                  * If so, bump the refcount on the existing network-layer
  346                  * group association and return it.
  347                  */
  348                 error = if_addmulti(ifp, &gsa.sa, &ifma);
  349                 if (error)
  350                         break;
  351                 if (ifma->ifma_protospec != NULL) {
  352                         inm = (struct in_multi *)ifma->ifma_protospec;
  353 #ifdef INVARIANTS
  354                         if (inm->inm_ifma != ifma || inm->inm_ifp != ifp ||
  355                             inm->inm_addr.s_addr != ap->s_addr)
  356                                 panic("%s: ifma is inconsistent", __func__);
  357 #endif
  358                         ++inm->inm_refcount;
  359                         break;
  360                 }
  361 
  362                 /*
  363                  * A new membership is needed; construct it and
  364                  * perform the IGMP join.
  365                  */
  366                 ninm = malloc(sizeof(*ninm), M_IPMADDR, M_NOWAIT | M_ZERO);
  367                 if (ninm == NULL) {
  368                         if_delmulti_ifma(ifma);
  369                         break;
  370                 }
  371                 ninm->inm_addr = *ap;
  372                 ninm->inm_ifp = ifp;
  373                 ninm->inm_ifma = ifma;
  374                 ninm->inm_refcount = 1;
  375                 ifma->ifma_protospec = ninm;
  376                 LIST_INSERT_HEAD(&in_multihead, ninm, inm_link);
  377 
  378                 igmp_joingroup(ninm);
  379 
  380                 inm = ninm;
  381         } while (0);
  382 
  383         IN_MULTI_UNLOCK();
  384         IFF_UNLOCKGIANT(ifp);
  385 
  386         return (inm);
  387 }
  388 
  389 /*
  390  * Leave an IPv4 multicast group.
  391  * It is OK to call this routine if the underlying ifnet went away.
  392  *
  393  * XXX: To deal with the ifp going away, we cheat; the link-layer code in net
  394  * will set ifma_ifp to NULL when the associated ifnet instance is detached
  395  * from the system.
  396  *
  397  * The only reason we need to violate layers and check ifma_ifp here at all
  398  * is because certain hardware drivers still require Giant to be held,
  399  * and it must always be taken before other locks.
  400  */
  401 void
  402 in_delmulti(struct in_multi *inm)
  403 {
  404         struct ifnet *ifp;
  405 
  406         KASSERT(inm != NULL, ("%s: inm is NULL", __func__));
  407         KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__));
  408         ifp = inm->inm_ifma->ifma_ifp;
  409 
  410         if (ifp != NULL) {
  411                 /*
  412                  * Sanity check that netinet's notion of ifp is the
  413                  * same as net's.
  414                  */
  415                 KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__));
  416                 IFF_LOCKGIANT(ifp);
  417         }
  418 
  419         IN_MULTI_LOCK();
  420         in_delmulti_locked(inm);
  421         IN_MULTI_UNLOCK();
  422 
  423         if (ifp != NULL)
  424                 IFF_UNLOCKGIANT(ifp);
  425 }
  426 
  427 /*
  428  * Delete a multicast address record, with locks held.
  429  *
  430  * It is OK to call this routine if the ifp went away.
  431  * Assumes that caller holds the IN_MULTI lock, and that
  432  * Giant was taken before other locks if required by the hardware.
  433  */
  434 void
  435 in_delmulti_locked(struct in_multi *inm)
  436 {
  437         struct ifmultiaddr *ifma;
  438 
  439         IN_MULTI_LOCK_ASSERT();
  440         KASSERT(inm->inm_refcount >= 1, ("%s: freeing freed inm", __func__));
  441 
  442         if (--inm->inm_refcount == 0) {
  443                 igmp_leavegroup(inm);
  444 
  445                 ifma = inm->inm_ifma;
  446 #ifdef DIAGNOSTIC
  447                 if (bootverbose)
  448                         printf("%s: purging ifma %p\n", __func__, ifma);
  449 #endif
  450                 KASSERT(ifma->ifma_protospec == inm,
  451                     ("%s: ifma_protospec != inm", __func__));
  452                 ifma->ifma_protospec = NULL;
  453 
  454                 LIST_REMOVE(inm, inm_link);
  455                 free(inm, M_IPMADDR);
  456 
  457                 if_delmulti_ifma(ifma);
  458         }
  459 }
  460 
  461 /*
  462  * Block or unblock an ASM/SSM multicast source on an inpcb.
  463  */
  464 static int
  465 inp_change_source_filter(struct inpcb *inp, struct sockopt *sopt)
  466 {
  467         struct group_source_req          gsr;
  468         sockunion_t                     *gsa, *ssa;
  469         struct ifnet                    *ifp;
  470         struct in_mfilter               *imf;
  471         struct ip_moptions              *imo;
  472         struct in_msource               *ims;
  473         size_t                           idx;
  474         int                              error;
  475         int                              block;
  476 
  477         ifp = NULL;
  478         error = 0;
  479         block = 0;
  480 
  481         memset(&gsr, 0, sizeof(struct group_source_req));
  482         gsa = (sockunion_t *)&gsr.gsr_group;
  483         ssa = (sockunion_t *)&gsr.gsr_source;
  484 
  485         switch (sopt->sopt_name) {
  486         case IP_BLOCK_SOURCE:
  487         case IP_UNBLOCK_SOURCE: {
  488                 struct ip_mreq_source    mreqs;
  489 
  490                 error = sooptcopyin(sopt, &mreqs,
  491                     sizeof(struct ip_mreq_source),
  492                     sizeof(struct ip_mreq_source));
  493                 if (error)
  494                         return (error);
  495 
  496                 gsa->sin.sin_family = AF_INET;
  497                 gsa->sin.sin_len = sizeof(struct sockaddr_in);
  498                 gsa->sin.sin_addr = mreqs.imr_multiaddr;
  499 
  500                 ssa->sin.sin_family = AF_INET;
  501                 ssa->sin.sin_len = sizeof(struct sockaddr_in);
  502                 ssa->sin.sin_addr = mreqs.imr_sourceaddr;
  503 
  504                 if (mreqs.imr_interface.s_addr != INADDR_ANY)
  505                         INADDR_TO_IFP(mreqs.imr_interface, ifp);
  506 
  507                 if (sopt->sopt_name == IP_BLOCK_SOURCE)
  508                         block = 1;
  509 
  510 #ifdef DIAGNOSTIC
  511                 if (bootverbose) {
  512                         printf("%s: imr_interface = %s, ifp = %p\n",
  513                             __func__, inet_ntoa(mreqs.imr_interface), ifp);
  514                 }
  515 #endif
  516                 break;
  517             }
  518 
  519         case MCAST_BLOCK_SOURCE:
  520         case MCAST_UNBLOCK_SOURCE:
  521                 error = sooptcopyin(sopt, &gsr,
  522                     sizeof(struct group_source_req),
  523                     sizeof(struct group_source_req));
  524                 if (error)
  525                         return (error);
  526 
  527                 if (gsa->sin.sin_family != AF_INET ||
  528                     gsa->sin.sin_len != sizeof(struct sockaddr_in))
  529                         return (EINVAL);
  530 
  531                 if (ssa->sin.sin_family != AF_INET ||
  532                     ssa->sin.sin_len != sizeof(struct sockaddr_in))
  533                         return (EINVAL);
  534 
  535                 if (gsr.gsr_interface == 0 || if_index < gsr.gsr_interface)
  536                         return (EADDRNOTAVAIL);
  537 
  538                 ifp = ifnet_byindex(gsr.gsr_interface);
  539 
  540                 if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
  541                         block = 1;
  542                 break;
  543 
  544         default:
  545 #ifdef DIAGNOSTIC
  546                 if (bootverbose) {
  547                         printf("%s: unknown sopt_name %d\n", __func__,
  548                             sopt->sopt_name);
  549                 }
  550 #endif
  551                 return (EOPNOTSUPP);
  552                 break;
  553         }
  554 
  555         /* XXX INET6 */
  556         if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
  557                 return (EINVAL);
  558 
  559         /*
  560          * Check if we are actually a member of this group.
  561          */
  562         imo = inp_findmoptions(inp);
  563         idx = imo_match_group(imo, ifp, &gsa->sa);
  564         if (idx == -1 || imo->imo_mfilters == NULL) {
  565                 error = EADDRNOTAVAIL;
  566                 goto out_locked;
  567         }
  568 
  569         KASSERT(imo->imo_mfilters != NULL,
  570             ("%s: imo_mfilters not allocated", __func__));
  571         imf = &imo->imo_mfilters[idx];
  572 
  573         /*
  574          * SSM multicast truth table for block/unblock operations.
  575          *
  576          * Operation   Filter Mode  Entry exists?   Action
  577          *
  578          * block       exclude      no              add source to filter
  579          * unblock     include      no              add source to filter
  580          * block       include      no              EINVAL
  581          * unblock     exclude      no              EINVAL
  582          * block       exclude      yes             EADDRNOTAVAIL
  583          * unblock     include      yes             EADDRNOTAVAIL
  584          * block       include      yes             remove source from filter
  585          * unblock     exclude      yes             remove source from filter
  586          *
  587          * FreeBSD does not explicitly distinguish between ASM and SSM
  588          * mode sockets; all sockets are assumed to have a filter list.
  589          */
  590 #ifdef DIAGNOSTIC
  591         if (bootverbose) {
  592                 printf("%s: imf_fmode is %s\n", __func__,
  593                     imf->imf_fmode == MCAST_INCLUDE ? "include" : "exclude");
  594         }
  595 #endif
  596         ims = imo_match_source(imo, idx, &ssa->sa);
  597         if (ims == NULL) {
  598                 if ((block == 1 && imf->imf_fmode == MCAST_EXCLUDE) ||
  599                     (block == 0 && imf->imf_fmode == MCAST_INCLUDE)) {
  600 #ifdef DIAGNOSTIC
  601                         if (bootverbose) {
  602                                 printf("%s: adding %s to filter list\n",
  603                                     __func__, inet_ntoa(ssa->sin.sin_addr));
  604                         }
  605 #endif
  606                         error = imo_join_source(imo, idx, ssa);
  607                 }
  608                 if ((block == 1 && imf->imf_fmode == MCAST_INCLUDE) ||
  609                     (block == 0 && imf->imf_fmode == MCAST_EXCLUDE)) {
  610                         /*
  611                          * If the socket is in inclusive mode:
  612                          *  the source is already blocked as it has no entry.
  613                          * If the socket is in exclusive mode:
  614                          *  the source is already unblocked as it has no entry.
  615                          */
  616 #ifdef DIAGNOSTIC
  617                         if (bootverbose) {
  618                                 printf("%s: ims %p; %s already [un]blocked\n",
  619                                     __func__, ims,
  620                                     inet_ntoa(ssa->sin.sin_addr));
  621                         }
  622 #endif
  623                         error = EINVAL;
  624                 }
  625         } else {
  626                 if ((block == 1 && imf->imf_fmode == MCAST_EXCLUDE) ||
  627                     (block == 0 && imf->imf_fmode == MCAST_INCLUDE)) {
  628                         /*
  629                          * If the socket is in exclusive mode:
  630                          *  the source is already blocked as it has an entry.
  631                          * If the socket is in inclusive mode:
  632                          *  the source is already unblocked as it has an entry.
  633                          */
  634 #ifdef DIAGNOSTIC
  635                         if (bootverbose) {
  636                                 printf("%s: ims %p; %s already [un]blocked\n",
  637                                     __func__, ims,
  638                                     inet_ntoa(ssa->sin.sin_addr));
  639                         }
  640 #endif
  641                         error = EADDRNOTAVAIL;
  642                 }
  643                 if ((block == 1 && imf->imf_fmode == MCAST_INCLUDE) ||
  644                     (block == 0 && imf->imf_fmode == MCAST_EXCLUDE)) {
  645 #ifdef DIAGNOSTIC
  646                         if (bootverbose) {
  647                                 printf("%s: removing %s from filter list\n",
  648                                     __func__, inet_ntoa(ssa->sin.sin_addr));
  649                         }
  650 #endif
  651                         error = imo_leave_source(imo, idx, ssa);
  652                 }
  653         }
  654 
  655 out_locked:
  656         INP_WUNLOCK(inp);
  657         return (error);
  658 }
  659 
  660 /*
  661  * Given an inpcb, return its multicast options structure pointer.  Accepts
  662  * an unlocked inpcb pointer, but will return it locked.  May sleep.
  663  */
  664 static struct ip_moptions *
  665 inp_findmoptions(struct inpcb *inp)
  666 {
  667         struct ip_moptions       *imo;
  668         struct in_multi         **immp;
  669         struct in_mfilter        *imfp;
  670         size_t                    idx;
  671 
  672         INP_WLOCK(inp);
  673         if (inp->inp_moptions != NULL)
  674                 return (inp->inp_moptions);
  675 
  676         INP_WUNLOCK(inp);
  677 
  678         imo = (struct ip_moptions *)malloc(sizeof(*imo), M_IPMOPTS,
  679             M_WAITOK);
  680         immp = (struct in_multi **)malloc(sizeof(*immp) * IP_MIN_MEMBERSHIPS,
  681             M_IPMOPTS, M_WAITOK | M_ZERO);
  682         imfp = (struct in_mfilter *)malloc(
  683             sizeof(struct in_mfilter) * IP_MIN_MEMBERSHIPS,
  684             M_IPMSOURCE, M_WAITOK);
  685 
  686         imo->imo_multicast_ifp = NULL;
  687         imo->imo_multicast_addr.s_addr = INADDR_ANY;
  688         imo->imo_multicast_vif = -1;
  689         imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
  690         imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
  691         imo->imo_num_memberships = 0;
  692         imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
  693         imo->imo_membership = immp;
  694 
  695         /* Initialize per-group source filters. */
  696         for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) {
  697                 imfp[idx].imf_fmode = MCAST_EXCLUDE;
  698                 imfp[idx].imf_nsources = 0;
  699                 TAILQ_INIT(&imfp[idx].imf_sources);
  700         }
  701         imo->imo_mfilters = imfp;
  702 
  703         INP_WLOCK(inp);
  704         if (inp->inp_moptions != NULL) {
  705                 free(imfp, M_IPMSOURCE);
  706                 free(immp, M_IPMOPTS);
  707                 free(imo, M_IPMOPTS);
  708                 return (inp->inp_moptions);
  709         }
  710         inp->inp_moptions = imo;
  711         return (imo);
  712 }
  713 
  714 /*
  715  * Discard the IP multicast options (and source filters).
  716  */
  717 void
  718 inp_freemoptions(struct ip_moptions *imo)
  719 {
  720         struct in_mfilter       *imf;
  721         struct in_msource       *ims, *tims;
  722         size_t                   idx, nmships;
  723 
  724         KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__));
  725 
  726         nmships = imo->imo_num_memberships;
  727         for (idx = 0; idx < nmships; ++idx) {
  728                 in_delmulti(imo->imo_membership[idx]);
  729 
  730                 if (imo->imo_mfilters != NULL) {
  731                         imf = &imo->imo_mfilters[idx];
  732                         TAILQ_FOREACH_SAFE(ims, &imf->imf_sources,
  733                             ims_next, tims) {
  734                                 TAILQ_REMOVE(&imf->imf_sources, ims, ims_next);
  735                                 FREE(ims, M_IPMSOURCE);
  736                                 imf->imf_nsources--;
  737                         }
  738                         KASSERT(imf->imf_nsources == 0,
  739                             ("%s: did not free all imf_nsources", __func__));
  740                 }
  741         }
  742 
  743         if (imo->imo_mfilters != NULL)
  744                 free(imo->imo_mfilters, M_IPMSOURCE);
  745         free(imo->imo_membership, M_IPMOPTS);
  746         free(imo, M_IPMOPTS);
  747 }
  748 
  749 /*
  750  * Atomically get source filters on a socket for an IPv4 multicast group.
  751  * Called with INP lock held; returns with lock released.
  752  */
  753 static int
  754 inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
  755 {
  756         struct __msfilterreq     msfr;
  757         sockunion_t             *gsa;
  758         struct ifnet            *ifp;
  759         struct ip_moptions      *imo;
  760         struct in_mfilter       *imf;
  761         struct in_msource       *ims;
  762         struct sockaddr_storage *ptss;
  763         struct sockaddr_storage *tss;
  764         int                      error;
  765         size_t                   idx;
  766 
  767         INP_WLOCK_ASSERT(inp);
  768 
  769         imo = inp->inp_moptions;
  770         KASSERT(imo != NULL, ("%s: null ip_moptions", __func__));
  771 
  772         INP_WUNLOCK(inp);
  773 
  774         error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
  775             sizeof(struct __msfilterreq));
  776         if (error)
  777                 return (error);
  778 
  779         if (msfr.msfr_ifindex == 0 || if_index < msfr.msfr_ifindex)
  780                 return (EINVAL);
  781 
  782         ifp = ifnet_byindex(msfr.msfr_ifindex);
  783         if (ifp == NULL)
  784                 return (EINVAL);
  785 
  786         INP_WLOCK(inp);
  787 
  788         /*
  789          * Lookup group on the socket.
  790          */
  791         gsa = (sockunion_t *)&msfr.msfr_group;
  792         idx = imo_match_group(imo, ifp, &gsa->sa);
  793         if (idx == -1 || imo->imo_mfilters == NULL) {
  794                 INP_WUNLOCK(inp);
  795                 return (EADDRNOTAVAIL);
  796         }
  797 
  798         imf = &imo->imo_mfilters[idx];
  799         msfr.msfr_fmode = imf->imf_fmode;
  800         msfr.msfr_nsrcs = imf->imf_nsources;
  801 
  802         /*
  803          * If the user specified a buffer, copy out the source filter
  804          * entries to userland gracefully.
  805          * msfr.msfr_nsrcs is always set to the total number of filter
  806          * entries which the kernel currently has for this group.
  807          */
  808         tss = NULL;
  809         if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) {
  810                 /*
  811                  * Make a copy of the source vector so that we do not
  812                  * thrash the inpcb lock whilst copying it out.
  813                  * We only copy out the number of entries which userland
  814                  * has asked for, but we always tell userland how big the
  815                  * buffer really needs to be.
  816                  */
  817                 MALLOC(tss, struct sockaddr_storage *,
  818                     sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
  819                     M_TEMP, M_NOWAIT);
  820                 if (tss == NULL) {
  821                         error = ENOBUFS;
  822                 } else {
  823                         ptss = tss;
  824                         TAILQ_FOREACH(ims, &imf->imf_sources, ims_next) {
  825                                 memcpy(ptss++, &ims->ims_addr,
  826                                     sizeof(struct sockaddr_storage));
  827                         }
  828                 }
  829         }
  830 
  831         INP_WUNLOCK(inp);
  832 
  833         if (tss != NULL) {
  834                 error = copyout(tss, msfr.msfr_srcs,
  835                     sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
  836                 FREE(tss, M_TEMP);
  837         }
  838 
  839         if (error)
  840                 return (error);
  841 
  842         error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq));
  843 
  844         return (error);
  845 }
  846 
  847 /*
  848  * Return the IP multicast options in response to user getsockopt().
  849  */
  850 int
  851 inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
  852 {
  853         struct ip_mreqn          mreqn;
  854         struct ip_moptions      *imo;
  855         struct ifnet            *ifp;
  856         struct in_ifaddr        *ia;
  857         int                      error, optval;
  858         u_char                   coptval;
  859 
  860         INP_WLOCK(inp);
  861         imo = inp->inp_moptions;
  862         /*
  863          * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
  864          * or is a divert socket, reject it.
  865          */
  866         if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
  867             (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
  868             inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) {
  869                 INP_WUNLOCK(inp);
  870                 return (EOPNOTSUPP);
  871         }
  872 
  873         error = 0;
  874         switch (sopt->sopt_name) {
  875         case IP_MULTICAST_VIF:
  876                 if (imo != NULL)
  877                         optval = imo->imo_multicast_vif;
  878                 else
  879                         optval = -1;
  880                 INP_WUNLOCK(inp);
  881                 error = sooptcopyout(sopt, &optval, sizeof(int));
  882                 break;
  883 
  884         case IP_MULTICAST_IF:
  885                 memset(&mreqn, 0, sizeof(struct ip_mreqn));
  886                 if (imo != NULL) {
  887                         ifp = imo->imo_multicast_ifp;
  888                         if (imo->imo_multicast_addr.s_addr != INADDR_ANY) {
  889                                 mreqn.imr_address = imo->imo_multicast_addr;
  890                         } else if (ifp != NULL) {
  891                                 mreqn.imr_ifindex = ifp->if_index;
  892                                 IFP_TO_IA(ifp, ia);
  893                                 if (ia != NULL) {
  894                                         mreqn.imr_address =
  895                                             IA_SIN(ia)->sin_addr;
  896                                 }
  897                         }
  898                 }
  899                 INP_WUNLOCK(inp);
  900                 if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
  901                         error = sooptcopyout(sopt, &mreqn,
  902                             sizeof(struct ip_mreqn));
  903                 } else {
  904                         error = sooptcopyout(sopt, &mreqn.imr_address,
  905                             sizeof(struct in_addr));
  906                 }
  907                 break;
  908 
  909         case IP_MULTICAST_TTL:
  910                 if (imo == 0)
  911                         optval = coptval = IP_DEFAULT_MULTICAST_TTL;
  912                 else
  913                         optval = coptval = imo->imo_multicast_ttl;
  914                 INP_WUNLOCK(inp);
  915                 if (sopt->sopt_valsize == sizeof(u_char))
  916                         error = sooptcopyout(sopt, &coptval, sizeof(u_char));
  917                 else
  918                         error = sooptcopyout(sopt, &optval, sizeof(int));
  919                 break;
  920 
  921         case IP_MULTICAST_LOOP:
  922                 if (imo == 0)
  923                         optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
  924                 else
  925                         optval = coptval = imo->imo_multicast_loop;
  926                 INP_WUNLOCK(inp);
  927                 if (sopt->sopt_valsize == sizeof(u_char))
  928                         error = sooptcopyout(sopt, &coptval, sizeof(u_char));
  929                 else
  930                         error = sooptcopyout(sopt, &optval, sizeof(int));
  931                 break;
  932 
  933         case IP_MSFILTER:
  934                 if (imo == NULL) {
  935                         error = EADDRNOTAVAIL;
  936                         INP_WUNLOCK(inp);
  937                 } else {
  938                         error = inp_get_source_filters(inp, sopt);
  939                 }
  940                 break;
  941 
  942         default:
  943                 INP_WUNLOCK(inp);
  944                 error = ENOPROTOOPT;
  945                 break;
  946         }
  947 
  948         INP_UNLOCK_ASSERT(inp);
  949 
  950         return (error);
  951 }
  952 
  953 /*
  954  * Join an IPv4 multicast group, possibly with a source.
  955  */
  956 static int
  957 inp_join_group(struct inpcb *inp, struct sockopt *sopt)
  958 {
  959         struct group_source_req          gsr;
  960         sockunion_t                     *gsa, *ssa;
  961         struct ifnet                    *ifp;
  962         struct in_mfilter               *imf;
  963         struct ip_moptions              *imo;
  964         struct in_multi                 *inm;
  965         size_t                           idx;
  966         int                              error;
  967 
  968         ifp = NULL;
  969         error = 0;
  970 
  971         memset(&gsr, 0, sizeof(struct group_source_req));
  972         gsa = (sockunion_t *)&gsr.gsr_group;
  973         gsa->ss.ss_family = AF_UNSPEC;
  974         ssa = (sockunion_t *)&gsr.gsr_source;
  975         ssa->ss.ss_family = AF_UNSPEC;
  976 
  977         switch (sopt->sopt_name) {
  978         case IP_ADD_MEMBERSHIP:
  979         case IP_ADD_SOURCE_MEMBERSHIP: {
  980                 struct ip_mreq_source    mreqs;
  981 
  982                 if (sopt->sopt_name == IP_ADD_MEMBERSHIP) {
  983                         error = sooptcopyin(sopt, &mreqs,
  984                             sizeof(struct ip_mreq),
  985                             sizeof(struct ip_mreq));
  986                         /*
  987                          * Do argument switcharoo from ip_mreq into
  988                          * ip_mreq_source to avoid using two instances.
  989                          */
  990                         mreqs.imr_interface = mreqs.imr_sourceaddr;
  991                         mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
  992                 } else if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
  993                         error = sooptcopyin(sopt, &mreqs,
  994                             sizeof(struct ip_mreq_source),
  995                             sizeof(struct ip_mreq_source));
  996                 }
  997                 if (error)
  998                         return (error);
  999 
 1000                 gsa->sin.sin_family = AF_INET;
 1001                 gsa->sin.sin_len = sizeof(struct sockaddr_in);
 1002                 gsa->sin.sin_addr = mreqs.imr_multiaddr;
 1003 
 1004                 if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
 1005                         ssa->sin.sin_family = AF_INET;
 1006                         ssa->sin.sin_len = sizeof(struct sockaddr_in);
 1007                         ssa->sin.sin_addr = mreqs.imr_sourceaddr;
 1008                 }
 1009 
 1010                 /*
 1011                  * Obtain ifp. If no interface address was provided,
 1012                  * use the interface of the route in the unicast FIB for
 1013                  * the given multicast destination; usually, this is the
 1014                  * default route.
 1015                  * If this lookup fails, attempt to use the first non-loopback
 1016                  * interface with multicast capability in the system as a
 1017                  * last resort. The legacy IPv4 ASM API requires that we do
 1018                  * this in order to allow groups to be joined when the routing
 1019                  * table has not yet been populated during boot.
 1020                  * If all of these conditions fail, return EADDRNOTAVAIL, and
 1021                  * reject the IPv4 multicast join.
 1022                  */
 1023                 if (mreqs.imr_interface.s_addr != INADDR_ANY) {
 1024                         ifp = ip_multicast_if(&mreqs.imr_interface);
 1025                 } else {
 1026                         struct route ro;
 1027 
 1028                         ro.ro_rt = NULL;
 1029                         *(struct sockaddr_in *)&ro.ro_dst = gsa->sin;
 1030                         in_rtalloc_ign(&ro, RTF_CLONING,
 1031                            inp->inp_inc.inc_fibnum);
 1032                         if (ro.ro_rt != NULL) {
 1033                                 ifp = ro.ro_rt->rt_ifp;
 1034                                 KASSERT(ifp != NULL, ("%s: null ifp",
 1035                                     __func__));
 1036                                 RTFREE(ro.ro_rt);
 1037                         } else {
 1038                                 struct in_ifaddr *ia;
 1039                                 struct ifnet *mfp = NULL;
 1040                                 TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
 1041                                         mfp = ia->ia_ifp;
 1042                                         if (!(mfp->if_flags & IFF_LOOPBACK) &&
 1043                                              (mfp->if_flags & IFF_MULTICAST)) {
 1044                                                 ifp = mfp;
 1045                                                 break;
 1046                                         }
 1047                                 }
 1048                         }
 1049                 }
 1050 #ifdef DIAGNOSTIC
 1051                 if (bootverbose) {
 1052                         printf("%s: imr_interface = %s, ifp = %p\n",
 1053                             __func__, inet_ntoa(mreqs.imr_interface), ifp);
 1054                 }
 1055 #endif
 1056                 break;
 1057         }
 1058 
 1059         case MCAST_JOIN_GROUP:
 1060         case MCAST_JOIN_SOURCE_GROUP:
 1061                 if (sopt->sopt_name == MCAST_JOIN_GROUP) {
 1062                         error = sooptcopyin(sopt, &gsr,
 1063                             sizeof(struct group_req),
 1064                             sizeof(struct group_req));
 1065                 } else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
 1066                         error = sooptcopyin(sopt, &gsr,
 1067                             sizeof(struct group_source_req),
 1068                             sizeof(struct group_source_req));
 1069                 }
 1070                 if (error)
 1071                         return (error);
 1072 
 1073                 if (gsa->sin.sin_family != AF_INET ||
 1074                     gsa->sin.sin_len != sizeof(struct sockaddr_in))
 1075                         return (EINVAL);
 1076 
 1077                 /*
 1078                  * Overwrite the port field if present, as the sockaddr
 1079                  * being copied in may be matched with a binary comparison.
 1080                  * XXX INET6
 1081                  */
 1082                 gsa->sin.sin_port = 0;
 1083                 if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
 1084                         if (ssa->sin.sin_family != AF_INET ||
 1085                             ssa->sin.sin_len != sizeof(struct sockaddr_in))
 1086                                 return (EINVAL);
 1087                         ssa->sin.sin_port = 0;
 1088                 }
 1089 
 1090                 /*
 1091                  * Obtain the ifp.
 1092                  */
 1093                 if (gsr.gsr_interface == 0 || if_index < gsr.gsr_interface)
 1094                         return (EADDRNOTAVAIL);
 1095                 ifp = ifnet_byindex(gsr.gsr_interface);
 1096 
 1097                 break;
 1098 
 1099         default:
 1100 #ifdef DIAGNOSTIC
 1101                 if (bootverbose) {
 1102                         printf("%s: unknown sopt_name %d\n", __func__,
 1103                             sopt->sopt_name);
 1104                 }
 1105 #endif
 1106                 return (EOPNOTSUPP);
 1107                 break;
 1108         }
 1109 
 1110         if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
 1111                 return (EINVAL);
 1112 
 1113         if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
 1114                 return (EADDRNOTAVAIL);
 1115 
 1116         /*
 1117          * Check if we already hold membership of this group for this inpcb.
 1118          * If so, we do not need to perform the initial join.
 1119          */
 1120         imo = inp_findmoptions(inp);
 1121         idx = imo_match_group(imo, ifp, &gsa->sa);
 1122         if (idx != -1) {
 1123                 if (ssa->ss.ss_family != AF_UNSPEC) {
 1124                         /*
 1125                          * Attempting to join an ASM group (when already
 1126                          * an ASM or SSM member) is an error.
 1127                          */
 1128                         error = EADDRNOTAVAIL;
 1129                 } else {
 1130                         imf = &imo->imo_mfilters[idx];
 1131                         if (imf->imf_nsources == 0) {
 1132                                 /*
 1133                                  * Attempting to join an SSM group (when
 1134                                  * already an ASM member) is an error.
 1135                                  */
 1136                                 error = EINVAL;
 1137                         } else {
 1138                                 /*
 1139                                  * Attempting to join an SSM group (when
 1140                                  * already an SSM member) means "add this
 1141                                  * source to the inclusive filter list".
 1142                                  */
 1143                                 error = imo_join_source(imo, idx, ssa);
 1144                         }
 1145                 }
 1146                 goto out_locked;
 1147         }
 1148 
 1149         /*
 1150          * Call imo_grow() to reallocate the membership and source filter
 1151          * vectors if they are full. If the size would exceed the hard limit,
 1152          * then we know we've really run out of entries. We keep the INP
 1153          * lock held to avoid introducing a race condition.
 1154          */
 1155         if (imo->imo_num_memberships == imo->imo_max_memberships) {
 1156                 error = imo_grow(imo);
 1157                 if (error)
 1158                         goto out_locked;
 1159         }
 1160 
 1161         /*
 1162          * So far, so good: perform the layer 3 join, layer 2 join,
 1163          * and make an IGMP announcement if needed.
 1164          */
 1165         inm = in_addmulti(&gsa->sin.sin_addr, ifp);
 1166         if (inm == NULL) {
 1167                 error = ENOBUFS;
 1168                 goto out_locked;
 1169         }
 1170         idx = imo->imo_num_memberships;
 1171         imo->imo_membership[idx] = inm;
 1172         imo->imo_num_memberships++;
 1173 
 1174         KASSERT(imo->imo_mfilters != NULL,
 1175             ("%s: imf_mfilters vector was not allocated", __func__));
 1176         imf = &imo->imo_mfilters[idx];
 1177         KASSERT(TAILQ_EMPTY(&imf->imf_sources),
 1178             ("%s: imf_sources not empty", __func__));
 1179 
 1180         /*
 1181          * If this is a new SSM group join (i.e. a source was specified
 1182          * with this group), add this source to the filter list.
 1183          */
 1184         if (ssa->ss.ss_family != AF_UNSPEC) {
 1185                 /*
 1186                  * An initial SSM join implies that this socket's membership
 1187                  * of the multicast group is now in inclusive mode.
 1188                  */
 1189                 imf->imf_fmode = MCAST_INCLUDE;
 1190 
 1191                 error = imo_join_source(imo, idx, ssa);
 1192                 if (error) {
 1193                         /*
 1194                          * Drop inp lock before calling in_delmulti(),
 1195                          * to prevent a lock order reversal.
 1196                          */
 1197                         --imo->imo_num_memberships;
 1198                         INP_WUNLOCK(inp);
 1199                         in_delmulti(inm);
 1200                         return (error);
 1201                 }
 1202         }
 1203 
 1204 out_locked:
 1205         INP_WUNLOCK(inp);
 1206         return (error);
 1207 }
 1208 
 1209 /*
 1210  * Leave an IPv4 multicast group on an inpcb, possibly with a source.
 1211  */
 1212 static int
 1213 inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
 1214 {
 1215         struct group_source_req          gsr;
 1216         struct ip_mreq_source            mreqs;
 1217         sockunion_t                     *gsa, *ssa;
 1218         struct ifnet                    *ifp;
 1219         struct in_mfilter               *imf;
 1220         struct ip_moptions              *imo;
 1221         struct in_msource               *ims, *tims;
 1222         struct in_multi                 *inm;
 1223         size_t                           idx;
 1224         int                              error;
 1225 
 1226         ifp = NULL;
 1227         error = 0;
 1228 
 1229         memset(&gsr, 0, sizeof(struct group_source_req));
 1230         gsa = (sockunion_t *)&gsr.gsr_group;
 1231         gsa->ss.ss_family = AF_UNSPEC;
 1232         ssa = (sockunion_t *)&gsr.gsr_source;
 1233         ssa->ss.ss_family = AF_UNSPEC;
 1234 
 1235         switch (sopt->sopt_name) {
 1236         case IP_DROP_MEMBERSHIP:
 1237         case IP_DROP_SOURCE_MEMBERSHIP:
 1238                 if (sopt->sopt_name == IP_DROP_MEMBERSHIP) {
 1239                         error = sooptcopyin(sopt, &mreqs,
 1240                             sizeof(struct ip_mreq),
 1241                             sizeof(struct ip_mreq));
 1242                         /*
 1243                          * Swap interface and sourceaddr arguments,
 1244                          * as ip_mreq and ip_mreq_source are laid
 1245                          * out differently.
 1246                          */
 1247                         mreqs.imr_interface = mreqs.imr_sourceaddr;
 1248                         mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
 1249                 } else if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
 1250                         error = sooptcopyin(sopt, &mreqs,
 1251                             sizeof(struct ip_mreq_source),
 1252                             sizeof(struct ip_mreq_source));
 1253                 }
 1254                 if (error)
 1255                         return (error);
 1256 
 1257                 gsa->sin.sin_family = AF_INET;
 1258                 gsa->sin.sin_len = sizeof(struct sockaddr_in);
 1259                 gsa->sin.sin_addr = mreqs.imr_multiaddr;
 1260 
 1261                 if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
 1262                         ssa->sin.sin_family = AF_INET;
 1263                         ssa->sin.sin_len = sizeof(struct sockaddr_in);
 1264                         ssa->sin.sin_addr = mreqs.imr_sourceaddr;
 1265                 }
 1266 
 1267                 if (gsa->sin.sin_addr.s_addr != INADDR_ANY)
 1268                         INADDR_TO_IFP(mreqs.imr_interface, ifp);
 1269 
 1270 #ifdef DIAGNOSTIC
 1271                 if (bootverbose) {
 1272                         printf("%s: imr_interface = %s, ifp = %p\n",
 1273                             __func__, inet_ntoa(mreqs.imr_interface), ifp);
 1274                 }
 1275 #endif
 1276                 break;
 1277 
 1278         case MCAST_LEAVE_GROUP:
 1279         case MCAST_LEAVE_SOURCE_GROUP:
 1280                 if (sopt->sopt_name == MCAST_LEAVE_GROUP) {
 1281                         error = sooptcopyin(sopt, &gsr,
 1282                             sizeof(struct group_req),
 1283                             sizeof(struct group_req));
 1284                 } else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
 1285                         error = sooptcopyin(sopt, &gsr,
 1286                             sizeof(struct group_source_req),
 1287                             sizeof(struct group_source_req));
 1288                 }
 1289                 if (error)
 1290                         return (error);
 1291 
 1292                 if (gsa->sin.sin_family != AF_INET ||
 1293                     gsa->sin.sin_len != sizeof(struct sockaddr_in))
 1294                         return (EINVAL);
 1295 
 1296                 if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
 1297                         if (ssa->sin.sin_family != AF_INET ||
 1298                             ssa->sin.sin_len != sizeof(struct sockaddr_in))
 1299                                 return (EINVAL);
 1300                 }
 1301 
 1302                 if (gsr.gsr_interface == 0 || if_index < gsr.gsr_interface)
 1303                         return (EADDRNOTAVAIL);
 1304 
 1305                 ifp = ifnet_byindex(gsr.gsr_interface);
 1306                 break;
 1307 
 1308         default:
 1309 #ifdef DIAGNOSTIC
 1310                 if (bootverbose) {
 1311                         printf("%s: unknown sopt_name %d\n", __func__,
 1312                             sopt->sopt_name);
 1313                 }
 1314 #endif
 1315                 return (EOPNOTSUPP);
 1316                 break;
 1317         }
 1318 
 1319         if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
 1320                 return (EINVAL);
 1321 
 1322         /*
 1323          * Find the membership in the membership array.
 1324          */
 1325         imo = inp_findmoptions(inp);
 1326         idx = imo_match_group(imo, ifp, &gsa->sa);
 1327         if (idx == -1) {
 1328                 error = EADDRNOTAVAIL;
 1329                 goto out_locked;
 1330         }
 1331         imf = &imo->imo_mfilters[idx];
 1332 
 1333         /*
 1334          * If we were instructed only to leave a given source, do so.
 1335          */
 1336         if (ssa->ss.ss_family != AF_UNSPEC) {
 1337                 if (imf->imf_nsources == 0 ||
 1338                     imf->imf_fmode == MCAST_EXCLUDE) {
 1339                         /*
 1340                          * Attempting to SSM leave an ASM group
 1341                          * is an error; should use *_BLOCK_SOURCE instead.
 1342                          * Attempting to SSM leave a source in a group when
 1343                          * the socket is in 'exclude mode' is also an error.
 1344                          */
 1345                         error = EINVAL;
 1346                 } else {
 1347                         error = imo_leave_source(imo, idx, ssa);
 1348                 }
 1349                 /*
 1350                  * If an error occurred, or this source is not the last
 1351                  * source in the group, do not leave the whole group.
 1352                  */
 1353                 if (error || imf->imf_nsources > 0)
 1354                         goto out_locked;
 1355         }
 1356 
 1357         /*
 1358          * Give up the multicast address record to which the membership points.
 1359          */
 1360         inm = imo->imo_membership[idx];
 1361         in_delmulti(inm);
 1362 
 1363         /*
 1364          * Free any source filters for this group if they exist.
 1365          * Revert inpcb to the default MCAST_EXCLUDE state.
 1366          */
 1367         if (imo->imo_mfilters != NULL) {
 1368                 TAILQ_FOREACH_SAFE(ims, &imf->imf_sources, ims_next, tims) {
 1369                         TAILQ_REMOVE(&imf->imf_sources, ims, ims_next);
 1370                         FREE(ims, M_IPMSOURCE);
 1371                         imf->imf_nsources--;
 1372                 }
 1373                 KASSERT(imf->imf_nsources == 0,
 1374                     ("%s: imf_nsources not 0", __func__));
 1375                 KASSERT(TAILQ_EMPTY(&imf->imf_sources),
 1376                     ("%s: imf_sources not empty", __func__));
 1377                 imf->imf_fmode = MCAST_EXCLUDE;
 1378         }
 1379 
 1380         /*
 1381          * Remove the gap in the membership array.
 1382          */
 1383         for (++idx; idx < imo->imo_num_memberships; ++idx)
 1384                 imo->imo_membership[idx-1] = imo->imo_membership[idx];
 1385         imo->imo_num_memberships--;
 1386 
 1387 out_locked:
 1388         INP_WUNLOCK(inp);
 1389         return (error);
 1390 }
 1391 
 1392 /*
 1393  * Select the interface for transmitting IPv4 multicast datagrams.
 1394  *
 1395  * Either an instance of struct in_addr or an instance of struct ip_mreqn
 1396  * may be passed to this socket option. An address of INADDR_ANY or an
 1397  * interface index of 0 is used to remove a previous selection.
 1398  * When no interface is selected, one is chosen for every send.
 1399  */
 1400 static int
 1401 inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
 1402 {
 1403         struct in_addr           addr;
 1404         struct ip_mreqn          mreqn;
 1405         struct ifnet            *ifp;
 1406         struct ip_moptions      *imo;
 1407         int                      error;
 1408 
 1409         if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
 1410                 /*
 1411                  * An interface index was specified using the
 1412                  * Linux-derived ip_mreqn structure.
 1413                  */
 1414                 error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn),
 1415                     sizeof(struct ip_mreqn));
 1416                 if (error)
 1417                         return (error);
 1418 
 1419                 if (mreqn.imr_ifindex < 0 || if_index < mreqn.imr_ifindex)
 1420                         return (EINVAL);
 1421 
 1422                 if (mreqn.imr_ifindex == 0) {
 1423                         ifp = NULL;
 1424                 } else {
 1425                         ifp = ifnet_byindex(mreqn.imr_ifindex);
 1426                         if (ifp == NULL)
 1427                                 return (EADDRNOTAVAIL);
 1428                 }
 1429         } else {
 1430                 /*
 1431                  * An interface was specified by IPv4 address.
 1432                  * This is the traditional BSD usage.
 1433                  */
 1434                 error = sooptcopyin(sopt, &addr, sizeof(struct in_addr),
 1435                     sizeof(struct in_addr));
 1436                 if (error)
 1437                         return (error);
 1438                 if (addr.s_addr == INADDR_ANY) {
 1439                         ifp = NULL;
 1440                 } else {
 1441                         ifp = ip_multicast_if(&addr);
 1442                         if (ifp == NULL)
 1443                                 return (EADDRNOTAVAIL);
 1444                 }
 1445 #ifdef DIAGNOSTIC
 1446                 if (bootverbose) {
 1447                         printf("%s: ifp = %p, addr = %s\n",
 1448                             __func__, ifp, inet_ntoa(addr)); /* XXX INET6 */
 1449                 }
 1450 #endif
 1451         }
 1452 
 1453         /* Reject interfaces which do not support multicast. */
 1454         if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0)
 1455                 return (EOPNOTSUPP);
 1456 
 1457         imo = inp_findmoptions(inp);
 1458         imo->imo_multicast_ifp = ifp;
 1459         imo->imo_multicast_addr.s_addr = INADDR_ANY;
 1460         INP_WUNLOCK(inp);
 1461 
 1462         return (0);
 1463 }
 1464 
 1465 /*
 1466  * Atomically set source filters on a socket for an IPv4 multicast group.
 1467  */
 1468 static int
 1469 inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
 1470 {
 1471         struct __msfilterreq     msfr;
 1472         sockunion_t             *gsa;
 1473         struct ifnet            *ifp;
 1474         struct in_mfilter       *imf;
 1475         struct ip_moptions      *imo;
 1476         struct in_msource       *ims, *tims;
 1477         size_t                   idx;
 1478         int                      error;
 1479 
 1480         error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
 1481             sizeof(struct __msfilterreq));
 1482         if (error)
 1483                 return (error);
 1484 
 1485         if (msfr.msfr_nsrcs > IP_MAX_SOURCE_FILTER ||
 1486             (msfr.msfr_fmode != MCAST_EXCLUDE &&
 1487              msfr.msfr_fmode != MCAST_INCLUDE))
 1488                 return (EINVAL);
 1489 
 1490         if (msfr.msfr_group.ss_family != AF_INET ||
 1491             msfr.msfr_group.ss_len != sizeof(struct sockaddr_in))
 1492                 return (EINVAL);
 1493 
 1494         gsa = (sockunion_t *)&msfr.msfr_group;
 1495         if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
 1496                 return (EINVAL);
 1497 
 1498         gsa->sin.sin_port = 0;  /* ignore port */
 1499 
 1500         if (msfr.msfr_ifindex == 0 || if_index < msfr.msfr_ifindex)
 1501                 return (EADDRNOTAVAIL);
 1502 
 1503         ifp = ifnet_byindex(msfr.msfr_ifindex);
 1504         if (ifp == NULL)
 1505                 return (EADDRNOTAVAIL);
 1506 
 1507         /*
 1508          * Take the INP lock.
 1509          * Check if this socket is a member of this group.
 1510          */
 1511         imo = inp_findmoptions(inp);
 1512         idx = imo_match_group(imo, ifp, &gsa->sa);
 1513         if (idx == -1 || imo->imo_mfilters == NULL) {
 1514                 error = EADDRNOTAVAIL;
 1515                 goto out_locked;
 1516         }
 1517         imf = &imo->imo_mfilters[idx];
 1518 
 1519 #ifdef DIAGNOSTIC
 1520         if (bootverbose)
 1521                 printf("%s: clearing source list\n", __func__);
 1522 #endif
 1523 
 1524         /*
 1525          * Remove any existing source filters.
 1526          */
 1527         TAILQ_FOREACH_SAFE(ims, &imf->imf_sources, ims_next, tims) {
 1528                 TAILQ_REMOVE(&imf->imf_sources, ims, ims_next);
 1529                 FREE(ims, M_IPMSOURCE);
 1530                 imf->imf_nsources--;
 1531         }
 1532         KASSERT(imf->imf_nsources == 0,
 1533             ("%s: source list not cleared", __func__));
 1534 
 1535         /*
 1536          * Apply any new source filters, if present.
 1537          */
 1538         if (msfr.msfr_nsrcs > 0) {
 1539                 struct in_msource       **pnims;
 1540                 struct in_msource       *nims;
 1541                 struct sockaddr_storage *kss;
 1542                 struct sockaddr_storage *pkss;
 1543                 sockunion_t             *psu;
 1544                 int                      i, j;
 1545 
 1546                 /*
 1547                  * Drop the inp lock so we may sleep if we need to
 1548                  * in order to satisfy a malloc request.
 1549                  * We will re-take it before changing socket state.
 1550                  */
 1551                 INP_WUNLOCK(inp);
 1552 #ifdef DIAGNOSTIC
 1553                 if (bootverbose) {
 1554                         printf("%s: loading %lu source list entries\n",
 1555                             __func__, (unsigned long)msfr.msfr_nsrcs);
 1556                 }
 1557 #endif
 1558                 /*
 1559                  * Make a copy of the user-space source vector so
 1560                  * that we may copy them with a single copyin. This
 1561                  * allows us to deal with page faults up-front.
 1562                  */
 1563                 MALLOC(kss, struct sockaddr_storage *,
 1564                     sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
 1565                     M_TEMP, M_WAITOK);
 1566                 error = copyin(msfr.msfr_srcs, kss,
 1567                     sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
 1568                 if (error) {
 1569                         FREE(kss, M_TEMP);
 1570                         return (error);
 1571                 }
 1572 
 1573                 /*
 1574                  * Perform argument checking on every sockaddr_storage
 1575                  * structure in the vector provided to us. Overwrite
 1576                  * fields which should not apply to source entries.
 1577                  * TODO: Check for duplicate sources on this pass.
 1578                  */
 1579                 psu = (sockunion_t *)kss;
 1580                 for (i = 0; i < msfr.msfr_nsrcs; i++, psu++) {
 1581                         switch (psu->ss.ss_family) {
 1582                         case AF_INET:
 1583                                 if (psu->sin.sin_len !=
 1584                                     sizeof(struct sockaddr_in)) {
 1585                                         error = EINVAL;
 1586                                 } else {
 1587                                         psu->sin.sin_port = 0;
 1588                                 }
 1589                                 break;
 1590 #ifdef notyet
 1591                         case AF_INET6;
 1592                                 if (psu->sin6.sin6_len !=
 1593                                     sizeof(struct sockaddr_in6)) {
 1594                                         error = EINVAL;
 1595                                 } else {
 1596                                         psu->sin6.sin6_port = 0;
 1597                                         psu->sin6.sin6_flowinfo = 0;
 1598                                 }
 1599                                 break;
 1600 #endif
 1601                         default:
 1602                                 error = EAFNOSUPPORT;
 1603                                 break;
 1604                         }
 1605                         if (error)
 1606                                 break;
 1607                 }
 1608                 if (error) {
 1609                         FREE(kss, M_TEMP);
 1610                         return (error);
 1611                 }
 1612 
 1613                 /*
 1614                  * Allocate a block to track all the in_msource
 1615                  * entries we are about to allocate, in case we
 1616                  * abruptly need to free them.
 1617                  */
 1618                 MALLOC(pnims, struct in_msource **,
 1619                     sizeof(struct in_msource *) * msfr.msfr_nsrcs,
 1620                     M_TEMP, M_WAITOK | M_ZERO);
 1621 
 1622                 /*
 1623                  * Allocate up to nsrcs individual chunks.
 1624                  * If we encounter an error, backtrack out of
 1625                  * all allocations cleanly; updates must be atomic.
 1626                  */
 1627                 pkss = kss;
 1628                 nims = NULL;
 1629                 for (i = 0; i < msfr.msfr_nsrcs; i++, pkss++) {
 1630                         MALLOC(nims, struct in_msource *,
 1631                             sizeof(struct in_msource) * msfr.msfr_nsrcs,
 1632                             M_IPMSOURCE, M_WAITOK | M_ZERO);
 1633                         pnims[i] = nims;
 1634                 }
 1635                 if (i < msfr.msfr_nsrcs) {
 1636                         for (j = 0; j < i; j++) {
 1637                                 if (pnims[j] != NULL)
 1638                                         FREE(pnims[j], M_IPMSOURCE);
 1639                         }
 1640                         FREE(pnims, M_TEMP);
 1641                         FREE(kss, M_TEMP);
 1642                         return (ENOBUFS);
 1643                 }
 1644 
 1645                 INP_UNLOCK_ASSERT(inp);
 1646 
 1647                 /*
 1648                  * Finally, apply the filters to the socket.
 1649                  * Re-take the inp lock; we are changing socket state.
 1650                  */
 1651                 pkss = kss;
 1652                 INP_WLOCK(inp);
 1653                 for (i = 0; i < msfr.msfr_nsrcs; i++, pkss++) {
 1654                         memcpy(&(pnims[i]->ims_addr), pkss,
 1655                             sizeof(struct sockaddr_storage));
 1656                         TAILQ_INSERT_TAIL(&imf->imf_sources, pnims[i],
 1657                             ims_next);
 1658                         imf->imf_nsources++;
 1659                 }
 1660                 FREE(pnims, M_TEMP);
 1661                 FREE(kss, M_TEMP);
 1662         }
 1663 
 1664         /*
 1665          * Update the filter mode on the socket before releasing the inpcb.
 1666          */
 1667         INP_WLOCK_ASSERT(inp);
 1668         imf->imf_fmode = msfr.msfr_fmode;
 1669 
 1670 out_locked:
 1671         INP_WUNLOCK(inp);
 1672         return (error);
 1673 }
 1674 
 1675 /*
 1676  * Set the IP multicast options in response to user setsockopt().
 1677  *
 1678  * Many of the socket options handled in this function duplicate the
 1679  * functionality of socket options in the regular unicast API. However,
 1680  * it is not possible to merge the duplicate code, because the idempotence
 1681  * of the IPv4 multicast part of the BSD Sockets API must be preserved;
 1682  * the effects of these options must be treated as separate and distinct.
 1683  */
 1684 int
 1685 inp_setmoptions(struct inpcb *inp, struct sockopt *sopt)
 1686 {
 1687         struct ip_moptions      *imo;
 1688         int                      error;
 1689 
 1690         error = 0;
 1691 
 1692         /*
 1693          * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
 1694          * or is a divert socket, reject it.
 1695          * XXX Unlocked read of inp_socket believed OK.
 1696          */
 1697         if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
 1698             (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
 1699             inp->inp_socket->so_proto->pr_type != SOCK_DGRAM))
 1700                 return (EOPNOTSUPP);
 1701 
 1702         switch (sopt->sopt_name) {
 1703         case IP_MULTICAST_VIF: {
 1704                 int vifi;
 1705                 /*
 1706                  * Select a multicast VIF for transmission.
 1707                  * Only useful if multicast forwarding is active.
 1708                  */
 1709                 if (legal_vif_num == NULL) {
 1710                         error = EOPNOTSUPP;
 1711                         break;
 1712                 }
 1713                 error = sooptcopyin(sopt, &vifi, sizeof(int), sizeof(int));
 1714                 if (error)
 1715                         break;
 1716                 if (!legal_vif_num(vifi) && (vifi != -1)) {
 1717                         error = EINVAL;
 1718                         break;
 1719                 }
 1720                 imo = inp_findmoptions(inp);
 1721                 imo->imo_multicast_vif = vifi;
 1722                 INP_WUNLOCK(inp);
 1723                 break;
 1724         }
 1725 
 1726         case IP_MULTICAST_IF:
 1727                 error = inp_set_multicast_if(inp, sopt);
 1728                 break;
 1729 
 1730         case IP_MULTICAST_TTL: {
 1731                 u_char ttl;
 1732 
 1733                 /*
 1734                  * Set the IP time-to-live for outgoing multicast packets.
 1735                  * The original multicast API required a char argument,
 1736                  * which is inconsistent with the rest of the socket API.
 1737                  * We allow either a char or an int.
 1738                  */
 1739                 if (sopt->sopt_valsize == sizeof(u_char)) {
 1740                         error = sooptcopyin(sopt, &ttl, sizeof(u_char),
 1741                             sizeof(u_char));
 1742                         if (error)
 1743                                 break;
 1744                 } else {
 1745                         u_int ittl;
 1746 
 1747                         error = sooptcopyin(sopt, &ittl, sizeof(u_int),
 1748                             sizeof(u_int));
 1749                         if (error)
 1750                                 break;
 1751                         if (ittl > 255) {
 1752                                 error = EINVAL;
 1753                                 break;
 1754                         }
 1755                         ttl = (u_char)ittl;
 1756                 }
 1757                 imo = inp_findmoptions(inp);
 1758                 imo->imo_multicast_ttl = ttl;
 1759                 INP_WUNLOCK(inp);
 1760                 break;
 1761         }
 1762 
 1763         case IP_MULTICAST_LOOP: {
 1764                 u_char loop;
 1765 
 1766                 /*
 1767                  * Set the loopback flag for outgoing multicast packets.
 1768                  * Must be zero or one.  The original multicast API required a
 1769                  * char argument, which is inconsistent with the rest
 1770                  * of the socket API.  We allow either a char or an int.
 1771                  */
 1772                 if (sopt->sopt_valsize == sizeof(u_char)) {
 1773                         error = sooptcopyin(sopt, &loop, sizeof(u_char),
 1774                             sizeof(u_char));
 1775                         if (error)
 1776                                 break;
 1777                 } else {
 1778                         u_int iloop;
 1779 
 1780                         error = sooptcopyin(sopt, &iloop, sizeof(u_int),
 1781                                             sizeof(u_int));
 1782                         if (error)
 1783                                 break;
 1784                         loop = (u_char)iloop;
 1785                 }
 1786                 imo = inp_findmoptions(inp);
 1787                 imo->imo_multicast_loop = !!loop;
 1788                 INP_WUNLOCK(inp);
 1789                 break;
 1790         }
 1791 
 1792         case IP_ADD_MEMBERSHIP:
 1793         case IP_ADD_SOURCE_MEMBERSHIP:
 1794         case MCAST_JOIN_GROUP:
 1795         case MCAST_JOIN_SOURCE_GROUP:
 1796                 error = inp_join_group(inp, sopt);
 1797                 break;
 1798 
 1799         case IP_DROP_MEMBERSHIP:
 1800         case IP_DROP_SOURCE_MEMBERSHIP:
 1801         case MCAST_LEAVE_GROUP:
 1802         case MCAST_LEAVE_SOURCE_GROUP:
 1803                 error = inp_leave_group(inp, sopt);
 1804                 break;
 1805 
 1806         case IP_BLOCK_SOURCE:
 1807         case IP_UNBLOCK_SOURCE:
 1808         case MCAST_BLOCK_SOURCE:
 1809         case MCAST_UNBLOCK_SOURCE:
 1810                 error = inp_change_source_filter(inp, sopt);
 1811                 break;
 1812 
 1813         case IP_MSFILTER:
 1814                 error = inp_set_source_filters(inp, sopt);
 1815                 break;
 1816 
 1817         default:
 1818                 error = EOPNOTSUPP;
 1819                 break;
 1820         }
 1821 
 1822         INP_UNLOCK_ASSERT(inp);
 1823 
 1824         return (error);
 1825 }
 1826 
 1827 /*
 1828  * Following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
 1829  */
 1830 static struct ifnet *
 1831 ip_multicast_if(struct in_addr *a)
 1832 {
 1833         int ifindex;
 1834         struct ifnet *ifp;
 1835 
 1836         if (ntohl(a->s_addr) >> 24 == 0) {
 1837                 ifindex = ntohl(a->s_addr) & 0xffffff;
 1838                 if (ifindex < 0 || if_index < ifindex)
 1839                         return (NULL);
 1840                 ifp = ifnet_byindex(ifindex);
 1841         } else
 1842                 INADDR_TO_IFP(*a, ifp);
 1843         return (ifp);
 1844 }
 1845 

Cache object: feec2909b0ff4cb38aa064390369de91


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.