[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]

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

Version: -  FREEBSD  -  FREEBSD7  -  FREEBSD70  -  FREEBSD6  -  FREEBSD64  -  FREEBSD63  -  FREEBSD62  -  FREEBSD61  -  FREEBSD60  -  FREEBSD5  -  FREEBSD55  -  FREEBSD54  -  FREEBSD53  -  FREEBSD52  -  FREEBSD51  -  FREEBSD50  -  FREEBSD4  -  FREEBSD3  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  OPENSOLARIS  -  minix-3-1-1  -  TRUSTEDBSD-SEBSD  -  FREEBSD-LIBC  -  FREEBSD7-LIBC  -  FREEBSD6-LIBC  -  GLIBC27 
SearchContext: -  none  -  excerpts  -  bigexcerpts 

  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: src/sys/netinet/in_mcast.c,v 1.11 2008/12/02 21:37:28 bz Exp $");
 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 #include <sys/vimage.h>
 52 
 53 #include <net/if.h>
 54 #include <net/if_dl.h>
 55 #include <net/route.h>
 56 #include <net/vnet.h>
 57 
 58 #include <netinet/in.h>
 59 #include <netinet/in_systm.h>
 60 #include <netinet/in_pcb.h>
 61 #include <netinet/in_var.h>
 62 #include <netinet/ip_var.h>
 63 #include <netinet/igmp_var.h>
 64 #include <netinet/vinet.h>
 65 
 66 #ifndef __SOCKUNION_DECLARED
 67 union sockunion {
 68         struct sockaddr_storage ss;
 69         struct sockaddr         sa;
 70         struct sockaddr_dl      sdl;
 71         struct sockaddr_in      sin;
 72 #ifdef INET6
 73         struct sockaddr_in6     sin6;
 74 #endif
 75 };
 76 typedef union sockunion sockunion_t;
 77 #define __SOCKUNION_DECLARED
 78 #endif /* __SOCKUNION_DECLARED */
 79 
 80 static MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group");
 81 static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options");
 82 static MALLOC_DEFINE(M_IPMSOURCE, "in_msource", "IPv4 multicast source filter");
 83 
 84 /*
 85  * The IPv4 multicast list (in_multihead and associated structures) are
 86  * protected by the global in_multi_mtx.  See in_var.h for more details.  For
 87  * now, in_multi_mtx is marked as recursible due to IGMP's calling back into
 88  * ip_output() to send IGMP packets while holding the lock; this probably is
 89  * not quite desirable.
 90  */
 91 #ifdef VIMAGE_GLOBALS
 92 struct in_multihead in_multihead;       /* XXX BSS initialization */
 93 #endif
 94 struct mtx in_multi_mtx;
 95 MTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF | MTX_RECURSE);
 96 
 97 /*
 98  * Functions with non-static linkage defined in this file should be
 99  * declared in in_var.h:
100  *  imo_match_group()
101  *  imo_match_source()
102  *  in_addmulti()
103  *  in_delmulti()
104  *  in_delmulti_locked()
105  * and ip_var.h:
106  *  inp_freemoptions()
107  *  inp_getmoptions()
108  *  inp_setmoptions()
109  */
110 static int      imo_grow(struct ip_moptions *);
111 static int      imo_join_source(struct ip_moptions *, size_t, sockunion_t *);
112 static int      imo_leave_source(struct ip_moptions *, size_t, sockunion_t *);
113 static int      inp_change_source_filter(struct inpcb *, struct sockopt *);
114 static struct ip_moptions *
115                 inp_findmoptions(struct inpcb *);
116 static int      inp_get_source_filters(struct inpcb *, struct sockopt *);
117 static int      inp_join_group(struct inpcb *, struct sockopt *);
118 static int      inp_leave_group(struct inpcb *, struct sockopt *);
119 static int      inp_set_multicast_if(struct inpcb *, struct sockopt *);
120 static int      inp_set_source_filters(struct inpcb *, struct sockopt *);
121 
122 /*
123  * Resize the ip_moptions vector to the next power-of-two minus 1.
124  * May be called with locks held; do not sleep.
125  */
126 static int
127 imo_grow(struct ip_moptions *imo)
128 {
129         struct in_multi         **nmships;
130         struct in_multi         **omships;
131         struct in_mfilter        *nmfilters;
132         struct in_mfilter        *omfilters;
133         size_t                    idx;
134         size_t                    newmax;
135         size_t                    oldmax;
136 
137         nmships = NULL;
138         nmfilters = NULL;
139         omships = imo->imo_membership;
140         omfilters = imo->imo_mfilters;
141         oldmax = imo->imo_max_memberships;
142         newmax = ((oldmax + 1) * 2) - 1;
143 
144         if (newmax <= IP_MAX_MEMBERSHIPS) {
145                 nmships = (struct in_multi **)realloc(omships,
146                     sizeof(struct in_multi *) * newmax, M_IPMOPTS, M_NOWAIT);
147                 nmfilters = (struct in_mfilter *)realloc(omfilters,
148                     sizeof(struct in_mfilter) * newmax, M_IPMSOURCE, M_NOWAIT);
149                 if (nmships != NULL && nmfilters != NULL) {
150                         /* Initialize newly allocated source filter heads. */
151                         for (idx = oldmax; idx < newmax; idx++) {
152                                 nmfilters[idx].imf_fmode = MCAST_EXCLUDE;
153                                 nmfilters[idx].imf_nsources = 0;
154                                 TAILQ_INIT(&nmfilters[idx].imf_sources);
155                         }
156                         imo->imo_max_memberships = newmax;
157                         imo->imo_membership = nmships;
158                         imo->imo_mfilters = nmfilters;
159                 }
160         }
161 
162         if (nmships == NULL || nmfilters == NULL) {
163                 if (nmships != NULL)
164                         free(nmships, M_IPMOPTS);
165                 if (nmfilters != NULL)
166                         free(nmfilters, M_IPMSOURCE);
167                 return (ETOOMANYREFS);
168         }
169 
170         return (0);
171 }
172 
173 /*
174  * Add a source to a multicast filter list.
175  * Assumes the associated inpcb is locked.
176  */
177 static int
178 imo_join_source(struct ip_moptions *imo, size_t gidx, sockunion_t *src)
179 {
180         struct in_msource       *ims, *nims;
181         struct in_mfilter       *imf;
182 
183         KASSERT(src->ss.ss_family == AF_INET, ("%s: !AF_INET", __func__));
184         KASSERT(imo->imo_mfilters != NULL,
185             ("%s: imo_mfilters vector not allocated", __func__));
186 
187         imf = &imo->imo_mfilters[gidx];
188         if (imf->imf_nsources == IP_MAX_SOURCE_FILTER)
189                 return (ENOBUFS);
190 
191         ims = imo_match_source(imo, gidx, &src->sa);
192         if (ims != NULL)
193                 return (EADDRNOTAVAIL);
194 
195         /* Do not sleep with inp lock held. */
196         nims = malloc(sizeof(struct in_msource),
197             M_IPMSOURCE, M_NOWAIT | M_ZERO);
198         if (nims == NULL)
199                 return (ENOBUFS);
200 
201         nims->ims_addr = src->ss;
202         TAILQ_INSERT_TAIL(&imf->imf_sources, nims, ims_next);
203         imf->imf_nsources++;
204 
205         return (0);
206 }
207 
208 static int
209 imo_leave_source(struct ip_moptions *imo, size_t gidx, sockunion_t *src)
210 {
211         struct in_msource       *ims;
212         struct in_mfilter       *imf;
213 
214         KASSERT(src->ss.ss_family == AF_INET, ("%s: !AF_INET", __func__));
215         KASSERT(imo->imo_mfilters != NULL,
216             ("%s: imo_mfilters vector not allocated", __func__));
217 
218         imf = &imo->imo_mfilters[gidx];
219         if (imf->imf_nsources == IP_MAX_SOURCE_FILTER)
220                 return (ENOBUFS);
221 
222         ims = imo_match_source(imo, gidx, &src->sa);
223         if (ims == NULL)
224                 return (EADDRNOTAVAIL);
225 
226         TAILQ_REMOVE(&imf->imf_sources, ims, ims_next);
227         free(ims, M_IPMSOURCE);
228         imf->imf_nsources--;
229 
230         return (0);
231 }
232 
233 /*
234  * Find an IPv4 multicast group entry for this ip_moptions instance
235  * which matches the specified group, and optionally an interface.
236  * Return its index into the array, or -1 if not found.
237  */
238 size_t
239 imo_match_group(struct ip_moptions *imo, struct ifnet *ifp,
240     struct sockaddr *group)
241 {
242         sockunion_t      *gsa;
243         struct in_multi **pinm;
244         int               idx;
245         int               nmships;
246 
247         gsa = (sockunion_t *)group;
248 
249         /* The imo_membership array may be lazy allocated. */
250         if (imo->imo_membership == NULL || imo->imo_num_memberships == 0)
251                 return (-1);
252 
253         nmships = imo->imo_num_memberships;
254         pinm = &imo->imo_membership[0];
255         for (idx = 0; idx < nmships; idx++, pinm++) {
256                 if (*pinm == NULL)
257                         continue;
258 #if 0
259                 printf("%s: trying ifp = %p, inaddr = %s ", __func__,
260                     ifp, inet_ntoa(gsa->sin.sin_addr));
261                 printf("against %p, %s\n",
262                     (*pinm)->inm_ifp, inet_ntoa((*pinm)->inm_addr));
263 #endif
264                 if ((ifp == NULL || ((*pinm)->inm_ifp == ifp)) &&
265                     (*pinm)->inm_addr.s_addr == gsa->sin.sin_addr.s_addr) {
266                         break;
267                 }
268         }
269         if (idx >= nmships)
270                 idx = -1;
271 
272         return (idx);
273 }
274 
275 /*
276  * Find a multicast source entry for this imo which matches
277  * the given group index for this socket, and source address.
278  */
279 struct in_msource *
280 imo_match_source(struct ip_moptions *imo, size_t gidx, struct sockaddr *src)
281 {
282         struct in_mfilter       *imf;
283         struct in_msource       *ims, *pims;
284 
285         KASSERT(src->sa_family == AF_INET, ("%s: !AF_INET", __func__));
286         KASSERT(gidx != -1 && gidx < imo->imo_num_memberships,
287             ("%s: invalid index %d\n", __func__, (int)gidx));
288 
289         /* The imo_mfilters array may be lazy allocated. */
290         if (imo->imo_mfilters == NULL)
291                 return (NULL);
292 
293         pims = NULL;
294         imf = &imo->imo_mfilters[gidx];
295         TAILQ_FOREACH(ims, &imf->imf_sources, ims_next) {
296                 /*
297                  * Perform bitwise comparison of two IPv4 addresses.
298                  * TODO: Do the same for IPv6.
299                  * Do not use sa_equal() for this as it is not aware of
300                  * deeper structure in sockaddr_in or sockaddr_in6.
301                  */
302                 if (((struct sockaddr_in *)&ims->ims_addr)->sin_addr.s_addr ==
303                     ((struct sockaddr_in *)src)->sin_addr.s_addr) {
304                         pims = ims;
305                         break;
306                 }
307         }
308 
309         return (pims);
310 }
311 
312 /*
313  * Join an IPv4 multicast group.
314  */
315 struct in_multi *
316 in_addmulti(struct in_addr *ap, struct ifnet *ifp)
317 {
318         INIT_VNET_INET(ifp->if_vnet);
319         struct in_multi *inm;
320 
321         inm = NULL;
322 
323         IFF_LOCKGIANT(ifp);
324         IN_MULTI_LOCK();
325 
326         IN_LOOKUP_MULTI(*ap, ifp, inm);
327         if (inm != NULL) {
328                 /*
329                  * If we already joined this group, just bump the
330                  * refcount and return it.
331                  */
332                 KASSERT(inm->inm_refcount >= 1,
333                     ("%s: bad refcount %d", __func__, inm->inm_refcount));
334                 ++inm->inm_refcount;
335         } else do {
336                 sockunion_t              gsa;
337                 struct ifmultiaddr      *ifma;
338                 struct in_multi         *ninm;
339                 int                      error;
340 
341                 memset(&gsa, 0, sizeof(gsa));
342                 gsa.sin.sin_family = AF_INET;
343                 gsa.sin.sin_len = sizeof(struct sockaddr_in);
344                 gsa.sin.sin_addr = *ap;
345 
346                 /*
347                  * Check if a link-layer group is already associated
348                  * with this network-layer group on the given ifnet.
349                  * If so, bump the refcount on the existing network-layer
350                  * group association and return it.
351                  */
352                 error = if_addmulti(ifp, &gsa.sa, &ifma);
353                 if (error)
354                         break;
355                 if (ifma->ifma_protospec != NULL) {
356                         inm = (struct in_multi *)ifma->ifma_protospec;
357 #ifdef INVARIANTS
358                         if (inm->inm_ifma != ifma || inm->inm_ifp != ifp ||
359                             inm->inm_addr.s_addr != ap->s_addr)
360                                 panic("%s: ifma is inconsistent", __func__);
361 #endif
362                         ++inm->inm_refcount;
363                         break;
364                 }
365 
366                 /*
367                  * A new membership is needed; construct it and
368                  * perform the IGMP join.
369                  */
370                 ninm = malloc(sizeof(*ninm), M_IPMADDR, M_NOWAIT | M_ZERO);
371                 if (ninm == NULL) {
372                         if_delmulti_ifma(ifma);
373                         break;
374                 }
375                 ninm->inm_addr = *ap;
376                 ninm->inm_ifp = ifp;
377                 ninm->inm_ifma = ifma;
378                 ninm->inm_refcount = 1;
379                 ifma->ifma_protospec = ninm;
380                 LIST_INSERT_HEAD(&V_in_multihead, ninm, inm_link);
381 
382                 igmp_joingroup(ninm);
383 
384                 inm = ninm;
385         } while (0);
386 
387         IN_MULTI_UNLOCK();
388         IFF_UNLOCKGIANT(ifp);
389 
390         return (inm);
391 }
392 
393 /*
394  * Leave an IPv4 multicast group.
395  * It is OK to call this routine if the underlying ifnet went away.
396  *
397  * XXX: To deal with the ifp going away, we cheat; the link-layer code in net
398  * will set ifma_ifp to NULL when the associated ifnet instance is detached
399  * from the system.
400  *
401  * The only reason we need to violate layers and check ifma_ifp here at all
402  * is because certain hardware drivers still require Giant to be held,
403  * and it must always be taken before other locks.
404  */
405 void
406 in_delmulti(struct in_multi *inm)
407 {
408         struct ifnet *ifp;
409 
410         KASSERT(inm != NULL, ("%s: inm is NULL", __func__));
411         KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__));
412         ifp = inm->inm_ifma->ifma_ifp;
413 
414         if (ifp != NULL) {
415                 /*
416                  * Sanity check that netinet's notion of ifp is the
417                  * same as net's.
418                  */
419                 KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__));
420                 IFF_LOCKGIANT(ifp);
421         }
422 
423         IN_MULTI_LOCK();
424         in_delmulti_locked(inm);
425         IN_MULTI_UNLOCK();
426 
427         if (ifp != NULL)
428                 IFF_UNLOCKGIANT(ifp);
429 }
430 
431 /*
432  * Delete a multicast address record, with locks held.
433  *
434  * It is OK to call this routine if the ifp went away.
435  * Assumes that caller holds the IN_MULTI lock, and that
436  * Giant was taken before other locks if required by the hardware.
437  */
438 void
439 in_delmulti_locked(struct in_multi *inm)
440 {
441         struct ifmultiaddr *ifma;
442 
443         IN_MULTI_LOCK_ASSERT();
444         KASSERT(inm->inm_refcount >= 1, ("%s: freeing freed inm", __func__));
445 
446         if (--inm->inm_refcount == 0) {
447                 igmp_leavegroup(inm);
448 
449                 ifma = inm->inm_ifma;
450 #ifdef DIAGNOSTIC
451                 if (bootverbose)
452                         printf("%s: purging ifma %p\n", __func__, ifma);
453 #endif
454                 KASSERT(ifma->ifma_protospec == inm,
455                     ("%s: ifma_protospec != inm", __func__));
456                 ifma->ifma_protospec = NULL;
457 
458                 LIST_REMOVE(inm, inm_link);
459                 free(inm, M_IPMADDR);
460 
461                 if_delmulti_ifma(ifma);
462         }
463 }
464 
465 /*
466  * Block or unblock an ASM/SSM multicast source on an inpcb.
467  */
468 static int
469 inp_change_source_filter(struct inpcb *inp, struct sockopt *sopt)
470 {
471         INIT_VNET_NET(curvnet);
472         INIT_VNET_INET(curvnet);
473         struct group_source_req          gsr;
474         sockunion_t                     *gsa, *ssa;
475         struct ifnet                    *ifp;
476         struct in_mfilter               *imf;
477         struct ip_moptions              *imo;
478         struct in_msource               *ims;
479         size_t                           idx;
480         int                              error;
481         int                              block;
482 
483         ifp = NULL;
484         error = 0;
485         block = 0;
486 
487         memset(&gsr, 0, sizeof(struct group_source_req));
488         gsa = (sockunion_t *)&gsr.gsr_group;
489         ssa = (sockunion_t *)&gsr.gsr_source;
490 
491         switch (sopt->sopt_name) {
492         case IP_BLOCK_SOURCE:
493         case IP_UNBLOCK_SOURCE: {
494                 struct ip_mreq_source    mreqs;
495 
496                 error = sooptcopyin(sopt, &mreqs,
497                     sizeof(struct ip_mreq_source),
498                     sizeof(struct ip_mreq_source));
499                 if (error)
500                         return (error);
501 
502                 gsa->sin.sin_family = AF_INET;
503                 gsa->sin.sin_len = sizeof(struct sockaddr_in);
504                 gsa->sin.sin_addr = mreqs.imr_multiaddr;
505 
506                 ssa->sin.sin_family = AF_INET;
507                 ssa->sin.sin_len = sizeof(struct sockaddr_in);
508                 ssa->sin.sin_addr = mreqs.imr_sourceaddr;
509 
510                 if (mreqs.imr_interface.s_addr != INADDR_ANY)
511                         INADDR_TO_IFP(mreqs.imr_interface, ifp);
512 
513                 if (sopt->sopt_name == IP_BLOCK_SOURCE)
514                         block = 1;
515 
516 #ifdef DIAGNOSTIC
517                 if (bootverbose) {
518                         printf("%s: imr_interface = %s, ifp = %p\n",
519                             __func__, inet_ntoa(mreqs.imr_interface), ifp);
520                 }
521 #endif
522                 break;
523             }
524 
525         case MCAST_BLOCK_SOURCE:
526         case MCAST_UNBLOCK_SOURCE:
527                 error = sooptcopyin(sopt, &gsr,
528                     sizeof(struct group_source_req),
529                     sizeof(struct group_source_req));
530                 if (error)
531                         return (error);
532 
533                 if (gsa->sin.sin_family != AF_INET ||
534                     gsa->sin.sin_len != sizeof(struct sockaddr_in))
535                         return (EINVAL);
536 
537                 if (ssa->sin.sin_family != AF_INET ||
538                     ssa->sin.sin_len != sizeof(struct sockaddr_in))
539                         return (EINVAL);
540 
541                 if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
542                         return (EADDRNOTAVAIL);
543 
544                 ifp = ifnet_byindex(gsr.gsr_interface);
545 
546                 if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
547                         block = 1;
548                 break;
549 
550         default:
551 #ifdef DIAGNOSTIC
552                 if (bootverbose) {
553                         printf("%s: unknown sopt_name %d\n", __func__,
554                             sopt->sopt_name);
555                 }
556 #endif
557                 return (EOPNOTSUPP);
558                 break;
559         }
560 
561         /* XXX INET6 */
562         if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
563                 return (EINVAL);
564 
565         /*
566          * Check if we are actually a member of this group.
567          */
568         imo = inp_findmoptions(inp);
569         idx = imo_match_group(imo, ifp, &gsa->sa);
570         if (idx == -1 || imo->imo_mfilters == NULL) {
571                 error = EADDRNOTAVAIL;
572                 goto out_locked;
573         }
574 
575         KASSERT(imo->imo_mfilters != NULL,
576             ("%s: imo_mfilters not allocated", __func__));
577         imf = &imo->imo_mfilters[idx];
578 
579         /*
580          * SSM multicast truth table for block/unblock operations.
581          *
582          * Operation   Filter Mode  Entry exists?   Action
583          *
584          * block       exclude      no              add source to filter
585          * unblock     include      no              add source to filter
586          * block       include      no              EINVAL
587          * unblock     exclude      no              EINVAL
588          * block       exclude      yes             EADDRNOTAVAIL
589          * unblock     include      yes             EADDRNOTAVAIL
590          * block       include      yes             remove source from filter
591          * unblock     exclude      yes             remove source from filter
592          *
593          * FreeBSD does not explicitly distinguish between ASM and SSM
594          * mode sockets; all sockets are assumed to have a filter list.
595          */
596 #ifdef DIAGNOSTIC
597         if (bootverbose) {
598                 printf("%s: imf_fmode is %s\n", __func__,
599                     imf->imf_fmode == MCAST_INCLUDE ? "include" : "exclude");
600         }
601 #endif
602         ims = imo_match_source(imo, idx, &ssa->sa);
603         if (ims == NULL) {
604                 if ((block == 1 && imf->imf_fmode == MCAST_EXCLUDE) ||
605                     (block == 0 && imf->imf_fmode == MCAST_INCLUDE)) {
606 #ifdef DIAGNOSTIC
607                         if (bootverbose) {
608                                 printf("%s: adding %s to filter list\n",
609                                     __func__, inet_ntoa(ssa->sin.sin_addr));
610                         }
611 #endif
612                         error = imo_join_source(imo, idx, ssa);
613                 }
614                 if ((block == 1 && imf->imf_fmode == MCAST_INCLUDE) ||
615                     (block == 0 && imf->imf_fmode == MCAST_EXCLUDE)) {
616                         /*
617                          * If the socket is in inclusive mode:
618                          *  the source is already blocked as it has no entry.
619                          * If the socket is in exclusive mode:
620                          *  the source is already unblocked as it has no entry.
621                          */
622 #ifdef DIAGNOSTIC
623                         if (bootverbose) {
624                                 printf("%s: ims %p; %s already [un]blocked\n",
625                                     __func__, ims,
626                                     inet_ntoa(ssa->sin.sin_addr));
627                         }
628 #endif
629                         error = EINVAL;
630                 }
631         } else {
632                 if ((block == 1 && imf->imf_fmode == MCAST_EXCLUDE) ||
633                     (block == 0 && imf->imf_fmode == MCAST_INCLUDE)) {
634                         /*
635                          * If the socket is in exclusive mode:
636                          *  the source is already blocked as it has an entry.
637                          * If the socket is in inclusive mode:
638                          *  the source is already unblocked as it has an entry.
639                          */
640 #ifdef DIAGNOSTIC
641                         if (bootverbose) {
642                                 printf("%s: ims %p; %s already [un]blocked\n",
643                                     __func__, ims,
644                                     inet_ntoa(ssa->sin.sin_addr));
645                         }
646 #endif
647                         error = EADDRNOTAVAIL;
648                 }
649                 if ((block == 1 && imf->imf_fmode == MCAST_INCLUDE) ||
650                     (block == 0 && imf->imf_fmode == MCAST_EXCLUDE)) {
651 #ifdef DIAGNOSTIC
652                         if (bootverbose) {
653                                 printf("%s: removing %s from filter list\n",
654                                     __func__, inet_ntoa(ssa->sin.sin_addr));
655                         }
656 #endif
657                         error = imo_leave_source(imo, idx, ssa);
658                 }
659         }
660 
661 out_locked:
662         INP_WUNLOCK(inp);
663         return (error);
664 }
665 
666 /*
667  * Given an inpcb, return its multicast options structure pointer.  Accepts
668  * an unlocked inpcb pointer, but will return it locked.  May sleep.
669  */
670 static struct ip_moptions *
671 inp_findmoptions(struct inpcb *inp)
672 {
673         struct ip_moptions       *imo;
674         struct in_multi         **immp;
675         struct in_mfilter        *imfp;
676         size_t                    idx;
677 
678         INP_WLOCK(inp);
679         if (inp->inp_moptions != NULL)
680                 return (inp->inp_moptions);
681 
682         INP_WUNLOCK(inp);
683 
684         imo = (struct ip_moptions *)malloc(sizeof(*imo), M_IPMOPTS,
685             M_WAITOK);
686         immp = (struct in_multi **)malloc(sizeof(*immp) * IP_MIN_MEMBERSHIPS,
687             M_IPMOPTS, M_WAITOK | M_ZERO);
688         imfp = (struct in_mfilter *)malloc(
689             sizeof(struct in_mfilter) * IP_MIN_MEMBERSHIPS,
690             M_IPMSOURCE, M_WAITOK);
691 
692         imo->imo_multicast_ifp = NULL;
693         imo->imo_multicast_addr.s_addr = INADDR_ANY;
694         imo->imo_multicast_vif = -1;
695         imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
696         imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
697         imo->imo_num_memberships = 0;
698         imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
699         imo->imo_membership = immp;
700 
701         /* Initialize per-group source filters. */
702         for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) {
703                 imfp[idx].imf_fmode = MCAST_EXCLUDE;
704                 imfp[idx].imf_nsources = 0;
705                 TAILQ_INIT(&imfp[idx].imf_sources);
706         }
707         imo->imo_mfilters = imfp;
708 
709         INP_WLOCK(inp);
710         if (inp->inp_moptions != NULL) {
711                 free(imfp, M_IPMSOURCE);
712                 free(immp, M_IPMOPTS);
713                 free(imo, M_IPMOPTS);
714                 return (inp->inp_moptions);
715         }
716         inp->inp_moptions = imo;
717         return (imo);
718 }
719 
720 /*
721  * Discard the IP multicast options (and source filters).
722  */
723 void
724 inp_freemoptions(struct ip_moptions *imo)
725 {
726         struct in_mfilter       *imf;
727         struct in_msource       *ims, *tims;
728         size_t                   idx, nmships;
729 
730         KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__));
731 
732         nmships = imo->imo_num_memberships;
733         for (idx = 0; idx < nmships; ++idx) {
734                 in_delmulti(imo->imo_membership[idx]);
735 
736                 if (imo->imo_mfilters != NULL) {
737                         imf = &imo->imo_mfilters[idx];
738                         TAILQ_FOREACH_SAFE(ims, &imf->imf_sources,
739                             ims_next, tims) {
740                                 TAILQ_REMOVE(&imf->imf_sources, ims, ims_next);
741                                 free(ims, M_IPMSOURCE);
742                                 imf->imf_nsources--;
743                         }
744                         KASSERT(imf->imf_nsources == 0,
745                             ("%s: did not free all imf_nsources", __func__));
746                 }
747         }
748 
749         if (imo->imo_mfilters != NULL)
750                 free(imo->imo_mfilters, M_IPMSOURCE);
751         free(imo->imo_membership, M_IPMOPTS);
752         free(imo, M_IPMOPTS);
753 }
754 
755 /*
756  * Atomically get source filters on a socket for an IPv4 multicast group.
757  * Called with INP lock held; returns with lock released.
758  */
759 static int
760 inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
761 {
762         INIT_VNET_NET(curvnet);
763         struct __msfilterreq     msfr;
764         sockunion_t             *gsa;
765         struct ifnet            *ifp;
766         struct ip_moptions      *imo;
767         struct in_mfilter       *imf;
768         struct in_msource       *ims;
769         struct sockaddr_storage *ptss;
770         struct sockaddr_storage *tss;
771         int                      error;
772         size_t                   idx;
773 
774         INP_WLOCK_ASSERT(inp);
775 
776         imo = inp->inp_moptions;
777         KASSERT(imo != NULL, ("%s: null ip_moptions", __func__));
778 
779         INP_WUNLOCK(inp);
780 
781         error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
782             sizeof(struct __msfilterreq));
783         if (error)
784                 return (error);
785 
786         if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
787                 return (EINVAL);
788 
789         ifp = ifnet_byindex(msfr.msfr_ifindex);
790         if (ifp == NULL)
791                 return (EINVAL);
792 
793         INP_WLOCK(inp);
794 
795         /*
796          * Lookup group on the socket.
797          */
798         gsa = (sockunion_t *)&msfr.msfr_group;
799         idx = imo_match_group(imo, ifp, &gsa->sa);
800         if (idx == -1 || imo->imo_mfilters == NULL) {
801                 INP_WUNLOCK(inp);
802                 return (EADDRNOTAVAIL);
803         }
804 
805         imf = &imo->imo_mfilters[idx];
806         msfr.msfr_fmode = imf->imf_fmode;
807         msfr.msfr_nsrcs = imf->imf_nsources;
808 
809         /*
810          * If the user specified a buffer, copy out the source filter
811          * entries to userland gracefully.
812          * msfr.msfr_nsrcs is always set to the total number of filter
813          * entries which the kernel currently has for this group.
814          */
815         tss = NULL;
816         if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) {
817                 /*
818                  * Make a copy of the source vector so that we do not
819                  * thrash the inpcb lock whilst copying it out.
820                  * We only copy out the number of entries which userland
821                  * has asked for, but we always tell userland how big the
822                  * buffer really needs to be.
823                  */
824                 tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
825                     M_TEMP, M_NOWAIT);
826                 if (tss == NULL) {
827                         error = ENOBUFS;
828                 } else {
829                         ptss = tss;
830                         TAILQ_FOREACH(ims, &imf->imf_sources, ims_next) {
831                                 memcpy(ptss++, &ims->ims_addr,
832                                     sizeof(struct sockaddr_storage));
833                         }
834                 }
835         }
836 
837         INP_WUNLOCK(inp);
838 
839         if (tss != NULL) {
840                 error = copyout(tss, msfr.msfr_srcs,
841                     sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
842                 free(tss, M_TEMP);
843         }
844 
845         if (error)
846                 return (error);
847 
848         error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq));
849 
850         return (error);
851 }
852 
853 /*
854  * Return the IP multicast options in response to user getsockopt().
855  */
856 int
857 inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
858 {
859         INIT_VNET_INET(curvnet);
860         struct ip_mreqn          mreqn;
861         struct ip_moptions      *imo;
862         struct ifnet            *ifp;
863         struct in_ifaddr        *ia;
864         int                      error, optval;
865         u_char                   coptval;
866 
867         INP_WLOCK(inp);
868         imo = inp->inp_moptions;
869         /*
870          * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
871          * or is a divert socket, reject it.
872          */
873         if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
874             (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
875             inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) {
876                 INP_WUNLOCK(inp);
877                 return (EOPNOTSUPP);
878         }
879 
880         error = 0;
881         switch (sopt->sopt_name) {
882         case IP_MULTICAST_VIF:
883                 if (imo != NULL)
884                         optval = imo->imo_multicast_vif;
885                 else
886                         optval = -1;
887                 INP_WUNLOCK(inp);
888                 error = sooptcopyout(sopt, &optval, sizeof(int));
889                 break;
890 
891         case IP_MULTICAST_IF:
892                 memset(&mreqn, 0, sizeof(struct ip_mreqn));
893                 if (imo != NULL) {
894                         ifp = imo->imo_multicast_ifp;
895                         if (imo->imo_multicast_addr.s_addr != INADDR_ANY) {
896                                 mreqn.imr_address = imo->imo_multicast_addr;
897                         } else if (ifp != NULL) {
898                                 mreqn.imr_ifindex = ifp->if_index;
899                                 IFP_TO_IA(ifp, ia);
900                                 if (ia != NULL) {
901                                         mreqn.imr_address =
902                                             IA_SIN(ia)->sin_addr;
903                                 }
904                         }
905                 }
906                 INP_WUNLOCK(inp);
907                 if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
908                         error = sooptcopyout(sopt, &mreqn,
909                             sizeof(struct ip_mreqn));
910                 } else {
911                         error = sooptcopyout(sopt, &mreqn.imr_address,
912                             sizeof(struct in_addr));
913                 }
914                 break;
915 
916         case IP_MULTICAST_TTL:
917                 if (imo == 0)
918                         optval = coptval = IP_DEFAULT_MULTICAST_TTL;
919                 else
920                         optval = coptval = imo->imo_multicast_ttl;
921                 INP_WUNLOCK(inp);
922                 if (sopt->sopt_valsize == sizeof(u_char))
923                         error = sooptcopyout(sopt, &coptval, sizeof(u_char));
924                 else
925                         error = sooptcopyout(sopt, &optval, sizeof(int));
926                 break;
927 
928         case IP_MULTICAST_LOOP:
929                 if (imo == 0)
930                         optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
931                 else
932                         optval = coptval = imo->imo_multicast_loop;
933                 INP_WUNLOCK(inp);
934                 if (sopt->sopt_valsize == sizeof(u_char))
935                         error = sooptcopyout(sopt, &coptval, sizeof(u_char));
936                 else
937                         error = sooptcopyout(sopt, &optval, sizeof(int));
938                 break;
939 
940         case IP_MSFILTER:
941                 if (imo == NULL) {
942                         error = EADDRNOTAVAIL;
943                         INP_WUNLOCK(inp);
944                 } else {
945                         error = inp_get_source_filters(inp, sopt);
946                 }
947                 break;
948 
949         default:
950                 INP_WUNLOCK(inp);
951                 error = ENOPROTOOPT;
952                 break;
953         }
954 
955         INP_UNLOCK_ASSERT(inp);
956 
957         return (error);
958 }
959 
960 /*
961  * Join an IPv4 multicast group, possibly with a source.
962  */
963 static int
964 inp_join_group(struct inpcb *inp, struct sockopt *sopt)
965 {
966         INIT_VNET_NET(curvnet);
967         INIT_VNET_INET(curvnet);
968         struct group_source_req          gsr;
969         sockunion_t                     *gsa, *ssa;
970         struct ifnet                    *ifp;
971         struct in_mfilter               *imf;
972         struct ip_moptions              *imo;
973         struct in_multi                 *inm;
974         size_t                           idx;
975         int                              error;
976 
977         ifp = NULL;
978         error = 0;
979 
980         memset(&gsr, 0, sizeof(struct group_source_req));
981         gsa = (sockunion_t *)&gsr.gsr_group;
982         gsa->ss.ss_family = AF_UNSPEC;
983         ssa = (sockunion_t *)&gsr.gsr_source;
984         ssa->ss.ss_family = AF_UNSPEC;
985 
986         switch (sopt->sopt_name) {
987         case IP_ADD_MEMBERSHIP:
988         case IP_ADD_SOURCE_MEMBERSHIP: {
989                 struct ip_mreq_source    mreqs;
990 
991                 if (sopt->sopt_name == IP_ADD_MEMBERSHIP) {
992                         error = sooptcopyin(sopt, &mreqs,
993                             sizeof(struct ip_mreq),
994                             sizeof(struct ip_mreq));
995                         /*
996                          * Do argument switcharoo from ip_mreq into
997                          * ip_mreq_source to avoid using two instances.
998                          */
999                         mreqs.imr_interface = mreqs.imr_sourceaddr;
1000                         mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
1001                 } else if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
1002                         error = sooptcopyin(sopt, &mreqs,
1003                             sizeof(struct ip_mreq_source),
1004                             sizeof(struct ip_mreq_source));
1005                 }
1006                 if (error)
1007                         return (error);
1008 
1009                 gsa->sin.sin_family = AF_INET;
1010                 gsa->sin.sin_len = sizeof(struct sockaddr_in);
1011                 gsa->sin.sin_addr = mreqs.imr_multiaddr;
1012 
1013                 if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
1014                         ssa->sin.sin_family = AF_INET;
1015                         ssa->sin.sin_len = sizeof(struct sockaddr_in);
1016                         ssa->sin.sin_addr = mreqs.imr_sourceaddr;
1017                 }
1018 
1019                 /*
1020                  * Obtain ifp. If no interface address was provided,
1021                  * use the interface of the route in the unicast FIB for
1022                  * the given multicast destination; usually, this is the
1023                  * default route.
1024                  * If this lookup fails, attempt to use the first non-loopback
1025                  * interface with multicast capability in the system as a
1026                  * last resort. The legacy IPv4 ASM API requires that we do
1027                  * this in order to allow groups to be joined when the routing
1028                  * table has not yet been populated during boot.
1029                  * If all of these conditions fail, return