xref: /xnu-8796.101.5/bsd/netinet/in_mcast.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 /*
2  * Copyright (c) 2010-2022 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /*-
29  * Copyright (c) 2007-2009 Bruce Simpson.
30  * Copyright (c) 2005 Robert N. M. Watson.
31  * All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  * 1. Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  * 2. Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in the
40  *    documentation and/or other materials provided with the distribution.
41  * 3. The name of the author may not be used to endorse or promote
42  *    products derived from this software without specific prior written
43  *    permission.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 /*
59  * IPv4 multicast socket, group, and socket option processing module.
60  */
61 
62 #include <sys/cdefs.h>
63 
64 #include <sys/param.h>
65 #include <sys/systm.h>
66 #include <sys/kernel.h>
67 #include <sys/malloc.h>
68 #include <sys/mbuf.h>
69 #include <sys/protosw.h>
70 #include <sys/socket.h>
71 #include <sys/socketvar.h>
72 #include <sys/protosw.h>
73 #include <sys/sysctl.h>
74 #include <sys/tree.h>
75 #include <sys/mcache.h>
76 
77 #include <kern/zalloc.h>
78 
79 #include <pexpert/pexpert.h>
80 
81 #include <net/if.h>
82 #include <net/if_dl.h>
83 #include <net/net_api_stats.h>
84 #include <net/route.h>
85 
86 #include <netinet/in.h>
87 #include <netinet/in_systm.h>
88 #include <netinet/in_pcb.h>
89 #include <netinet/in_var.h>
90 #include <netinet/ip_var.h>
91 #include <netinet/igmp_var.h>
92 
93 /*
94  * Functions with non-static linkage defined in this file should be
95  * declared in in_var.h:
96  *  imo_multi_filter()
97  *  in_addmulti()
98  *  in_delmulti()
99  *  in_joingroup()
100  *  in_leavegroup()
101  * and ip_var.h:
102  *  inp_freemoptions()
103  *  inp_getmoptions()
104  *  inp_setmoptions()
105  *
106  * XXX: Both carp and pf need to use the legacy (*,G) KPIs in_addmulti()
107  * and in_delmulti().
108  */
109 static void     imf_commit(struct in_mfilter *);
110 static int      imf_get_source(struct in_mfilter *imf,
111     const struct sockaddr_in *psin,
112     struct in_msource **);
113 static struct in_msource *
114 imf_graft(struct in_mfilter *, const uint8_t,
115     const struct sockaddr_in *);
116 static int      imf_prune(struct in_mfilter *, const struct sockaddr_in *);
117 static void     imf_rollback(struct in_mfilter *);
118 static void     imf_reap(struct in_mfilter *);
119 static int      imo_grow(struct ip_moptions *, uint16_t);
120 static size_t   imo_match_group(const struct ip_moptions *,
121     const struct ifnet *, const struct sockaddr_in *);
122 static struct in_msource *
123 imo_match_source(const struct ip_moptions *, const size_t,
124     const struct sockaddr_in *);
125 static void     ims_merge(struct ip_msource *ims,
126     const struct in_msource *lims, const int rollback);
127 static int      in_getmulti(struct ifnet *, const struct in_addr *,
128     struct in_multi **);
129 static int      in_joingroup(struct ifnet *, const struct in_addr *,
130     struct in_mfilter *, struct in_multi **);
131 static int      inm_get_source(struct in_multi *inm, const in_addr_t haddr,
132     const int noalloc, struct ip_msource **pims);
133 static int      inm_is_ifp_detached(const struct in_multi *);
134 static int      inm_merge(struct in_multi *, /*const*/ struct in_mfilter *);
135 static void     inm_reap(struct in_multi *);
136 static struct ip_moptions *
137 inp_findmoptions(struct inpcb *);
138 static int      inp_get_source_filters(struct inpcb *, struct sockopt *);
139 static struct ifnet *
140 inp_lookup_mcast_ifp(const struct inpcb *,
141     const struct sockaddr_in *, const struct in_addr);
142 static int      inp_block_unblock_source(struct inpcb *, struct sockopt *);
143 static int      inp_set_multicast_if(struct inpcb *, struct sockopt *);
144 static int      inp_set_source_filters(struct inpcb *, struct sockopt *);
145 static int      sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS;
146 static struct ifnet * ip_multicast_if(struct in_addr *, unsigned int *);
147 static __inline__ int ip_msource_cmp(const struct ip_msource *,
148     const struct ip_msource *);
149 
150 SYSCTL_NODE(_net_inet_ip, OID_AUTO, mcast, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "IPv4 multicast");
151 
152 static u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER;
153 SYSCTL_LONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc,
154     CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_maxgrpsrc, "Max source filters per group");
155 
156 static u_int in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER;
157 SYSCTL_UINT(_net_inet_ip_mcast, OID_AUTO, maxsocksrc,
158     CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_maxsocksrc, IP_MAX_SOCK_SRC_FILTER,
159     "Max source filters per socket");
160 
161 int in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP;
162 SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_LOCKED,
163     &in_mcast_loop, 0, "Loopback multicast datagrams by default");
164 
165 SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters,
166     CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_ip_mcast_filters,
167     "Per-interface stack-wide source filters");
168 
169 RB_GENERATE_PREV(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp);
170 
171 #define INM_TRACE_HIST_SIZE     32      /* size of trace history */
172 
173 /* For gdb */
174 __private_extern__ unsigned int inm_trace_hist_size = INM_TRACE_HIST_SIZE;
175 
176 struct in_multi_dbg {
177 	struct in_multi         inm;                    /* in_multi */
178 	u_int16_t               inm_refhold_cnt;        /* # of ref */
179 	u_int16_t               inm_refrele_cnt;        /* # of rele */
180 	/*
181 	 * Circular lists of inm_addref and inm_remref callers.
182 	 */
183 	ctrace_t                inm_refhold[INM_TRACE_HIST_SIZE];
184 	ctrace_t                inm_refrele[INM_TRACE_HIST_SIZE];
185 	/*
186 	 * Trash list linkage
187 	 */
188 	TAILQ_ENTRY(in_multi_dbg) inm_trash_link;
189 };
190 
191 static LCK_ATTR_DECLARE(in_multihead_lock_attr, 0, 0);
192 static LCK_GRP_DECLARE(in_multihead_lock_grp, "in_multihead");
193 
194 /* List of trash in_multi entries protected by inm_trash_lock */
195 static TAILQ_HEAD(, in_multi_dbg) inm_trash_head;
196 static LCK_MTX_DECLARE_ATTR(inm_trash_lock, &in_multihead_lock_grp,
197     &in_multihead_lock_attr);
198 
199 #if DEBUG
200 static TUNABLE(bool, inm_debug, "ifa_debug", true); /* debugging (enabled) */
201 #else
202 static TUNABLE(bool, inm_debug, "ifa_debug", false); /* debugging (disabled) */
203 #endif /* !DEBUG */
204 #define INM_ZONE_NAME           "in_multi"      /* zone name */
205 static struct zone *inm_zone;                   /* zone for in_multi */
206 
207 static KALLOC_TYPE_DEFINE(ipms_zone, struct ip_msource, NET_KT_DEFAULT);
208 static KALLOC_TYPE_DEFINE(inms_zone, struct in_msource, NET_KT_DEFAULT);
209 
210 static LCK_RW_DECLARE_ATTR(in_multihead_lock, &in_multihead_lock_grp,
211     &in_multihead_lock_attr);
212 
213 struct in_multihead in_multihead;
214 
215 static struct in_multi *in_multi_alloc(zalloc_flags_t);
216 static void in_multi_free(struct in_multi *);
217 static void in_multi_attach(struct in_multi *);
218 static void inm_trace(struct in_multi *, int);
219 
220 static struct ip_msource *ipms_alloc(zalloc_flags_t);
221 static void ipms_free(struct ip_msource *);
222 static struct in_msource *inms_alloc(zalloc_flags_t);
223 static void inms_free(struct in_msource *);
224 
225 static __inline int
ip_msource_cmp(const struct ip_msource * a,const struct ip_msource * b)226 ip_msource_cmp(const struct ip_msource *a, const struct ip_msource *b)
227 {
228 	if (a->ims_haddr < b->ims_haddr) {
229 		return -1;
230 	}
231 	if (a->ims_haddr == b->ims_haddr) {
232 		return 0;
233 	}
234 	return 1;
235 }
236 
237 /*
238  * Inline function which wraps assertions for a valid ifp.
239  */
240 static __inline__ int
inm_is_ifp_detached(const struct in_multi * inm)241 inm_is_ifp_detached(const struct in_multi *inm)
242 {
243 	VERIFY(inm->inm_ifma != NULL);
244 	VERIFY(inm->inm_ifp == inm->inm_ifma->ifma_ifp);
245 
246 	return !ifnet_is_attached(inm->inm_ifp, 0);
247 }
248 
249 /*
250  * Initialize an in_mfilter structure to a known state at t0, t1
251  * with an empty source filter list.
252  */
253 static __inline__ void
imf_init(struct in_mfilter * imf,const uint8_t st0,const uint8_t st1)254 imf_init(struct in_mfilter *imf, const uint8_t st0, const uint8_t st1)
255 {
256 	memset(imf, 0, sizeof(struct in_mfilter));
257 	RB_INIT(&imf->imf_sources);
258 	imf->imf_st[0] = st0;
259 	imf->imf_st[1] = st1;
260 }
261 
262 /*
263  * Resize the ip_moptions vector to the next power-of-two minus 1.
264  */
265 static int
imo_grow(struct ip_moptions * imo,uint16_t newmax)266 imo_grow(struct ip_moptions *imo, uint16_t newmax)
267 {
268 	struct in_multi         **nmships;
269 	struct in_multi         **omships;
270 	struct in_mfilter        *nmfilters;
271 	struct in_mfilter        *omfilters;
272 	int                       err;
273 	uint16_t                  idx;
274 	uint16_t                  oldmax;
275 
276 	IMO_LOCK_ASSERT_HELD(imo);
277 
278 	nmships = NULL;
279 	nmfilters = NULL;
280 	err = 0;
281 	omships = imo->imo_membership;
282 	omfilters = imo->imo_mfilters;
283 	oldmax = imo->imo_max_memberships;
284 
285 	if (newmax == 0) {
286 		newmax = ((oldmax + 1) * 2) - 1;
287 	} else if (newmax <= oldmax) {
288 		/* Nothing to do, exit early. */
289 		return 0;
290 	}
291 
292 	if (newmax > IP_MAX_MEMBERSHIPS) {
293 		err = ETOOMANYREFS;
294 		goto cleanup;
295 	}
296 
297 	if ((nmships = kalloc_type(struct in_multi *, newmax,
298 	    Z_WAITOK | Z_ZERO)) == NULL) {
299 		err = ENOMEM;
300 		goto cleanup;
301 	}
302 
303 	if ((nmfilters = kalloc_type(struct in_mfilter, newmax,
304 	    Z_WAITOK | Z_ZERO)) == NULL) {
305 		err = ENOMEM;
306 		goto cleanup;
307 	}
308 
309 	/* Copy the existing memberships and release the memory. */
310 	if (omships != NULL) {
311 		VERIFY(oldmax <= newmax);
312 		memcpy(nmships, omships, oldmax * sizeof(struct in_multi *));
313 		kfree_type(struct in_multi *, oldmax, omships);
314 	}
315 
316 	/* Copy the existing filters and release the memory. */
317 	if (omfilters != NULL) {
318 		VERIFY(oldmax <= newmax);
319 		memcpy(nmfilters, omfilters, oldmax * sizeof(struct in_mfilter));
320 		kfree_type(struct in_mfilter, oldmax, omfilters);
321 	}
322 
323 	/* Initialize the newly allocated source filter heads. */
324 	for (idx = oldmax; idx < newmax; idx++) {
325 		imf_init(&nmfilters[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
326 	}
327 
328 	imo->imo_membership = nmships;
329 	nmships = NULL;
330 	imo->imo_mfilters = nmfilters;
331 	nmfilters = NULL;
332 	imo->imo_max_memberships = newmax;
333 
334 	return 0;
335 
336 cleanup:
337 	if (nmfilters != NULL) {
338 		kfree_type(struct in_mfilter, newmax, nmfilters);
339 	}
340 
341 	if (nmships != NULL) {
342 		kfree_type(struct in_multi *, newmax, nmships);
343 	}
344 
345 	return err;
346 }
347 
348 /*
349  * Find an IPv4 multicast group entry for this ip_moptions instance
350  * which matches the specified group, and optionally an interface.
351  * Return its index into the array, or -1 if not found.
352  */
353 static size_t
imo_match_group(const struct ip_moptions * imo,const struct ifnet * ifp,const struct sockaddr_in * group)354 imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp,
355     const struct sockaddr_in *group)
356 {
357 	struct in_multi *pinm;
358 	int               idx;
359 	int               nmships;
360 
361 	IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo));
362 
363 
364 	/* The imo_membership array may be lazy allocated. */
365 	if (imo->imo_membership == NULL || imo->imo_num_memberships == 0) {
366 		return -1;
367 	}
368 
369 	nmships = imo->imo_num_memberships;
370 	for (idx = 0; idx < nmships; idx++) {
371 		pinm = imo->imo_membership[idx];
372 		if (pinm == NULL) {
373 			continue;
374 		}
375 		INM_LOCK(pinm);
376 		if ((ifp == NULL || (pinm->inm_ifp == ifp)) &&
377 		    in_hosteq(pinm->inm_addr, group->sin_addr)) {
378 			INM_UNLOCK(pinm);
379 			break;
380 		}
381 		INM_UNLOCK(pinm);
382 	}
383 	if (idx >= nmships) {
384 		idx = -1;
385 	}
386 
387 	return idx;
388 }
389 
390 /*
391  * Find an IPv4 multicast source entry for this imo which matches
392  * the given group index for this socket, and source address.
393  *
394  * NOTE: This does not check if the entry is in-mode, merely if
395  * it exists, which may not be the desired behaviour.
396  */
397 static struct in_msource *
imo_match_source(const struct ip_moptions * imo,const size_t gidx,const struct sockaddr_in * src)398 imo_match_source(const struct ip_moptions *imo, const size_t gidx,
399     const struct sockaddr_in *src)
400 {
401 	struct ip_msource        find;
402 	struct in_mfilter       *imf;
403 	struct ip_msource       *ims;
404 
405 	IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo));
406 
407 	VERIFY(src->sin_family == AF_INET);
408 	VERIFY(gidx != (size_t)-1 && gidx < imo->imo_num_memberships);
409 
410 	/* The imo_mfilters array may be lazy allocated. */
411 	if (imo->imo_mfilters == NULL) {
412 		return NULL;
413 	}
414 	imf = &imo->imo_mfilters[gidx];
415 
416 	/* Source trees are keyed in host byte order. */
417 	find.ims_haddr = ntohl(src->sin_addr.s_addr);
418 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
419 
420 	return (struct in_msource *)ims;
421 }
422 
423 /*
424  * Perform filtering for multicast datagrams on a socket by group and source.
425  *
426  * Returns 0 if a datagram should be allowed through, or various error codes
427  * if the socket was not a member of the group, or the source was muted, etc.
428  */
429 int
imo_multi_filter(const struct ip_moptions * imo,const struct ifnet * ifp,const struct sockaddr_in * group,const struct sockaddr_in * src)430 imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp,
431     const struct sockaddr_in *group, const struct sockaddr_in *src)
432 {
433 	size_t gidx;
434 	struct in_msource *ims;
435 	int mode;
436 
437 	IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo));
438 	VERIFY(ifp != NULL);
439 
440 	gidx = imo_match_group(imo, ifp, group);
441 	if (gidx == (size_t)-1) {
442 		return MCAST_NOTGMEMBER;
443 	}
444 
445 	/*
446 	 * Check if the source was included in an (S,G) join.
447 	 * Allow reception on exclusive memberships by default,
448 	 * reject reception on inclusive memberships by default.
449 	 * Exclude source only if an in-mode exclude filter exists.
450 	 * Include source only if an in-mode include filter exists.
451 	 * NOTE: We are comparing group state here at IGMP t1 (now)
452 	 * with socket-layer t0 (since last downcall).
453 	 */
454 	mode = imo->imo_mfilters[gidx].imf_st[1];
455 	ims = imo_match_source(imo, gidx, src);
456 
457 	if ((ims == NULL && mode == MCAST_INCLUDE) ||
458 	    (ims != NULL && ims->imsl_st[0] != mode)) {
459 		return MCAST_NOTSMEMBER;
460 	}
461 
462 	return MCAST_PASS;
463 }
464 
465 int
imo_clone(struct inpcb * from_inp,struct inpcb * to_inp)466 imo_clone(struct inpcb *from_inp, struct inpcb *to_inp)
467 {
468 	int err = 0;
469 	struct ip_moptions *from;
470 	struct ip_moptions *to;
471 
472 	from = inp_findmoptions(from_inp);
473 	if (from == NULL) {
474 		return ENOMEM;
475 	}
476 
477 	to = inp_findmoptions(to_inp);
478 	if (to == NULL) {
479 		IMO_REMREF(from);
480 		return ENOMEM;
481 	}
482 
483 	IMO_LOCK(from);
484 	IMO_LOCK(to);
485 
486 	to->imo_multicast_ifp = from->imo_multicast_ifp;
487 	to->imo_multicast_vif = from->imo_multicast_vif;
488 	to->imo_multicast_ttl = from->imo_multicast_ttl;
489 	to->imo_multicast_loop = from->imo_multicast_loop;
490 
491 	/*
492 	 * We're cloning, so drop any existing memberships and source
493 	 * filters on the destination ip_moptions.
494 	 */
495 	IMO_PURGE_LOCKED(to);
496 
497 	VERIFY(to->imo_max_memberships != 0 && from->imo_max_memberships != 0);
498 	if (to->imo_max_memberships < from->imo_max_memberships) {
499 		/*
500 		 * Ensure source and destination ip_moptions memberships
501 		 * and source filters arrays are at least equal in size.
502 		 */
503 		err = imo_grow(to, from->imo_max_memberships);
504 		if (err != 0) {
505 			goto done;
506 		}
507 	}
508 	VERIFY(to->imo_max_memberships >= from->imo_max_memberships);
509 
510 	/*
511 	 * Source filtering doesn't apply to OpenTransport socket,
512 	 * so simply hold additional reference count per membership.
513 	 */
514 	for (int i = 0; i < from->imo_num_memberships; i++) {
515 		to->imo_membership[i] =
516 		    in_addmulti(&from->imo_membership[i]->inm_addr,
517 		    from->imo_membership[i]->inm_ifp);
518 		if (to->imo_membership[i] == NULL) {
519 			break;
520 		}
521 		to->imo_num_memberships++;
522 	}
523 	VERIFY(to->imo_num_memberships == from->imo_num_memberships);
524 
525 done:
526 	IMO_UNLOCK(to);
527 	IMO_REMREF(to);
528 	IMO_UNLOCK(from);
529 	IMO_REMREF(from);
530 
531 	return err;
532 }
533 
534 /*
535  * Find and return a reference to an in_multi record for (ifp, group),
536  * and bump its reference count.
537  * If one does not exist, try to allocate it, and update link-layer multicast
538  * filters on ifp to listen for group.
539  * Return 0 if successful, otherwise return an appropriate error code.
540  */
541 static int
in_getmulti(struct ifnet * ifp,const struct in_addr * group,struct in_multi ** pinm)542 in_getmulti(struct ifnet *ifp, const struct in_addr *group,
543     struct in_multi **pinm)
544 {
545 	struct sockaddr_in       gsin;
546 	struct ifmultiaddr      *ifma;
547 	struct in_multi         *inm;
548 	int                     error;
549 
550 	in_multihead_lock_shared();
551 	IN_LOOKUP_MULTI(group, ifp, inm);
552 	if (inm != NULL) {
553 		INM_LOCK(inm);
554 		VERIFY(inm->inm_reqcnt >= 1);
555 		inm->inm_reqcnt++;
556 		VERIFY(inm->inm_reqcnt != 0);
557 		*pinm = inm;
558 		INM_UNLOCK(inm);
559 		in_multihead_lock_done();
560 		/*
561 		 * We already joined this group; return the inm
562 		 * with a refcount held (via lookup) for caller.
563 		 */
564 		return 0;
565 	}
566 	in_multihead_lock_done();
567 
568 	bzero(&gsin, sizeof(gsin));
569 	gsin.sin_family = AF_INET;
570 	gsin.sin_len = sizeof(struct sockaddr_in);
571 	gsin.sin_addr = *group;
572 
573 	/*
574 	 * Check if a link-layer group is already associated
575 	 * with this network-layer group on the given ifnet.
576 	 */
577 	error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma);
578 	if (error != 0) {
579 		return error;
580 	}
581 
582 	/*
583 	 * See comments in inm_remref() for access to ifma_protospec.
584 	 */
585 	in_multihead_lock_exclusive();
586 	IFMA_LOCK(ifma);
587 	if ((inm = ifma->ifma_protospec) != NULL) {
588 		VERIFY(ifma->ifma_addr != NULL);
589 		VERIFY(ifma->ifma_addr->sa_family == AF_INET);
590 		INM_ADDREF(inm);        /* for caller */
591 		IFMA_UNLOCK(ifma);
592 		INM_LOCK(inm);
593 		VERIFY(inm->inm_ifma == ifma);
594 		VERIFY(inm->inm_ifp == ifp);
595 		VERIFY(in_hosteq(inm->inm_addr, *group));
596 		if (inm->inm_debug & IFD_ATTACHED) {
597 			VERIFY(inm->inm_reqcnt >= 1);
598 			inm->inm_reqcnt++;
599 			VERIFY(inm->inm_reqcnt != 0);
600 			*pinm = inm;
601 			INM_UNLOCK(inm);
602 			in_multihead_lock_done();
603 			IFMA_REMREF(ifma);
604 			/*
605 			 * We lost the race with another thread doing
606 			 * in_getmulti(); since this group has already
607 			 * been joined; return the inm with a refcount
608 			 * held for caller.
609 			 */
610 			return 0;
611 		}
612 		/*
613 		 * We lost the race with another thread doing in_delmulti();
614 		 * the inm referring to the ifma has been detached, thus we
615 		 * reattach it back to the in_multihead list and return the
616 		 * inm with a refcount held for the caller.
617 		 */
618 		in_multi_attach(inm);
619 		VERIFY((inm->inm_debug &
620 		    (IFD_ATTACHED | IFD_TRASHED)) == IFD_ATTACHED);
621 		*pinm = inm;
622 		INM_UNLOCK(inm);
623 		in_multihead_lock_done();
624 		IFMA_REMREF(ifma);
625 		return 0;
626 	}
627 	IFMA_UNLOCK(ifma);
628 
629 	/*
630 	 * A new in_multi record is needed; allocate and initialize it.
631 	 * We DO NOT perform an IGMP join as the in_ layer may need to
632 	 * push an initial source list down to IGMP to support SSM.
633 	 *
634 	 * The initial source filter state is INCLUDE, {} as per the RFC.
635 	 */
636 	inm = in_multi_alloc(Z_WAITOK);
637 
638 	INM_LOCK(inm);
639 	inm->inm_addr = *group;
640 	inm->inm_ifp = ifp;
641 	inm->inm_igi = IGMP_IFINFO(ifp);
642 	VERIFY(inm->inm_igi != NULL);
643 	IGI_ADDREF(inm->inm_igi);
644 	inm->inm_ifma = ifma;           /* keep refcount from if_addmulti() */
645 	inm->inm_state = IGMP_NOT_MEMBER;
646 	/*
647 	 * Pending state-changes per group are subject to a bounds check.
648 	 */
649 	inm->inm_scq.ifq_maxlen = IGMP_MAX_STATE_CHANGES;
650 	inm->inm_st[0].iss_fmode = MCAST_UNDEFINED;
651 	inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
652 	RB_INIT(&inm->inm_srcs);
653 	*pinm = inm;
654 	in_multi_attach(inm);
655 	VERIFY((inm->inm_debug & (IFD_ATTACHED | IFD_TRASHED)) == IFD_ATTACHED);
656 	INM_ADDREF_LOCKED(inm);         /* for caller */
657 	INM_UNLOCK(inm);
658 
659 	IFMA_LOCK(ifma);
660 	VERIFY(ifma->ifma_protospec == NULL);
661 	ifma->ifma_protospec = inm;
662 	IFMA_UNLOCK(ifma);
663 	in_multihead_lock_done();
664 
665 	return 0;
666 }
667 
668 /*
669  * Clear recorded source entries for a group.
670  * Used by the IGMP code.
671  * FIXME: Should reap.
672  */
673 void
inm_clear_recorded(struct in_multi * inm)674 inm_clear_recorded(struct in_multi *inm)
675 {
676 	struct ip_msource       *ims;
677 
678 	INM_LOCK_ASSERT_HELD(inm);
679 
680 	RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
681 		if (ims->ims_stp) {
682 			ims->ims_stp = 0;
683 			--inm->inm_st[1].iss_rec;
684 		}
685 	}
686 	VERIFY(inm->inm_st[1].iss_rec == 0);
687 }
688 
689 /*
690  * Record a source as pending for a Source-Group IGMPv3 query.
691  * This lives here as it modifies the shared tree.
692  *
693  * inm is the group descriptor.
694  * naddr is the address of the source to record in network-byte order.
695  *
696  * If the net.inet.igmp.sgalloc sysctl is non-zero, we will
697  * lazy-allocate a source node in response to an SG query.
698  * Otherwise, no allocation is performed. This saves some memory
699  * with the trade-off that the source will not be reported to the
700  * router if joined in the window between the query response and
701  * the group actually being joined on the local host.
702  *
703  * Return 0 if the source didn't exist or was already marked as recorded.
704  * Return 1 if the source was marked as recorded by this function.
705  * Return <0 if any error occured (negated errno code).
706  */
707 int
inm_record_source(struct in_multi * inm,const in_addr_t naddr)708 inm_record_source(struct in_multi *inm, const in_addr_t naddr)
709 {
710 	struct ip_msource        find;
711 	struct ip_msource       *ims, *nims;
712 
713 	INM_LOCK_ASSERT_HELD(inm);
714 
715 	find.ims_haddr = ntohl(naddr);
716 	ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
717 	if (ims && ims->ims_stp) {
718 		return 0;
719 	}
720 	if (ims == NULL) {
721 		if (inm->inm_nsrc == in_mcast_maxgrpsrc) {
722 			return -ENOSPC;
723 		}
724 		nims = ipms_alloc(Z_WAITOK);
725 		nims->ims_haddr = find.ims_haddr;
726 		RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
727 		++inm->inm_nsrc;
728 		ims = nims;
729 	}
730 
731 	/*
732 	 * Mark the source as recorded and update the recorded
733 	 * source count.
734 	 */
735 	++ims->ims_stp;
736 	++inm->inm_st[1].iss_rec;
737 
738 	return 1;
739 }
740 
741 /*
742  * Return a pointer to an in_msource owned by an in_mfilter,
743  * given its source address.
744  * Lazy-allocate if needed. If this is a new entry its filter state is
745  * undefined at t0.
746  *
747  * imf is the filter set being modified.
748  * haddr is the source address in *host* byte-order.
749  *
750  * Caller is expected to be holding imo_lock.
751  */
752 static int
imf_get_source(struct in_mfilter * imf,const struct sockaddr_in * psin,struct in_msource ** plims)753 imf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin,
754     struct in_msource **plims)
755 {
756 	struct ip_msource        find;
757 	struct ip_msource       *ims;
758 	struct in_msource       *lims;
759 	int                      error;
760 
761 	error = 0;
762 	ims = NULL;
763 	lims = NULL;
764 
765 	/* key is host byte order */
766 	find.ims_haddr = ntohl(psin->sin_addr.s_addr);
767 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
768 	lims = (struct in_msource *)ims;
769 	if (lims == NULL) {
770 		if (imf->imf_nsrc == in_mcast_maxsocksrc) {
771 			return ENOSPC;
772 		}
773 		lims = inms_alloc(Z_WAITOK);
774 		lims->ims_haddr = find.ims_haddr;
775 		lims->imsl_st[0] = MCAST_UNDEFINED;
776 		RB_INSERT(ip_msource_tree, &imf->imf_sources,
777 		    (struct ip_msource *)lims);
778 		++imf->imf_nsrc;
779 	}
780 
781 	*plims = lims;
782 
783 	return error;
784 }
785 
786 /*
787  * Graft a source entry into an existing socket-layer filter set,
788  * maintaining any required invariants and checking allocations.
789  *
790  * The source is marked as being in the new filter mode at t1.
791  *
792  * Return the pointer to the new node, otherwise return NULL.
793  *
794  * Caller is expected to be holding imo_lock.
795  */
796 static struct in_msource *
imf_graft(struct in_mfilter * imf,const uint8_t st1,const struct sockaddr_in * psin)797 imf_graft(struct in_mfilter *imf, const uint8_t st1,
798     const struct sockaddr_in *psin)
799 {
800 	struct in_msource       *lims;
801 
802 	lims = inms_alloc(Z_WAITOK);
803 	lims->ims_haddr = ntohl(psin->sin_addr.s_addr);
804 	lims->imsl_st[0] = MCAST_UNDEFINED;
805 	lims->imsl_st[1] = st1;
806 	RB_INSERT(ip_msource_tree, &imf->imf_sources,
807 	    (struct ip_msource *)lims);
808 	++imf->imf_nsrc;
809 
810 	return lims;
811 }
812 
813 /*
814  * Prune a source entry from an existing socket-layer filter set,
815  * maintaining any required invariants and checking allocations.
816  *
817  * The source is marked as being left at t1, it is not freed.
818  *
819  * Return 0 if no error occurred, otherwise return an errno value.
820  *
821  * Caller is expected to be holding imo_lock.
822  */
823 static int
imf_prune(struct in_mfilter * imf,const struct sockaddr_in * psin)824 imf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin)
825 {
826 	struct ip_msource        find;
827 	struct ip_msource       *ims;
828 	struct in_msource       *lims;
829 
830 	/* key is host byte order */
831 	find.ims_haddr = ntohl(psin->sin_addr.s_addr);
832 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
833 	if (ims == NULL) {
834 		return ENOENT;
835 	}
836 	lims = (struct in_msource *)ims;
837 	lims->imsl_st[1] = MCAST_UNDEFINED;
838 	return 0;
839 }
840 
841 /*
842  * Revert socket-layer filter set deltas at t1 to t0 state.
843  *
844  * Caller is expected to be holding imo_lock.
845  */
846 static void
imf_rollback(struct in_mfilter * imf)847 imf_rollback(struct in_mfilter *imf)
848 {
849 	struct ip_msource       *ims, *tims;
850 	struct in_msource       *lims;
851 
852 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
853 		lims = (struct in_msource *)ims;
854 		if (lims->imsl_st[0] == lims->imsl_st[1]) {
855 			/* no change at t1 */
856 			continue;
857 		} else if (lims->imsl_st[0] != MCAST_UNDEFINED) {
858 			/* revert change to existing source at t1 */
859 			lims->imsl_st[1] = lims->imsl_st[0];
860 		} else {
861 			/* revert source added t1 */
862 			IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__,
863 			    (uint64_t)VM_KERNEL_ADDRPERM(lims)));
864 			RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
865 			inms_free(lims);
866 			imf->imf_nsrc--;
867 		}
868 	}
869 	imf->imf_st[1] = imf->imf_st[0];
870 }
871 
872 /*
873  * Mark socket-layer filter set as INCLUDE {} at t1.
874  *
875  * Caller is expected to be holding imo_lock.
876  */
877 void
imf_leave(struct in_mfilter * imf)878 imf_leave(struct in_mfilter *imf)
879 {
880 	struct ip_msource       *ims;
881 	struct in_msource       *lims;
882 
883 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
884 		lims = (struct in_msource *)ims;
885 		lims->imsl_st[1] = MCAST_UNDEFINED;
886 	}
887 	imf->imf_st[1] = MCAST_INCLUDE;
888 }
889 
890 /*
891  * Mark socket-layer filter set deltas as committed.
892  *
893  * Caller is expected to be holding imo_lock.
894  */
895 static void
imf_commit(struct in_mfilter * imf)896 imf_commit(struct in_mfilter *imf)
897 {
898 	struct ip_msource       *ims;
899 	struct in_msource       *lims;
900 
901 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
902 		lims = (struct in_msource *)ims;
903 		lims->imsl_st[0] = lims->imsl_st[1];
904 	}
905 	imf->imf_st[0] = imf->imf_st[1];
906 }
907 
908 /*
909  * Reap unreferenced sources from socket-layer filter set.
910  *
911  * Caller is expected to be holding imo_lock.
912  */
913 static void
imf_reap(struct in_mfilter * imf)914 imf_reap(struct in_mfilter *imf)
915 {
916 	struct ip_msource       *ims, *tims;
917 	struct in_msource       *lims;
918 
919 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
920 		lims = (struct in_msource *)ims;
921 		if ((lims->imsl_st[0] == MCAST_UNDEFINED) &&
922 		    (lims->imsl_st[1] == MCAST_UNDEFINED)) {
923 			IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__,
924 			    (uint64_t)VM_KERNEL_ADDRPERM(lims)));
925 			RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
926 			inms_free(lims);
927 			imf->imf_nsrc--;
928 		}
929 	}
930 }
931 
932 /*
933  * Purge socket-layer filter set.
934  *
935  * Caller is expected to be holding imo_lock.
936  */
937 void
imf_purge(struct in_mfilter * imf)938 imf_purge(struct in_mfilter *imf)
939 {
940 	struct ip_msource       *ims, *tims;
941 	struct in_msource       *lims;
942 
943 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
944 		lims = (struct in_msource *)ims;
945 		IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__,
946 		    (uint64_t)VM_KERNEL_ADDRPERM(lims)));
947 		RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
948 		inms_free(lims);
949 		imf->imf_nsrc--;
950 	}
951 	imf->imf_st[0] = imf->imf_st[1] = MCAST_UNDEFINED;
952 	VERIFY(RB_EMPTY(&imf->imf_sources));
953 }
954 
955 /*
956  * Look up a source filter entry for a multicast group.
957  *
958  * inm is the group descriptor to work with.
959  * haddr is the host-byte-order IPv4 address to look up.
960  * noalloc may be non-zero to suppress allocation of sources.
961  * *pims will be set to the address of the retrieved or allocated source.
962  *
963  * Return 0 if successful, otherwise return a non-zero error code.
964  */
965 static int
inm_get_source(struct in_multi * inm,const in_addr_t haddr,const int noalloc,struct ip_msource ** pims)966 inm_get_source(struct in_multi *inm, const in_addr_t haddr,
967     const int noalloc, struct ip_msource **pims)
968 {
969 	struct ip_msource        find;
970 	struct ip_msource       *ims, *nims;
971 #ifdef IGMP_DEBUG
972 	struct in_addr ia;
973 	char buf[MAX_IPv4_STR_LEN];
974 #endif
975 	INM_LOCK_ASSERT_HELD(inm);
976 
977 	find.ims_haddr = haddr;
978 	ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
979 	if (ims == NULL && !noalloc) {
980 		if (inm->inm_nsrc == in_mcast_maxgrpsrc) {
981 			return ENOSPC;
982 		}
983 		nims = ipms_alloc(Z_WAITOK);
984 		nims->ims_haddr = haddr;
985 		RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
986 		++inm->inm_nsrc;
987 		ims = nims;
988 #ifdef IGMP_DEBUG
989 		ia.s_addr = htonl(haddr);
990 		inet_ntop(AF_INET, &ia, buf, sizeof(buf));
991 		IGMP_PRINTF(("%s: allocated %s as 0x%llx\n", __func__,
992 		    buf, (uint64_t)VM_KERNEL_ADDRPERM(ims)));
993 #endif
994 	}
995 
996 	*pims = ims;
997 	return 0;
998 }
999 
1000 /*
1001  * Helper function to derive the filter mode on a source entry
1002  * from its internal counters. Predicates are:
1003  *  A source is only excluded if all listeners exclude it.
1004  *  A source is only included if no listeners exclude it,
1005  *  and at least one listener includes it.
1006  * May be used by ifmcstat(8).
1007  */
1008 uint8_t
ims_get_mode(const struct in_multi * inm,const struct ip_msource * ims,uint8_t t)1009 ims_get_mode(const struct in_multi *inm, const struct ip_msource *ims,
1010     uint8_t t)
1011 {
1012 	INM_LOCK_ASSERT_HELD(__DECONST(struct in_multi *, inm));
1013 
1014 	t = !!t;
1015 	if (inm->inm_st[t].iss_ex > 0 &&
1016 	    inm->inm_st[t].iss_ex == ims->ims_st[t].ex) {
1017 		return MCAST_EXCLUDE;
1018 	} else if (ims->ims_st[t].in > 0 && ims->ims_st[t].ex == 0) {
1019 		return MCAST_INCLUDE;
1020 	}
1021 	return MCAST_UNDEFINED;
1022 }
1023 
1024 /*
1025  * Merge socket-layer source into IGMP-layer source.
1026  * If rollback is non-zero, perform the inverse of the merge.
1027  */
1028 static void
ims_merge(struct ip_msource * ims,const struct in_msource * lims,const int rollback)1029 ims_merge(struct ip_msource *ims, const struct in_msource *lims,
1030     const int rollback)
1031 {
1032 	int n = rollback ? -1 : 1;
1033 #ifdef IGMP_DEBUG
1034 	struct in_addr ia;
1035 
1036 	ia.s_addr = htonl(ims->ims_haddr);
1037 #endif
1038 
1039 	if (lims->imsl_st[0] == MCAST_EXCLUDE) {
1040 		IGMP_INET_PRINTF(ia,
1041 		    ("%s: t1 ex -= %d on %s\n",
1042 		    __func__, n, _igmp_inet_buf));
1043 		ims->ims_st[1].ex -= n;
1044 	} else if (lims->imsl_st[0] == MCAST_INCLUDE) {
1045 		IGMP_INET_PRINTF(ia,
1046 		    ("%s: t1 in -= %d on %s\n",
1047 		    __func__, n, _igmp_inet_buf));
1048 		ims->ims_st[1].in -= n;
1049 	}
1050 
1051 	if (lims->imsl_st[1] == MCAST_EXCLUDE) {
1052 		IGMP_INET_PRINTF(ia,
1053 		    ("%s: t1 ex += %d on %s\n",
1054 		    __func__, n, _igmp_inet_buf));
1055 		ims->ims_st[1].ex += n;
1056 	} else if (lims->imsl_st[1] == MCAST_INCLUDE) {
1057 		IGMP_INET_PRINTF(ia,
1058 		    ("%s: t1 in += %d on %s\n",
1059 		    __func__, n, _igmp_inet_buf));
1060 		ims->ims_st[1].in += n;
1061 	}
1062 }
1063 
1064 /*
1065  * Atomically update the global in_multi state, when a membership's
1066  * filter list is being updated in any way.
1067  *
1068  * imf is the per-inpcb-membership group filter pointer.
1069  * A fake imf may be passed for in-kernel consumers.
1070  *
1071  * XXX This is a candidate for a set-symmetric-difference style loop
1072  * which would eliminate the repeated lookup from root of ims nodes,
1073  * as they share the same key space.
1074  *
1075  * If any error occurred this function will back out of refcounts
1076  * and return a non-zero value.
1077  */
1078 static int
inm_merge(struct in_multi * inm,struct in_mfilter * imf)1079 inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1080 {
1081 	struct ip_msource       *ims, *nims = NULL;
1082 	struct in_msource       *lims;
1083 	int                      schanged, error;
1084 	int                      nsrc0, nsrc1;
1085 
1086 	INM_LOCK_ASSERT_HELD(inm);
1087 
1088 	schanged = 0;
1089 	error = 0;
1090 	nsrc1 = nsrc0 = 0;
1091 
1092 	/*
1093 	 * Update the source filters first, as this may fail.
1094 	 * Maintain count of in-mode filters at t0, t1. These are
1095 	 * used to work out if we transition into ASM mode or not.
1096 	 * Maintain a count of source filters whose state was
1097 	 * actually modified by this operation.
1098 	 */
1099 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1100 		lims = (struct in_msource *)ims;
1101 		if (lims->imsl_st[0] == imf->imf_st[0]) {
1102 			nsrc0++;
1103 		}
1104 		if (lims->imsl_st[1] == imf->imf_st[1]) {
1105 			nsrc1++;
1106 		}
1107 		if (lims->imsl_st[0] == lims->imsl_st[1]) {
1108 			continue;
1109 		}
1110 		error = inm_get_source(inm, lims->ims_haddr, 0, &nims);
1111 		++schanged;
1112 		if (error) {
1113 			break;
1114 		}
1115 		ims_merge(nims, lims, 0);
1116 	}
1117 	if (error) {
1118 		struct ip_msource *bims;
1119 
1120 		RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) {
1121 			lims = (struct in_msource *)ims;
1122 			if (lims->imsl_st[0] == lims->imsl_st[1]) {
1123 				continue;
1124 			}
1125 			(void) inm_get_source(inm, lims->ims_haddr, 1, &bims);
1126 			if (bims == NULL) {
1127 				continue;
1128 			}
1129 			ims_merge(bims, lims, 1);
1130 		}
1131 		goto out_reap;
1132 	}
1133 
1134 	IGMP_PRINTF(("%s: imf filters in-mode: %d at t0, %d at t1\n",
1135 	    __func__, nsrc0, nsrc1));
1136 
1137 	/* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */
1138 	if (imf->imf_st[0] == imf->imf_st[1] &&
1139 	    imf->imf_st[1] == MCAST_INCLUDE) {
1140 		if (nsrc1 == 0) {
1141 			IGMP_PRINTF(("%s: --in on inm at t1\n", __func__));
1142 			--inm->inm_st[1].iss_in;
1143 		}
1144 	}
1145 
1146 	/* Handle filter mode transition on socket. */
1147 	if (imf->imf_st[0] != imf->imf_st[1]) {
1148 		IGMP_PRINTF(("%s: imf transition %d to %d\n",
1149 		    __func__, imf->imf_st[0], imf->imf_st[1]));
1150 
1151 		if (imf->imf_st[0] == MCAST_EXCLUDE) {
1152 			IGMP_PRINTF(("%s: --ex on inm at t1\n", __func__));
1153 			--inm->inm_st[1].iss_ex;
1154 		} else if (imf->imf_st[0] == MCAST_INCLUDE) {
1155 			IGMP_PRINTF(("%s: --in on inm at t1\n", __func__));
1156 			--inm->inm_st[1].iss_in;
1157 		}
1158 
1159 		if (imf->imf_st[1] == MCAST_EXCLUDE) {
1160 			IGMP_PRINTF(("%s: ex++ on inm at t1\n", __func__));
1161 			inm->inm_st[1].iss_ex++;
1162 		} else if (imf->imf_st[1] == MCAST_INCLUDE && nsrc1 > 0) {
1163 			IGMP_PRINTF(("%s: in++ on inm at t1\n", __func__));
1164 			inm->inm_st[1].iss_in++;
1165 		}
1166 	}
1167 
1168 	/*
1169 	 * Track inm filter state in terms of listener counts.
1170 	 * If there are any exclusive listeners, stack-wide
1171 	 * membership is exclusive.
1172 	 * Otherwise, if only inclusive listeners, stack-wide is inclusive.
1173 	 * If no listeners remain, state is undefined at t1,
1174 	 * and the IGMP lifecycle for this group should finish.
1175 	 */
1176 	if (inm->inm_st[1].iss_ex > 0) {
1177 		IGMP_PRINTF(("%s: transition to EX\n", __func__));
1178 		inm->inm_st[1].iss_fmode = MCAST_EXCLUDE;
1179 	} else if (inm->inm_st[1].iss_in > 0) {
1180 		IGMP_PRINTF(("%s: transition to IN\n", __func__));
1181 		inm->inm_st[1].iss_fmode = MCAST_INCLUDE;
1182 	} else {
1183 		IGMP_PRINTF(("%s: transition to UNDEF\n", __func__));
1184 		inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
1185 	}
1186 
1187 	/* Decrement ASM listener count on transition out of ASM mode. */
1188 	if (imf->imf_st[0] == MCAST_EXCLUDE && nsrc0 == 0) {
1189 		if ((imf->imf_st[1] != MCAST_EXCLUDE) ||
1190 		    (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) {
1191 			IGMP_PRINTF(("%s: --asm on inm at t1\n", __func__));
1192 			--inm->inm_st[1].iss_asm;
1193 		}
1194 	}
1195 
1196 	/* Increment ASM listener count on transition to ASM mode. */
1197 	if (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 == 0) {
1198 		IGMP_PRINTF(("%s: asm++ on inm at t1\n", __func__));
1199 		inm->inm_st[1].iss_asm++;
1200 	}
1201 
1202 	IGMP_PRINTF(("%s: merged imf 0x%llx to inm 0x%llx\n", __func__,
1203 	    (uint64_t)VM_KERNEL_ADDRPERM(imf),
1204 	    (uint64_t)VM_KERNEL_ADDRPERM(inm)));
1205 	inm_print(inm);
1206 
1207 out_reap:
1208 	if (schanged > 0) {
1209 		IGMP_PRINTF(("%s: sources changed; reaping\n", __func__));
1210 		inm_reap(inm);
1211 	}
1212 	return error;
1213 }
1214 
1215 /*
1216  * Mark an in_multi's filter set deltas as committed.
1217  * Called by IGMP after a state change has been enqueued.
1218  */
1219 void
inm_commit(struct in_multi * inm)1220 inm_commit(struct in_multi *inm)
1221 {
1222 	struct ip_msource       *ims;
1223 
1224 	INM_LOCK_ASSERT_HELD(inm);
1225 
1226 	IGMP_PRINTF(("%s: commit inm 0x%llx\n", __func__,
1227 	    (uint64_t)VM_KERNEL_ADDRPERM(inm)));
1228 	IGMP_PRINTF(("%s: pre commit:\n", __func__));
1229 	inm_print(inm);
1230 
1231 	RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
1232 		ims->ims_st[0] = ims->ims_st[1];
1233 	}
1234 	inm->inm_st[0] = inm->inm_st[1];
1235 }
1236 
1237 /*
1238  * Reap unreferenced nodes from an in_multi's filter set.
1239  */
1240 static void
inm_reap(struct in_multi * inm)1241 inm_reap(struct in_multi *inm)
1242 {
1243 	struct ip_msource       *ims, *tims;
1244 
1245 	INM_LOCK_ASSERT_HELD(inm);
1246 
1247 	RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1248 		if (ims->ims_st[0].ex > 0 || ims->ims_st[0].in > 0 ||
1249 		    ims->ims_st[1].ex > 0 || ims->ims_st[1].in > 0 ||
1250 		    ims->ims_stp != 0) {
1251 			continue;
1252 		}
1253 		IGMP_PRINTF(("%s: free ims 0x%llx\n", __func__,
1254 		    (uint64_t)VM_KERNEL_ADDRPERM(ims)));
1255 		RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1256 		ipms_free(ims);
1257 		inm->inm_nsrc--;
1258 	}
1259 }
1260 
1261 /*
1262  * Purge all source nodes from an in_multi's filter set.
1263  */
1264 void
inm_purge(struct in_multi * inm)1265 inm_purge(struct in_multi *inm)
1266 {
1267 	struct ip_msource       *ims, *tims;
1268 
1269 	INM_LOCK_ASSERT_HELD(inm);
1270 
1271 	RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1272 		IGMP_PRINTF(("%s: free ims 0x%llx\n", __func__,
1273 		    (uint64_t)VM_KERNEL_ADDRPERM(ims)));
1274 		RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1275 		ipms_free(ims);
1276 		inm->inm_nsrc--;
1277 	}
1278 }
1279 
1280 /*
1281  * Join a multicast group; real entry point.
1282  *
1283  * Only preserves atomicity at inm level.
1284  * NOTE: imf argument cannot be const due to sys/tree.h limitations.
1285  *
1286  * If the IGMP downcall fails, the group is not joined, and an error
1287  * code is returned.
1288  */
1289 static int
in_joingroup(struct ifnet * ifp,const struct in_addr * gina,struct in_mfilter * imf,struct in_multi ** pinm)1290 in_joingroup(struct ifnet *ifp, const struct in_addr *gina,
1291     /*const*/ struct in_mfilter *imf, struct in_multi **pinm)
1292 {
1293 	struct in_mfilter        timf;
1294 	struct in_multi         *inm = NULL;
1295 	int                      error = 0;
1296 	struct igmp_tparams      itp;
1297 
1298 	IGMP_INET_PRINTF(*gina, ("%s: join %s on 0x%llx(%s))\n", __func__,
1299 	    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1300 
1301 	bzero(&itp, sizeof(itp));
1302 	*pinm = NULL;
1303 
1304 	/*
1305 	 * If no imf was specified (i.e. kernel consumer),
1306 	 * fake one up and assume it is an ASM join.
1307 	 */
1308 	if (imf == NULL) {
1309 		imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE);
1310 		imf = &timf;
1311 	}
1312 
1313 	error = in_getmulti(ifp, gina, &inm);
1314 	if (error) {
1315 		IGMP_PRINTF(("%s: in_getmulti() failure\n", __func__));
1316 		return error;
1317 	}
1318 
1319 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
1320 
1321 	INM_LOCK(inm);
1322 	error = inm_merge(inm, imf);
1323 	if (error) {
1324 		IGMP_PRINTF(("%s: failed to merge inm state\n", __func__));
1325 		goto out_inm_release;
1326 	}
1327 
1328 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
1329 	error = igmp_change_state(inm, &itp);
1330 	if (error) {
1331 		IGMP_PRINTF(("%s: failed to update source\n", __func__));
1332 		imf_rollback(imf);
1333 		goto out_inm_release;
1334 	}
1335 
1336 out_inm_release:
1337 	if (error) {
1338 		IGMP_PRINTF(("%s: dropping ref on 0x%llx\n", __func__,
1339 		    (uint64_t)VM_KERNEL_ADDRPERM(inm)));
1340 		INM_UNLOCK(inm);
1341 		INM_REMREF(inm);
1342 	} else {
1343 		INM_UNLOCK(inm);
1344 		*pinm = inm;    /* keep refcount from in_getmulti() */
1345 	}
1346 
1347 	/* schedule timer now that we've dropped the lock(s) */
1348 	igmp_set_fast_timeout(&itp);
1349 
1350 	return error;
1351 }
1352 
1353 /*
1354  * Leave a multicast group; real entry point.
1355  * All source filters will be expunged.
1356  *
1357  * Only preserves atomicity at inm level.
1358  *
1359  * Note: This is not the same as inm_release(*) as this function also
1360  * makes a state change downcall into IGMP.
1361  */
1362 int
in_leavegroup(struct in_multi * inm,struct in_mfilter * imf)1363 in_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1364 {
1365 	struct in_mfilter        timf;
1366 	int                      error, lastref;
1367 	struct igmp_tparams      itp;
1368 
1369 	bzero(&itp, sizeof(itp));
1370 	error = 0;
1371 
1372 	INM_LOCK_ASSERT_NOTHELD(inm);
1373 
1374 	in_multihead_lock_exclusive();
1375 	INM_LOCK(inm);
1376 
1377 	IGMP_INET_PRINTF(inm->inm_addr,
1378 	    ("%s: leave inm 0x%llx, %s/%s%d, imf 0x%llx\n", __func__,
1379 	    (uint64_t)VM_KERNEL_ADDRPERM(inm), _igmp_inet_buf,
1380 	    (inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_name),
1381 	    inm->inm_ifp->if_unit, (uint64_t)VM_KERNEL_ADDRPERM(imf)));
1382 
1383 	/*
1384 	 * If no imf was specified (i.e. kernel consumer),
1385 	 * fake one up and assume it is an ASM join.
1386 	 */
1387 	if (imf == NULL) {
1388 		imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED);
1389 		imf = &timf;
1390 	}
1391 
1392 	/*
1393 	 * Begin state merge transaction at IGMP layer.
1394 	 *
1395 	 * As this particular invocation should not cause any memory
1396 	 * to be allocated, and there is no opportunity to roll back
1397 	 * the transaction, it MUST NOT fail.
1398 	 */
1399 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
1400 
1401 	error = inm_merge(inm, imf);
1402 	KASSERT(error == 0, ("%s: failed to merge inm state\n", __func__));
1403 
1404 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
1405 	error = igmp_change_state(inm, &itp);
1406 #if IGMP_DEBUG
1407 	if (error) {
1408 		IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
1409 	}
1410 #endif
1411 	lastref = in_multi_detach(inm);
1412 	VERIFY(!lastref || (!(inm->inm_debug & IFD_ATTACHED) &&
1413 	    inm->inm_reqcnt == 0));
1414 	INM_UNLOCK(inm);
1415 	in_multihead_lock_done();
1416 
1417 	if (lastref) {
1418 		INM_REMREF(inm);        /* for in_multihead list */
1419 	}
1420 	/* schedule timer now that we've dropped the lock(s) */
1421 	igmp_set_fast_timeout(&itp);
1422 
1423 	return error;
1424 }
1425 
1426 /*
1427  * Join an IPv4 multicast group in (*,G) exclusive mode.
1428  * The group must be a 224.0.0.0/24 link-scope group.
1429  * This KPI is for legacy kernel consumers only.
1430  */
1431 struct in_multi *
in_addmulti(struct in_addr * ap,struct ifnet * ifp)1432 in_addmulti(struct in_addr *ap, struct ifnet *ifp)
1433 {
1434 	struct in_multi *pinm = NULL;
1435 	int error;
1436 
1437 	KASSERT(IN_LOCAL_GROUP(ntohl(ap->s_addr)),
1438 	    ("%s: %s not in 224.0.0.0/24\n", __func__, inet_ntoa(*ap)));
1439 
1440 	error = in_joingroup(ifp, ap, NULL, &pinm);
1441 	VERIFY(pinm != NULL || error != 0);
1442 
1443 	return pinm;
1444 }
1445 
1446 /*
1447  * Leave an IPv4 multicast group, assumed to be in exclusive (*,G) mode.
1448  * This KPI is for legacy kernel consumers only.
1449  */
1450 void
in_delmulti(struct in_multi * inm)1451 in_delmulti(struct in_multi *inm)
1452 {
1453 	(void) in_leavegroup(inm, NULL);
1454 }
1455 
1456 /*
1457  * Block or unblock an ASM multicast source on an inpcb.
1458  * This implements the delta-based API described in RFC 3678.
1459  *
1460  * The delta-based API applies only to exclusive-mode memberships.
1461  * An IGMP downcall will be performed.
1462  *
1463  * Return 0 if successful, otherwise return an appropriate error code.
1464  */
1465 static int
inp_block_unblock_source(struct inpcb * inp,struct sockopt * sopt)1466 inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
1467 {
1468 	struct group_source_req          gsr;
1469 	struct sockaddr_in              *gsa, *ssa;
1470 	struct ifnet                    *ifp;
1471 	struct in_mfilter               *imf;
1472 	struct ip_moptions              *imo;
1473 	struct in_msource               *ims;
1474 	struct in_multi                 *inm;
1475 	size_t                           idx;
1476 	uint8_t                          fmode;
1477 	int                              error, doblock;
1478 	unsigned int                     ifindex = 0;
1479 	struct igmp_tparams              itp;
1480 
1481 	bzero(&itp, sizeof(itp));
1482 	ifp = NULL;
1483 	error = 0;
1484 	doblock = 0;
1485 
1486 	memset(&gsr, 0, sizeof(struct group_source_req));
1487 	gsa = (struct sockaddr_in *)&gsr.gsr_group;
1488 	ssa = (struct sockaddr_in *)&gsr.gsr_source;
1489 
1490 	switch (sopt->sopt_name) {
1491 	case IP_BLOCK_SOURCE:
1492 	case IP_UNBLOCK_SOURCE: {
1493 		struct ip_mreq_source    mreqs;
1494 
1495 		error = sooptcopyin(sopt, &mreqs,
1496 		    sizeof(struct ip_mreq_source),
1497 		    sizeof(struct ip_mreq_source));
1498 		if (error) {
1499 			return error;
1500 		}
1501 
1502 		gsa->sin_family = AF_INET;
1503 		gsa->sin_len = sizeof(struct sockaddr_in);
1504 		gsa->sin_addr = mreqs.imr_multiaddr;
1505 
1506 		ssa->sin_family = AF_INET;
1507 		ssa->sin_len = sizeof(struct sockaddr_in);
1508 		ssa->sin_addr = mreqs.imr_sourceaddr;
1509 
1510 		if (!in_nullhost(mreqs.imr_interface)) {
1511 			ifp = ip_multicast_if(&mreqs.imr_interface, &ifindex);
1512 		}
1513 
1514 		if (sopt->sopt_name == IP_BLOCK_SOURCE) {
1515 			doblock = 1;
1516 		}
1517 
1518 		IGMP_INET_PRINTF(mreqs.imr_interface,
1519 		    ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__,
1520 		    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
1521 		break;
1522 	}
1523 
1524 	case MCAST_BLOCK_SOURCE:
1525 	case MCAST_UNBLOCK_SOURCE:
1526 		error = sooptcopyin(sopt, &gsr,
1527 		    sizeof(struct group_source_req),
1528 		    sizeof(struct group_source_req));
1529 		if (error) {
1530 			return error;
1531 		}
1532 
1533 		if (gsa->sin_family != AF_INET ||
1534 		    gsa->sin_len != sizeof(struct sockaddr_in)) {
1535 			return EINVAL;
1536 		}
1537 
1538 		if (ssa->sin_family != AF_INET ||
1539 		    ssa->sin_len != sizeof(struct sockaddr_in)) {
1540 			return EINVAL;
1541 		}
1542 
1543 		ifnet_head_lock_shared();
1544 		if (gsr.gsr_interface == 0 ||
1545 		    (u_int)if_index < gsr.gsr_interface) {
1546 			ifnet_head_done();
1547 			return EADDRNOTAVAIL;
1548 		}
1549 
1550 		ifp = ifindex2ifnet[gsr.gsr_interface];
1551 		ifnet_head_done();
1552 
1553 		if (ifp == NULL) {
1554 			return EADDRNOTAVAIL;
1555 		}
1556 
1557 		if (sopt->sopt_name == MCAST_BLOCK_SOURCE) {
1558 			doblock = 1;
1559 		}
1560 		break;
1561 
1562 	default:
1563 		IGMP_PRINTF(("%s: unknown sopt_name %d\n",
1564 		    __func__, sopt->sopt_name));
1565 		return EOPNOTSUPP;
1566 	}
1567 
1568 	if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
1569 		return EINVAL;
1570 	}
1571 
1572 	/*
1573 	 * Check if we are actually a member of this group.
1574 	 */
1575 	imo = inp_findmoptions(inp);
1576 	if (imo == NULL) {
1577 		return ENOMEM;
1578 	}
1579 
1580 	IMO_LOCK(imo);
1581 	idx = imo_match_group(imo, ifp, gsa);
1582 	if (idx == (size_t)-1 || imo->imo_mfilters == NULL) {
1583 		error = EADDRNOTAVAIL;
1584 		goto out_imo_locked;
1585 	}
1586 
1587 	VERIFY(imo->imo_mfilters != NULL);
1588 	imf = &imo->imo_mfilters[idx];
1589 	inm = imo->imo_membership[idx];
1590 
1591 	/*
1592 	 * Attempting to use the delta-based API on an
1593 	 * non exclusive-mode membership is an error.
1594 	 */
1595 	fmode = imf->imf_st[0];
1596 	if (fmode != MCAST_EXCLUDE) {
1597 		error = EINVAL;
1598 		goto out_imo_locked;
1599 	}
1600 
1601 	/*
1602 	 * Deal with error cases up-front:
1603 	 *  Asked to block, but already blocked; or
1604 	 *  Asked to unblock, but nothing to unblock.
1605 	 * If adding a new block entry, allocate it.
1606 	 */
1607 	ims = imo_match_source(imo, idx, ssa);
1608 	if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
1609 		IGMP_INET_PRINTF(ssa->sin_addr,
1610 		    ("%s: source %s %spresent\n", __func__,
1611 		    _igmp_inet_buf, doblock ? "" : "not "));
1612 		error = EADDRNOTAVAIL;
1613 		goto out_imo_locked;
1614 	}
1615 
1616 	/*
1617 	 * Begin state merge transaction at socket layer.
1618 	 */
1619 	if (doblock) {
1620 		IGMP_PRINTF(("%s: %s source\n", __func__, "block"));
1621 		ims = imf_graft(imf, fmode, ssa);
1622 		if (ims == NULL) {
1623 			error = ENOMEM;
1624 		}
1625 	} else {
1626 		IGMP_PRINTF(("%s: %s source\n", __func__, "allow"));
1627 		error = imf_prune(imf, ssa);
1628 	}
1629 
1630 	if (error) {
1631 		IGMP_PRINTF(("%s: merge imf state failed\n", __func__));
1632 		goto out_imf_rollback;
1633 	}
1634 
1635 	/*
1636 	 * Begin state merge transaction at IGMP layer.
1637 	 */
1638 	INM_LOCK(inm);
1639 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
1640 	error = inm_merge(inm, imf);
1641 	if (error) {
1642 		IGMP_PRINTF(("%s: failed to merge inm state\n", __func__));
1643 		INM_UNLOCK(inm);
1644 		goto out_imf_rollback;
1645 	}
1646 
1647 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
1648 	error = igmp_change_state(inm, &itp);
1649 	INM_UNLOCK(inm);
1650 #if IGMP_DEBUG
1651 	if (error) {
1652 		IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
1653 	}
1654 #endif
1655 
1656 out_imf_rollback:
1657 	if (error) {
1658 		imf_rollback(imf);
1659 	} else {
1660 		imf_commit(imf);
1661 	}
1662 
1663 	imf_reap(imf);
1664 
1665 out_imo_locked:
1666 	IMO_UNLOCK(imo);
1667 	IMO_REMREF(imo);        /* from inp_findmoptions() */
1668 
1669 	/* schedule timer now that we've dropped the lock(s) */
1670 	igmp_set_fast_timeout(&itp);
1671 
1672 	return error;
1673 }
1674 
1675 /*
1676  * Given an inpcb, return its multicast options structure pointer.
1677  *
1678  * Caller is responsible for locking the inpcb, and releasing the
1679  * extra reference held on the imo, upon a successful return.
1680  */
1681 static struct ip_moptions *
inp_findmoptions(struct inpcb * inp)1682 inp_findmoptions(struct inpcb *inp)
1683 {
1684 	struct ip_moptions       *imo;
1685 	struct in_multi         **immp;
1686 	struct in_mfilter        *imfp;
1687 	size_t                    idx;
1688 
1689 	if ((imo = inp->inp_moptions) != NULL) {
1690 		IMO_ADDREF(imo);        /* for caller */
1691 		return imo;
1692 	}
1693 
1694 	imo = ip_allocmoptions(Z_WAITOK);
1695 	if (imo == NULL) {
1696 		return NULL;
1697 	}
1698 
1699 	immp = kalloc_type(struct in_multi *, IP_MIN_MEMBERSHIPS,
1700 	    Z_WAITOK | Z_ZERO | Z_NOFAIL);
1701 	imfp = kalloc_type(struct in_mfilter, IP_MIN_MEMBERSHIPS,
1702 	    Z_WAITOK | Z_ZERO | Z_NOFAIL);
1703 
1704 	imo->imo_multicast_ifp = NULL;
1705 	imo->imo_multicast_addr.s_addr = INADDR_ANY;
1706 	imo->imo_multicast_vif = -1;
1707 	imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
1708 	imo->imo_multicast_loop = !!in_mcast_loop;
1709 	imo->imo_num_memberships = 0;
1710 	imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
1711 	imo->imo_membership = immp;
1712 
1713 	/* Initialize per-group source filters. */
1714 	for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) {
1715 		imf_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
1716 	}
1717 
1718 	imo->imo_mfilters = imfp;
1719 	inp->inp_moptions = imo; /* keep reference from ip_allocmoptions() */
1720 	IMO_ADDREF(imo);        /* for caller */
1721 
1722 	return imo;
1723 }
1724 /*
1725  * Atomically get source filters on a socket for an IPv4 multicast group.
1726  */
1727 static int
inp_get_source_filters(struct inpcb * inp,struct sockopt * sopt)1728 inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
1729 {
1730 	struct __msfilterreq64  msfr = {}, msfr64;
1731 	struct __msfilterreq32  msfr32;
1732 	struct sockaddr_in      *gsa;
1733 	struct ifnet            *ifp;
1734 	struct ip_moptions      *imo;
1735 	struct in_mfilter       *imf;
1736 	struct ip_msource       *ims;
1737 	struct in_msource       *lims;
1738 	struct sockaddr_in      *psin;
1739 	struct sockaddr_storage *ptss;
1740 	struct sockaddr_storage *tss;
1741 	int                      error;
1742 	size_t                   idx;
1743 	uint32_t                 nsrcs, ncsrcs;
1744 	user_addr_t              tmp_ptr;
1745 
1746 	imo = inp->inp_moptions;
1747 	VERIFY(imo != NULL);
1748 
1749 	int is_64bit_proc = IS_64BIT_PROCESS(current_proc());
1750 
1751 	if (is_64bit_proc) {
1752 		error = sooptcopyin(sopt, &msfr64,
1753 		    sizeof(struct __msfilterreq64),
1754 		    sizeof(struct __msfilterreq64));
1755 		if (error) {
1756 			return error;
1757 		}
1758 		/* we never use msfr.msfr_srcs; */
1759 		memcpy(&msfr, &msfr64, sizeof(msfr64));
1760 	} else {
1761 		error = sooptcopyin(sopt, &msfr32,
1762 		    sizeof(struct __msfilterreq32),
1763 		    sizeof(struct __msfilterreq32));
1764 		if (error) {
1765 			return error;
1766 		}
1767 		/* we never use msfr.msfr_srcs; */
1768 		memcpy(&msfr, &msfr32, sizeof(msfr32));
1769 	}
1770 
1771 	ifnet_head_lock_shared();
1772 	if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
1773 		ifnet_head_done();
1774 		return EADDRNOTAVAIL;
1775 	}
1776 
1777 	ifp = ifindex2ifnet[msfr.msfr_ifindex];
1778 	ifnet_head_done();
1779 
1780 	if (ifp == NULL) {
1781 		return EADDRNOTAVAIL;
1782 	}
1783 
1784 	if ((size_t) msfr.msfr_nsrcs >
1785 	    UINT32_MAX / sizeof(struct sockaddr_storage)) {
1786 		msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage);
1787 	}
1788 
1789 	if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) {
1790 		msfr.msfr_nsrcs = in_mcast_maxsocksrc;
1791 	}
1792 
1793 	IMO_LOCK(imo);
1794 	/*
1795 	 * Lookup group on the socket.
1796 	 */
1797 	gsa = (struct sockaddr_in *)&msfr.msfr_group;
1798 
1799 	idx = imo_match_group(imo, ifp, gsa);
1800 	if (idx == (size_t)-1 || imo->imo_mfilters == NULL) {
1801 		IMO_UNLOCK(imo);
1802 		return EADDRNOTAVAIL;
1803 	}
1804 	imf = &imo->imo_mfilters[idx];
1805 
1806 	/*
1807 	 * Ignore memberships which are in limbo.
1808 	 */
1809 	if (imf->imf_st[1] == MCAST_UNDEFINED) {
1810 		IMO_UNLOCK(imo);
1811 		return EAGAIN;
1812 	}
1813 	msfr.msfr_fmode = imf->imf_st[1];
1814 
1815 	/*
1816 	 * If the user specified a buffer, copy out the source filter
1817 	 * entries to userland gracefully.
1818 	 * We only copy out the number of entries which userland
1819 	 * has asked for, but we always tell userland how big the
1820 	 * buffer really needs to be.
1821 	 */
1822 
1823 	if (is_64bit_proc) {
1824 		tmp_ptr = CAST_USER_ADDR_T(msfr64.msfr_srcs);
1825 	} else {
1826 		tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
1827 	}
1828 
1829 	tss = NULL;
1830 	if (tmp_ptr != USER_ADDR_NULL && msfr.msfr_nsrcs > 0) {
1831 		tss = kalloc_data((size_t)msfr.msfr_nsrcs * sizeof(*tss),
1832 		    Z_WAITOK | Z_ZERO);
1833 		if (tss == NULL) {
1834 			IMO_UNLOCK(imo);
1835 			return ENOBUFS;
1836 		}
1837 	}
1838 
1839 	/*
1840 	 * Count number of sources in-mode at t0.
1841 	 * If buffer space exists and remains, copy out source entries.
1842 	 */
1843 	nsrcs = msfr.msfr_nsrcs;
1844 	ncsrcs = 0;
1845 	ptss = tss;
1846 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1847 		lims = (struct in_msource *)ims;
1848 		if (lims->imsl_st[0] == MCAST_UNDEFINED ||
1849 		    lims->imsl_st[0] != imf->imf_st[0]) {
1850 			continue;
1851 		}
1852 		if (tss != NULL && nsrcs > 0) {
1853 			psin = (struct sockaddr_in *)ptss;
1854 			psin->sin_family = AF_INET;
1855 			psin->sin_len = sizeof(struct sockaddr_in);
1856 			psin->sin_addr.s_addr = htonl(lims->ims_haddr);
1857 			psin->sin_port = 0;
1858 			++ptss;
1859 			--nsrcs;
1860 			++ncsrcs;
1861 		}
1862 	}
1863 
1864 	IMO_UNLOCK(imo);
1865 
1866 	if (tss != NULL) {
1867 		error = copyout(tss, CAST_USER_ADDR_T(tmp_ptr), ncsrcs * sizeof(*tss));
1868 		kfree_data(tss, (size_t)msfr.msfr_nsrcs * sizeof(*tss));
1869 		if (error) {
1870 			return error;
1871 		}
1872 	}
1873 
1874 	msfr.msfr_nsrcs = ncsrcs;
1875 	if (is_64bit_proc) {
1876 		msfr64.msfr_ifindex = msfr.msfr_ifindex;
1877 		msfr64.msfr_fmode   = msfr.msfr_fmode;
1878 		msfr64.msfr_nsrcs   = msfr.msfr_nsrcs;
1879 		memcpy(&msfr64.msfr_group, &msfr.msfr_group,
1880 		    sizeof(struct sockaddr_storage));
1881 		error = sooptcopyout(sopt, &msfr64,
1882 		    sizeof(struct __msfilterreq64));
1883 	} else {
1884 		msfr32.msfr_ifindex = msfr.msfr_ifindex;
1885 		msfr32.msfr_fmode   = msfr.msfr_fmode;
1886 		msfr32.msfr_nsrcs   = msfr.msfr_nsrcs;
1887 		memcpy(&msfr32.msfr_group, &msfr.msfr_group,
1888 		    sizeof(struct sockaddr_storage));
1889 		error = sooptcopyout(sopt, &msfr32,
1890 		    sizeof(struct __msfilterreq32));
1891 	}
1892 
1893 	return error;
1894 }
1895 
1896 /*
1897  * Return the IP multicast options in response to user getsockopt().
1898  */
1899 int
inp_getmoptions(struct inpcb * inp,struct sockopt * sopt)1900 inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
1901 {
1902 	struct ip_mreqn          mreqn;
1903 	struct ip_moptions      *imo;
1904 	struct ifnet            *ifp;
1905 	struct in_ifaddr        *ia;
1906 	int                      error, optval;
1907 	unsigned int             ifindex;
1908 	u_char                   coptval;
1909 
1910 	imo = inp->inp_moptions;
1911 	/*
1912 	 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
1913 	 * or is a divert socket, reject it.
1914 	 */
1915 	if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT ||
1916 	    (SOCK_TYPE(inp->inp_socket) != SOCK_RAW &&
1917 	    SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) {
1918 		return EOPNOTSUPP;
1919 	}
1920 
1921 	error = 0;
1922 	switch (sopt->sopt_name) {
1923 	case IP_MULTICAST_IF:
1924 		memset(&mreqn, 0, sizeof(struct ip_mreqn));
1925 		if (imo != NULL) {
1926 			IMO_LOCK(imo);
1927 			ifp = imo->imo_multicast_ifp;
1928 			if (!in_nullhost(imo->imo_multicast_addr)) {
1929 				mreqn.imr_address = imo->imo_multicast_addr;
1930 			} else if (ifp != NULL) {
1931 				mreqn.imr_ifindex = ifp->if_index;
1932 				IFP_TO_IA(ifp, ia);
1933 				if (ia != NULL) {
1934 					IFA_LOCK_SPIN(&ia->ia_ifa);
1935 					mreqn.imr_address =
1936 					    IA_SIN(ia)->sin_addr;
1937 					IFA_UNLOCK(&ia->ia_ifa);
1938 					IFA_REMREF(&ia->ia_ifa);
1939 				}
1940 			}
1941 			IMO_UNLOCK(imo);
1942 		}
1943 		if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
1944 			error = sooptcopyout(sopt, &mreqn,
1945 			    sizeof(struct ip_mreqn));
1946 		} else {
1947 			error = sooptcopyout(sopt, &mreqn.imr_address,
1948 			    sizeof(struct in_addr));
1949 		}
1950 		break;
1951 
1952 	case IP_MULTICAST_IFINDEX:
1953 		if (imo != NULL) {
1954 			IMO_LOCK(imo);
1955 		}
1956 		if (imo == NULL || imo->imo_multicast_ifp == NULL) {
1957 			ifindex = 0;
1958 		} else {
1959 			ifindex = imo->imo_multicast_ifp->if_index;
1960 		}
1961 		if (imo != NULL) {
1962 			IMO_UNLOCK(imo);
1963 		}
1964 		error = sooptcopyout(sopt, &ifindex, sizeof(ifindex));
1965 		break;
1966 
1967 	case IP_MULTICAST_TTL:
1968 		if (imo == NULL) {
1969 			optval = coptval = IP_DEFAULT_MULTICAST_TTL;
1970 		} else {
1971 			IMO_LOCK(imo);
1972 			optval = coptval = imo->imo_multicast_ttl;
1973 			IMO_UNLOCK(imo);
1974 		}
1975 		if (sopt->sopt_valsize == sizeof(u_char)) {
1976 			error = sooptcopyout(sopt, &coptval, sizeof(u_char));
1977 		} else {
1978 			error = sooptcopyout(sopt, &optval, sizeof(int));
1979 		}
1980 		break;
1981 
1982 	case IP_MULTICAST_LOOP:
1983 		if (imo == 0) {
1984 			optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
1985 		} else {
1986 			IMO_LOCK(imo);
1987 			optval = coptval = imo->imo_multicast_loop;
1988 			IMO_UNLOCK(imo);
1989 		}
1990 		if (sopt->sopt_valsize == sizeof(u_char)) {
1991 			error = sooptcopyout(sopt, &coptval, sizeof(u_char));
1992 		} else {
1993 			error = sooptcopyout(sopt, &optval, sizeof(int));
1994 		}
1995 		break;
1996 
1997 	case IP_MSFILTER:
1998 		if (imo == NULL) {
1999 			error = EADDRNOTAVAIL;
2000 		} else {
2001 			error = inp_get_source_filters(inp, sopt);
2002 		}
2003 		break;
2004 
2005 	default:
2006 		error = ENOPROTOOPT;
2007 		break;
2008 	}
2009 
2010 	return error;
2011 }
2012 
2013 /*
2014  * Look up the ifnet to use for a multicast group membership,
2015  * given the IPv4 address of an interface, and the IPv4 group address.
2016  *
2017  * This routine exists to support legacy multicast applications
2018  * which do not understand that multicast memberships are scoped to
2019  * specific physical links in the networking stack, or which need
2020  * to join link-scope groups before IPv4 addresses are configured.
2021  *
2022  * If inp is non-NULL and is bound to an interface, use this socket's
2023  * inp_boundif for any required routing table lookup.
2024  *
2025  * If the route lookup fails, attempt to use the first non-loopback
2026  * interface with multicast capability in the system as a
2027  * last resort. The legacy IPv4 ASM API requires that we do
2028  * this in order to allow groups to be joined when the routing
2029  * table has not yet been populated during boot.
2030  *
2031  * Returns NULL if no ifp could be found.
2032  *
2033  */
2034 static struct ifnet *
inp_lookup_mcast_ifp(const struct inpcb * inp,const struct sockaddr_in * gsin,const struct in_addr ina)2035 inp_lookup_mcast_ifp(const struct inpcb *inp,
2036     const struct sockaddr_in *gsin, const struct in_addr ina)
2037 {
2038 	struct ifnet    *ifp;
2039 	unsigned int     ifindex = 0;
2040 
2041 	VERIFY(gsin->sin_family == AF_INET);
2042 	VERIFY(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr)));
2043 
2044 	ifp = NULL;
2045 	if (!in_nullhost(ina)) {
2046 		struct in_addr new_ina;
2047 		memcpy(&new_ina, &ina, sizeof(struct in_addr));
2048 		ifp = ip_multicast_if(&new_ina, &ifindex);
2049 	} else {
2050 		struct route ro;
2051 		unsigned int ifscope = IFSCOPE_NONE;
2052 
2053 		if (inp != NULL && (inp->inp_flags & INP_BOUND_IF)) {
2054 			ifscope = inp->inp_boundifp->if_index;
2055 		}
2056 
2057 		bzero(&ro, sizeof(ro));
2058 		memcpy(&ro.ro_dst, gsin, sizeof(struct sockaddr_in));
2059 		rtalloc_scoped_ign(&ro, 0, ifscope);
2060 		if (ro.ro_rt != NULL) {
2061 			ifp = ro.ro_rt->rt_ifp;
2062 			VERIFY(ifp != NULL);
2063 		} else {
2064 			struct in_ifaddr *ia;
2065 			struct ifnet *mifp;
2066 
2067 			mifp = NULL;
2068 			lck_rw_lock_shared(&in_ifaddr_rwlock);
2069 			TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
2070 				IFA_LOCK_SPIN(&ia->ia_ifa);
2071 				mifp = ia->ia_ifp;
2072 				IFA_UNLOCK(&ia->ia_ifa);
2073 				if (!(mifp->if_flags & IFF_LOOPBACK) &&
2074 				    (mifp->if_flags & IFF_MULTICAST)) {
2075 					ifp = mifp;
2076 					break;
2077 				}
2078 			}
2079 			lck_rw_done(&in_ifaddr_rwlock);
2080 		}
2081 		ROUTE_RELEASE(&ro);
2082 	}
2083 
2084 	return ifp;
2085 }
2086 
2087 /*
2088  * Join an IPv4 multicast group, possibly with a source.
2089  *
2090  * NB: sopt->sopt_val might point to the kernel address space. This means that
2091  * we were called by the IPv6 stack due to the presence of an IPv6 v4 mapped
2092  * address. In this scenario, sopt_p points to kernproc and sooptcopyin() will
2093  * just issue an in-kernel memcpy.
2094  */
2095 int
inp_join_group(struct inpcb * inp,struct sockopt * sopt)2096 inp_join_group(struct inpcb *inp, struct sockopt *sopt)
2097 {
2098 	struct group_source_req          gsr;
2099 	struct sockaddr_in              *gsa, *ssa;
2100 	struct ifnet                    *ifp;
2101 	struct in_mfilter               *imf;
2102 	struct ip_moptions              *imo;
2103 	struct in_multi                 *inm = NULL;
2104 	struct in_msource               *lims;
2105 	size_t                           idx;
2106 	int                              error, is_new;
2107 	struct igmp_tparams              itp;
2108 
2109 	bzero(&itp, sizeof(itp));
2110 	ifp = NULL;
2111 	imf = NULL;
2112 	error = 0;
2113 	is_new = 0;
2114 
2115 	memset(&gsr, 0, sizeof(struct group_source_req));
2116 	gsa = (struct sockaddr_in *)&gsr.gsr_group;
2117 	gsa->sin_family = AF_UNSPEC;
2118 	ssa = (struct sockaddr_in *)&gsr.gsr_source;
2119 	ssa->sin_family = AF_UNSPEC;
2120 
2121 	switch (sopt->sopt_name) {
2122 	case IP_ADD_MEMBERSHIP:
2123 	case IP_ADD_SOURCE_MEMBERSHIP: {
2124 		struct ip_mreq_source    mreqs;
2125 
2126 		if (sopt->sopt_name == IP_ADD_MEMBERSHIP) {
2127 			error = sooptcopyin(sopt, &mreqs,
2128 			    sizeof(struct ip_mreq),
2129 			    sizeof(struct ip_mreq));
2130 			/*
2131 			 * Do argument switcharoo from ip_mreq into
2132 			 * ip_mreq_source to avoid using two instances.
2133 			 */
2134 			mreqs.imr_interface = mreqs.imr_sourceaddr;
2135 			mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
2136 		} else if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
2137 			error = sooptcopyin(sopt, &mreqs,
2138 			    sizeof(struct ip_mreq_source),
2139 			    sizeof(struct ip_mreq_source));
2140 		}
2141 		if (error) {
2142 			IGMP_PRINTF(("%s: error copyin IP_ADD_MEMBERSHIP/"
2143 			    "IP_ADD_SOURCE_MEMBERSHIP %d err=%d\n",
2144 			    __func__, sopt->sopt_name, error));
2145 			return error;
2146 		}
2147 
2148 		gsa->sin_family = AF_INET;
2149 		gsa->sin_len = sizeof(struct sockaddr_in);
2150 		gsa->sin_addr = mreqs.imr_multiaddr;
2151 
2152 		if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
2153 			ssa->sin_family = AF_INET;
2154 			ssa->sin_len = sizeof(struct sockaddr_in);
2155 			ssa->sin_addr = mreqs.imr_sourceaddr;
2156 		}
2157 
2158 		if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2159 			return EINVAL;
2160 		}
2161 
2162 		ifp = inp_lookup_mcast_ifp(inp, gsa, mreqs.imr_interface);
2163 		IGMP_INET_PRINTF(mreqs.imr_interface,
2164 		    ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__,
2165 		    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
2166 		break;
2167 	}
2168 
2169 	case MCAST_JOIN_GROUP:
2170 	case MCAST_JOIN_SOURCE_GROUP:
2171 		if (sopt->sopt_name == MCAST_JOIN_GROUP) {
2172 			error = sooptcopyin(sopt, &gsr,
2173 			    sizeof(struct group_req),
2174 			    sizeof(struct group_req));
2175 		} else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
2176 			error = sooptcopyin(sopt, &gsr,
2177 			    sizeof(struct group_source_req),
2178 			    sizeof(struct group_source_req));
2179 		}
2180 		if (error) {
2181 			return error;
2182 		}
2183 
2184 		if (gsa->sin_family != AF_INET ||
2185 		    gsa->sin_len != sizeof(struct sockaddr_in)) {
2186 			return EINVAL;
2187 		}
2188 
2189 		/*
2190 		 * Overwrite the port field if present, as the sockaddr
2191 		 * being copied in may be matched with a binary comparison.
2192 		 */
2193 		gsa->sin_port = 0;
2194 		if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
2195 			if (ssa->sin_family != AF_INET ||
2196 			    ssa->sin_len != sizeof(struct sockaddr_in)) {
2197 				return EINVAL;
2198 			}
2199 			ssa->sin_port = 0;
2200 		}
2201 
2202 		if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2203 			return EINVAL;
2204 		}
2205 
2206 		ifnet_head_lock_shared();
2207 		if (gsr.gsr_interface == 0 ||
2208 		    (u_int)if_index < gsr.gsr_interface) {
2209 			ifnet_head_done();
2210 			return EADDRNOTAVAIL;
2211 		}
2212 		ifp = ifindex2ifnet[gsr.gsr_interface];
2213 		ifnet_head_done();
2214 		if (ifp == NULL) {
2215 			return EADDRNOTAVAIL;
2216 		}
2217 		break;
2218 
2219 	default:
2220 		IGMP_PRINTF(("%s: unknown sopt_name %d\n",
2221 		    __func__, sopt->sopt_name));
2222 		return EOPNOTSUPP;
2223 	}
2224 
2225 	if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
2226 		return EADDRNOTAVAIL;
2227 	}
2228 
2229 	INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_total);
2230 	/*
2231 	 * TBD: revisit the criteria for non-OS initiated joins
2232 	 */
2233 	if (inp->inp_lport == htons(5353)) {
2234 		INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_os_total);
2235 	}
2236 
2237 	imo = inp_findmoptions(inp);
2238 	if (imo == NULL) {
2239 		return ENOMEM;
2240 	}
2241 
2242 	IMO_LOCK(imo);
2243 	idx = imo_match_group(imo, ifp, gsa);
2244 	if (idx == (size_t)-1) {
2245 		is_new = 1;
2246 	} else {
2247 		inm = imo->imo_membership[idx];
2248 		imf = &imo->imo_mfilters[idx];
2249 		if (ssa->sin_family != AF_UNSPEC) {
2250 			/*
2251 			 * MCAST_JOIN_SOURCE_GROUP on an exclusive membership
2252 			 * is an error. On an existing inclusive membership,
2253 			 * it just adds the source to the filter list.
2254 			 */
2255 			if (imf->imf_st[1] != MCAST_INCLUDE) {
2256 				error = EINVAL;
2257 				goto out_imo_locked;
2258 			}
2259 			/*
2260 			 * Throw out duplicates.
2261 			 *
2262 			 * XXX FIXME: This makes a naive assumption that
2263 			 * even if entries exist for *ssa in this imf,
2264 			 * they will be rejected as dupes, even if they
2265 			 * are not valid in the current mode (in-mode).
2266 			 *
2267 			 * in_msource is transactioned just as for anything
2268 			 * else in SSM -- but note naive use of inm_graft()
2269 			 * below for allocating new filter entries.
2270 			 *
2271 			 * This is only an issue if someone mixes the
2272 			 * full-state SSM API with the delta-based API,
2273 			 * which is discouraged in the relevant RFCs.
2274 			 */
2275 			lims = imo_match_source(imo, idx, ssa);
2276 			if (lims != NULL /*&&
2277 			                  *  lims->imsl_st[1] == MCAST_INCLUDE*/) {
2278 				error = EADDRNOTAVAIL;
2279 				goto out_imo_locked;
2280 			}
2281 		} else {
2282 			/*
2283 			 * MCAST_JOIN_GROUP on an existing exclusive
2284 			 * membership is an error; return EADDRINUSE
2285 			 * to preserve 4.4BSD API idempotence, and
2286 			 * avoid tedious detour to code below.
2287 			 * NOTE: This is bending RFC 3678 a bit.
2288 			 *
2289 			 * On an existing inclusive membership, this is also
2290 			 * an error; if you want to change filter mode,
2291 			 * you must use the userland API setsourcefilter().
2292 			 * XXX We don't reject this for imf in UNDEFINED
2293 			 * state at t1, because allocation of a filter
2294 			 * is atomic with allocation of a membership.
2295 			 */
2296 			error = EINVAL;
2297 			/* See comments above for EADDRINUSE */
2298 			if (imf->imf_st[1] == MCAST_EXCLUDE) {
2299 				error = EADDRINUSE;
2300 			}
2301 			goto out_imo_locked;
2302 		}
2303 	}
2304 
2305 	/*
2306 	 * Begin state merge transaction at socket layer.
2307 	 */
2308 
2309 	if (is_new) {
2310 		if (imo->imo_num_memberships == imo->imo_max_memberships) {
2311 			error = imo_grow(imo, 0);
2312 			if (error) {
2313 				goto out_imo_locked;
2314 			}
2315 		}
2316 		/*
2317 		 * Allocate the new slot upfront so we can deal with
2318 		 * grafting the new source filter in same code path
2319 		 * as for join-source on existing membership.
2320 		 */
2321 		idx = imo->imo_num_memberships;
2322 		imo->imo_membership[idx] = NULL;
2323 		imo->imo_num_memberships++;
2324 		VERIFY(imo->imo_mfilters != NULL);
2325 		imf = &imo->imo_mfilters[idx];
2326 		VERIFY(RB_EMPTY(&imf->imf_sources));
2327 	}
2328 
2329 	/*
2330 	 * Graft new source into filter list for this inpcb's
2331 	 * membership of the group. The in_multi may not have
2332 	 * been allocated yet if this is a new membership, however,
2333 	 * the in_mfilter slot will be allocated and must be initialized.
2334 	 */
2335 	if (ssa->sin_family != AF_UNSPEC) {
2336 		/* Membership starts in IN mode */
2337 		if (is_new) {
2338 			IGMP_PRINTF(("%s: new join w/source\n", __func__));
2339 			imf_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE);
2340 		} else {
2341 			IGMP_PRINTF(("%s: %s source\n", __func__, "allow"));
2342 		}
2343 		lims = imf_graft(imf, MCAST_INCLUDE, ssa);
2344 		if (lims == NULL) {
2345 			IGMP_PRINTF(("%s: merge imf state failed\n",
2346 			    __func__));
2347 			error = ENOMEM;
2348 			goto out_imo_free;
2349 		}
2350 	} else {
2351 		/* No address specified; Membership starts in EX mode */
2352 		if (is_new) {
2353 			IGMP_PRINTF(("%s: new join w/o source\n", __func__));
2354 			imf_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE);
2355 		}
2356 	}
2357 
2358 	/*
2359 	 * Begin state merge transaction at IGMP layer.
2360 	 */
2361 	if (is_new) {
2362 		VERIFY(inm == NULL);
2363 		error = in_joingroup(ifp, &gsa->sin_addr, imf, &inm);
2364 
2365 		VERIFY(inm != NULL || error != 0);
2366 		if (error) {
2367 			goto out_imo_free;
2368 		}
2369 		imo->imo_membership[idx] = inm; /* from in_joingroup() */
2370 	} else {
2371 		IGMP_PRINTF(("%s: merge inm state\n", __func__));
2372 		INM_LOCK(inm);
2373 		error = inm_merge(inm, imf);
2374 		if (error) {
2375 			IGMP_PRINTF(("%s: failed to merge inm state\n",
2376 			    __func__));
2377 			INM_UNLOCK(inm);
2378 			goto out_imf_rollback;
2379 		}
2380 		IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
2381 		error = igmp_change_state(inm, &itp);
2382 		INM_UNLOCK(inm);
2383 		if (error) {
2384 			IGMP_PRINTF(("%s: failed igmp downcall\n",
2385 			    __func__));
2386 			goto out_imf_rollback;
2387 		}
2388 	}
2389 
2390 out_imf_rollback:
2391 	if (error) {
2392 		imf_rollback(imf);
2393 		if (is_new) {
2394 			imf_purge(imf);
2395 		} else {
2396 			imf_reap(imf);
2397 		}
2398 	} else {
2399 		imf_commit(imf);
2400 	}
2401 
2402 out_imo_free:
2403 	if (error && is_new) {
2404 		VERIFY(inm == NULL);
2405 		imo->imo_membership[idx] = NULL;
2406 		--imo->imo_num_memberships;
2407 	}
2408 
2409 out_imo_locked:
2410 	IMO_UNLOCK(imo);
2411 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2412 
2413 	/* schedule timer now that we've dropped the lock(s) */
2414 	igmp_set_fast_timeout(&itp);
2415 
2416 	return error;
2417 }
2418 
2419 /*
2420  * Leave an IPv4 multicast group on an inpcb, possibly with a source.
2421  *
2422  * NB: sopt->sopt_val might point to the kernel address space. Refer to the
2423  * block comment on top of inp_join_group() for more information.
2424  */
2425 int
inp_leave_group(struct inpcb * inp,struct sockopt * sopt)2426 inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
2427 {
2428 	struct group_source_req          gsr;
2429 	struct ip_mreq_source            mreqs;
2430 	struct sockaddr_in              *gsa, *ssa;
2431 	struct ifnet                    *ifp;
2432 	struct in_mfilter               *imf;
2433 	struct ip_moptions              *imo;
2434 	struct in_msource               *ims;
2435 	struct in_multi                 *inm = NULL;
2436 	size_t                           idx;
2437 	int                              error, is_final;
2438 	unsigned int                     ifindex = 0;
2439 	struct igmp_tparams              itp;
2440 
2441 	bzero(&itp, sizeof(itp));
2442 	ifp = NULL;
2443 	error = 0;
2444 	is_final = 1;
2445 
2446 	memset(&gsr, 0, sizeof(struct group_source_req));
2447 	gsa = (struct sockaddr_in *)&gsr.gsr_group;
2448 	ssa = (struct sockaddr_in *)&gsr.gsr_source;
2449 
2450 	switch (sopt->sopt_name) {
2451 	case IP_DROP_MEMBERSHIP:
2452 	case IP_DROP_SOURCE_MEMBERSHIP:
2453 		if (sopt->sopt_name == IP_DROP_MEMBERSHIP) {
2454 			error = sooptcopyin(sopt, &mreqs,
2455 			    sizeof(struct ip_mreq),
2456 			    sizeof(struct ip_mreq));
2457 			/*
2458 			 * Swap interface and sourceaddr arguments,
2459 			 * as ip_mreq and ip_mreq_source are laid
2460 			 * out differently.
2461 			 */
2462 			mreqs.imr_interface = mreqs.imr_sourceaddr;
2463 			mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
2464 		} else if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
2465 			error = sooptcopyin(sopt, &mreqs,
2466 			    sizeof(struct ip_mreq_source),
2467 			    sizeof(struct ip_mreq_source));
2468 		}
2469 		if (error) {
2470 			return error;
2471 		}
2472 
2473 		gsa->sin_family = AF_INET;
2474 		gsa->sin_len = sizeof(struct sockaddr_in);
2475 		gsa->sin_addr = mreqs.imr_multiaddr;
2476 
2477 		if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
2478 			ssa->sin_family = AF_INET;
2479 			ssa->sin_len = sizeof(struct sockaddr_in);
2480 			ssa->sin_addr = mreqs.imr_sourceaddr;
2481 		}
2482 		/*
2483 		 * Attempt to look up hinted ifp from interface address.
2484 		 * Fallthrough with null ifp iff lookup fails, to
2485 		 * preserve 4.4BSD mcast API idempotence.
2486 		 * XXX NOTE WELL: The RFC 3678 API is preferred because
2487 		 * using an IPv4 address as a key is racy.
2488 		 */
2489 		if (!in_nullhost(mreqs.imr_interface)) {
2490 			ifp = ip_multicast_if(&mreqs.imr_interface, &ifindex);
2491 		}
2492 
2493 		IGMP_INET_PRINTF(mreqs.imr_interface,
2494 		    ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__,
2495 		    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
2496 
2497 		break;
2498 
2499 	case MCAST_LEAVE_GROUP:
2500 	case MCAST_LEAVE_SOURCE_GROUP:
2501 		if (sopt->sopt_name == MCAST_LEAVE_GROUP) {
2502 			error = sooptcopyin(sopt, &gsr,
2503 			    sizeof(struct group_req),
2504 			    sizeof(struct group_req));
2505 		} else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2506 			error = sooptcopyin(sopt, &gsr,
2507 			    sizeof(struct group_source_req),
2508 			    sizeof(struct group_source_req));
2509 		}
2510 		if (error) {
2511 			return error;
2512 		}
2513 
2514 		if (gsa->sin_family != AF_INET ||
2515 		    gsa->sin_len != sizeof(struct sockaddr_in)) {
2516 			return EINVAL;
2517 		}
2518 
2519 		if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2520 			if (ssa->sin_family != AF_INET ||
2521 			    ssa->sin_len != sizeof(struct sockaddr_in)) {
2522 				return EINVAL;
2523 			}
2524 		}
2525 
2526 		ifnet_head_lock_shared();
2527 		if (gsr.gsr_interface == 0 ||
2528 		    (u_int)if_index < gsr.gsr_interface) {
2529 			ifnet_head_done();
2530 			return EADDRNOTAVAIL;
2531 		}
2532 
2533 		ifp = ifindex2ifnet[gsr.gsr_interface];
2534 		ifnet_head_done();
2535 		if (ifp == NULL) {
2536 			return EADDRNOTAVAIL;
2537 		}
2538 		break;
2539 
2540 	default:
2541 		IGMP_PRINTF(("%s: unknown sopt_name %d\n",
2542 		    __func__, sopt->sopt_name));
2543 		return EOPNOTSUPP;
2544 	}
2545 
2546 	if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2547 		return EINVAL;
2548 	}
2549 
2550 	/*
2551 	 * Find the membership in the membership array.
2552 	 */
2553 	imo = inp_findmoptions(inp);
2554 	if (imo == NULL) {
2555 		return ENOMEM;
2556 	}
2557 
2558 	IMO_LOCK(imo);
2559 	idx = imo_match_group(imo, ifp, gsa);
2560 	if (idx == (size_t)-1) {
2561 		error = EADDRNOTAVAIL;
2562 		goto out_locked;
2563 	}
2564 	inm = imo->imo_membership[idx];
2565 	if (inm == NULL) {
2566 		error = EINVAL;
2567 		goto out_locked;
2568 	}
2569 	imf = &imo->imo_mfilters[idx];
2570 
2571 	if (ssa->sin_family != AF_UNSPEC) {
2572 		IGMP_PRINTF(("%s: opt=%d is_final=0\n", __func__,
2573 		    sopt->sopt_name));
2574 		is_final = 0;
2575 	}
2576 
2577 	/*
2578 	 * Begin state merge transaction at socket layer.
2579 	 */
2580 
2581 	/*
2582 	 * If we were instructed only to leave a given source, do so.
2583 	 * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
2584 	 */
2585 	if (is_final) {
2586 		imf_leave(imf);
2587 	} else {
2588 		if (imf->imf_st[0] == MCAST_EXCLUDE) {
2589 			error = EADDRNOTAVAIL;
2590 			goto out_locked;
2591 		}
2592 		ims = imo_match_source(imo, idx, ssa);
2593 		if (ims == NULL) {
2594 			IGMP_INET_PRINTF(ssa->sin_addr,
2595 			    ("%s: source %s %spresent\n", __func__,
2596 			    _igmp_inet_buf, "not "));
2597 			error = EADDRNOTAVAIL;
2598 			goto out_locked;
2599 		}
2600 		IGMP_PRINTF(("%s: %s source\n", __func__, "block"));
2601 		error = imf_prune(imf, ssa);
2602 		if (error) {
2603 			IGMP_PRINTF(("%s: merge imf state failed\n",
2604 			    __func__));
2605 			goto out_locked;
2606 		}
2607 	}
2608 
2609 	/*
2610 	 * Begin state merge transaction at IGMP layer.
2611 	 */
2612 	if (is_final) {
2613 		/*
2614 		 * Give up the multicast address record to which
2615 		 * the membership points.  Reference held in imo
2616 		 * will be released below.
2617 		 */
2618 		(void) in_leavegroup(inm, imf);
2619 	} else {
2620 		IGMP_PRINTF(("%s: merge inm state\n", __func__));
2621 		INM_LOCK(inm);
2622 		error = inm_merge(inm, imf);
2623 		if (error) {
2624 			IGMP_PRINTF(("%s: failed to merge inm state\n",
2625 			    __func__));
2626 			INM_UNLOCK(inm);
2627 			goto out_imf_rollback;
2628 		}
2629 
2630 		IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
2631 		error = igmp_change_state(inm, &itp);
2632 		if (error) {
2633 			IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
2634 		}
2635 		INM_UNLOCK(inm);
2636 	}
2637 
2638 out_imf_rollback:
2639 	if (error) {
2640 		imf_rollback(imf);
2641 	} else {
2642 		imf_commit(imf);
2643 	}
2644 
2645 	imf_reap(imf);
2646 
2647 	if (is_final) {
2648 		/* Remove the gap in the membership array and filter array. */
2649 		VERIFY(inm == imo->imo_membership[idx]);
2650 
2651 		INM_REMREF(inm);
2652 
2653 		for (++idx; idx < imo->imo_num_memberships; ++idx) {
2654 			imo->imo_membership[idx - 1] = imo->imo_membership[idx];
2655 			imo->imo_mfilters[idx - 1] = imo->imo_mfilters[idx];
2656 		}
2657 		imo->imo_num_memberships--;
2658 
2659 		/* Re-initialize the now unused tail of the list */
2660 		imo->imo_membership[imo->imo_num_memberships] = NULL;
2661 		imf_init(&imo->imo_mfilters[imo->imo_num_memberships], MCAST_UNDEFINED, MCAST_EXCLUDE);
2662 	}
2663 
2664 out_locked:
2665 	IMO_UNLOCK(imo);
2666 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2667 
2668 	/* schedule timer now that we've dropped the lock(s) */
2669 	igmp_set_fast_timeout(&itp);
2670 
2671 	return error;
2672 }
2673 
2674 /*
2675  * Select the interface for transmitting IPv4 multicast datagrams.
2676  *
2677  * Either an instance of struct in_addr or an instance of struct ip_mreqn
2678  * may be passed to this socket option. An address of INADDR_ANY or an
2679  * interface index of 0 is used to remove a previous selection.
2680  * When no interface is selected, one is chosen for every send.
2681  */
2682 static int
inp_set_multicast_if(struct inpcb * inp,struct sockopt * sopt)2683 inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
2684 {
2685 	struct in_addr           addr;
2686 	struct ip_mreqn          mreqn;
2687 	struct ifnet            *ifp;
2688 	struct ip_moptions      *imo;
2689 	int                      error = 0;
2690 	unsigned int             ifindex = 0;
2691 
2692 	bzero(&addr, sizeof(addr));
2693 	if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
2694 		/*
2695 		 * An interface index was specified using the
2696 		 * Linux-derived ip_mreqn structure.
2697 		 */
2698 		error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn),
2699 		    sizeof(struct ip_mreqn));
2700 		if (error) {
2701 			return error;
2702 		}
2703 
2704 		ifnet_head_lock_shared();
2705 		if (mreqn.imr_ifindex < 0 || if_index < mreqn.imr_ifindex) {
2706 			ifnet_head_done();
2707 			return EINVAL;
2708 		}
2709 
2710 		if (mreqn.imr_ifindex == 0) {
2711 			ifp = NULL;
2712 		} else {
2713 			ifp = ifindex2ifnet[mreqn.imr_ifindex];
2714 			if (ifp == NULL) {
2715 				ifnet_head_done();
2716 				return EADDRNOTAVAIL;
2717 			}
2718 		}
2719 		ifnet_head_done();
2720 	} else {
2721 		/*
2722 		 * An interface was specified by IPv4 address.
2723 		 * This is the traditional BSD usage.
2724 		 */
2725 		error = sooptcopyin(sopt, &addr, sizeof(struct in_addr),
2726 		    sizeof(struct in_addr));
2727 		if (error) {
2728 			return error;
2729 		}
2730 		if (in_nullhost(addr)) {
2731 			ifp = NULL;
2732 		} else {
2733 			ifp = ip_multicast_if(&addr, &ifindex);
2734 			if (ifp == NULL) {
2735 				IGMP_INET_PRINTF(addr,
2736 				    ("%s: can't find ifp for addr=%s\n",
2737 				    __func__, _igmp_inet_buf));
2738 				return EADDRNOTAVAIL;
2739 			}
2740 		}
2741 	}
2742 
2743 	/* Reject interfaces which do not support multicast. */
2744 	if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0) {
2745 		return EOPNOTSUPP;
2746 	}
2747 
2748 	imo = inp_findmoptions(inp);
2749 	if (imo == NULL) {
2750 		return ENOMEM;
2751 	}
2752 
2753 	IMO_LOCK(imo);
2754 	imo->imo_multicast_ifp = ifp;
2755 	if (ifindex) {
2756 		imo->imo_multicast_addr = addr;
2757 	} else {
2758 		imo->imo_multicast_addr.s_addr = INADDR_ANY;
2759 	}
2760 	IMO_UNLOCK(imo);
2761 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2762 
2763 	return 0;
2764 }
2765 
2766 /*
2767  * Atomically set source filters on a socket for an IPv4 multicast group.
2768  */
2769 static int
inp_set_source_filters(struct inpcb * inp,struct sockopt * sopt)2770 inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
2771 {
2772 	struct __msfilterreq64   msfr = {}, msfr64;
2773 	struct __msfilterreq32   msfr32;
2774 	struct sockaddr_in      *gsa;
2775 	struct ifnet            *ifp;
2776 	struct in_mfilter       *imf;
2777 	struct ip_moptions      *imo;
2778 	struct in_multi         *inm;
2779 	size_t                   idx;
2780 	int                      error;
2781 	uint64_t                 tmp_ptr;
2782 	struct igmp_tparams      itp;
2783 
2784 	bzero(&itp, sizeof(itp));
2785 
2786 	int is_64bit_proc = IS_64BIT_PROCESS(current_proc());
2787 
2788 	if (is_64bit_proc) {
2789 		error = sooptcopyin(sopt, &msfr64,
2790 		    sizeof(struct __msfilterreq64),
2791 		    sizeof(struct __msfilterreq64));
2792 		if (error) {
2793 			return error;
2794 		}
2795 		/* we never use msfr.msfr_srcs; */
2796 		memcpy(&msfr, &msfr64, sizeof(msfr64));
2797 	} else {
2798 		error = sooptcopyin(sopt, &msfr32,
2799 		    sizeof(struct __msfilterreq32),
2800 		    sizeof(struct __msfilterreq32));
2801 		if (error) {
2802 			return error;
2803 		}
2804 		/* we never use msfr.msfr_srcs; */
2805 		memcpy(&msfr, &msfr32, sizeof(msfr32));
2806 	}
2807 
2808 	if ((size_t) msfr.msfr_nsrcs >
2809 	    UINT32_MAX / sizeof(struct sockaddr_storage)) {
2810 		msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage);
2811 	}
2812 
2813 	if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) {
2814 		return ENOBUFS;
2815 	}
2816 
2817 	if ((msfr.msfr_fmode != MCAST_EXCLUDE &&
2818 	    msfr.msfr_fmode != MCAST_INCLUDE)) {
2819 		return EINVAL;
2820 	}
2821 
2822 	if (msfr.msfr_group.ss_family != AF_INET ||
2823 	    msfr.msfr_group.ss_len != sizeof(struct sockaddr_in)) {
2824 		return EINVAL;
2825 	}
2826 
2827 	gsa = (struct sockaddr_in *)&msfr.msfr_group;
2828 	if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2829 		return EINVAL;
2830 	}
2831 
2832 	gsa->sin_port = 0;      /* ignore port */
2833 
2834 	ifnet_head_lock_shared();
2835 	if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
2836 		ifnet_head_done();
2837 		return EADDRNOTAVAIL;
2838 	}
2839 
2840 	ifp = ifindex2ifnet[msfr.msfr_ifindex];
2841 	ifnet_head_done();
2842 	if (ifp == NULL) {
2843 		return EADDRNOTAVAIL;
2844 	}
2845 
2846 	/*
2847 	 * Check if this socket is a member of this group.
2848 	 */
2849 	imo = inp_findmoptions(inp);
2850 	if (imo == NULL) {
2851 		return ENOMEM;
2852 	}
2853 
2854 	IMO_LOCK(imo);
2855 	idx = imo_match_group(imo, ifp, gsa);
2856 	if (idx == (size_t)-1 || imo->imo_mfilters == NULL) {
2857 		error = EADDRNOTAVAIL;
2858 		goto out_imo_locked;
2859 	}
2860 	inm = imo->imo_membership[idx];
2861 	imf = &imo->imo_mfilters[idx];
2862 
2863 	/*
2864 	 * Begin state merge transaction at socket layer.
2865 	 */
2866 
2867 	imf->imf_st[1] = (uint8_t)msfr.msfr_fmode;
2868 
2869 	/*
2870 	 * Apply any new source filters, if present.
2871 	 * Make a copy of the user-space source vector so
2872 	 * that we may copy them with a single copyin. This
2873 	 * allows us to deal with page faults up-front.
2874 	 */
2875 	if (msfr.msfr_nsrcs > 0) {
2876 		struct in_msource       *lims;
2877 		struct sockaddr_in      *psin;
2878 		struct sockaddr_storage *kss, *pkss;
2879 		int                      i;
2880 
2881 		if (is_64bit_proc) {
2882 			tmp_ptr = msfr64.msfr_srcs;
2883 		} else {
2884 			tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
2885 		}
2886 
2887 		IGMP_PRINTF(("%s: loading %lu source list entries\n",
2888 		    __func__, (unsigned long)msfr.msfr_nsrcs));
2889 		kss = kalloc_data((size_t)msfr.msfr_nsrcs * sizeof(*kss), Z_WAITOK);
2890 		if (kss == NULL) {
2891 			error = ENOMEM;
2892 			goto out_imo_locked;
2893 		}
2894 		error = copyin(CAST_USER_ADDR_T(tmp_ptr), kss,
2895 		    (size_t) msfr.msfr_nsrcs * sizeof(*kss));
2896 		if (error) {
2897 			kfree_data(kss, (size_t)msfr.msfr_nsrcs * sizeof(*kss));
2898 			goto out_imo_locked;
2899 		}
2900 
2901 		/*
2902 		 * Mark all source filters as UNDEFINED at t1.
2903 		 * Restore new group filter mode, as imf_leave()
2904 		 * will set it to INCLUDE.
2905 		 */
2906 		imf_leave(imf);
2907 		imf->imf_st[1] = (uint8_t)msfr.msfr_fmode;
2908 
2909 		/*
2910 		 * Update socket layer filters at t1, lazy-allocating
2911 		 * new entries. This saves a bunch of memory at the
2912 		 * cost of one RB_FIND() per source entry; duplicate
2913 		 * entries in the msfr_nsrcs vector are ignored.
2914 		 * If we encounter an error, rollback transaction.
2915 		 *
2916 		 * XXX This too could be replaced with a set-symmetric
2917 		 * difference like loop to avoid walking from root
2918 		 * every time, as the key space is common.
2919 		 */
2920 		for (i = 0, pkss = kss; (u_int)i < msfr.msfr_nsrcs;
2921 		    i++, pkss++) {
2922 			psin = (struct sockaddr_in *)pkss;
2923 			if (psin->sin_family != AF_INET) {
2924 				error = EAFNOSUPPORT;
2925 				break;
2926 			}
2927 			if (psin->sin_len != sizeof(struct sockaddr_in)) {
2928 				error = EINVAL;
2929 				break;
2930 			}
2931 			error = imf_get_source(imf, psin, &lims);
2932 			if (error) {
2933 				break;
2934 			}
2935 			lims->imsl_st[1] = imf->imf_st[1];
2936 		}
2937 		kfree_data(kss, (size_t)msfr.msfr_nsrcs * sizeof(*kss));
2938 	}
2939 
2940 	if (error) {
2941 		goto out_imf_rollback;
2942 	}
2943 
2944 	/*
2945 	 * Begin state merge transaction at IGMP layer.
2946 	 */
2947 	INM_LOCK(inm);
2948 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
2949 	error = inm_merge(inm, imf);
2950 	if (error) {
2951 		IGMP_PRINTF(("%s: failed to merge inm state\n", __func__));
2952 		INM_UNLOCK(inm);
2953 		goto out_imf_rollback;
2954 	}
2955 
2956 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
2957 	error = igmp_change_state(inm, &itp);
2958 	INM_UNLOCK(inm);
2959 #ifdef IGMP_DEBUG
2960 	if (error) {
2961 		IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
2962 	}
2963 #endif
2964 
2965 out_imf_rollback:
2966 	if (error) {
2967 		imf_rollback(imf);
2968 	} else {
2969 		imf_commit(imf);
2970 	}
2971 
2972 	imf_reap(imf);
2973 
2974 out_imo_locked:
2975 	IMO_UNLOCK(imo);
2976 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2977 
2978 	/* schedule timer now that we've dropped the lock(s) */
2979 	igmp_set_fast_timeout(&itp);
2980 
2981 	return error;
2982 }
2983 
2984 /*
2985  * Set the IP multicast options in response to user setsockopt().
2986  *
2987  * Many of the socket options handled in this function duplicate the
2988  * functionality of socket options in the regular unicast API. However,
2989  * it is not possible to merge the duplicate code, because the idempotence
2990  * of the IPv4 multicast part of the BSD Sockets API must be preserved;
2991  * the effects of these options must be treated as separate and distinct.
2992  */
2993 int
inp_setmoptions(struct inpcb * inp,struct sockopt * sopt)2994 inp_setmoptions(struct inpcb *inp, struct sockopt *sopt)
2995 {
2996 	struct ip_moptions      *imo;
2997 	int                      error;
2998 	unsigned int             ifindex;
2999 	struct ifnet            *ifp;
3000 
3001 	error = 0;
3002 
3003 	/*
3004 	 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
3005 	 * or is a divert socket, reject it.
3006 	 */
3007 	if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT ||
3008 	    (SOCK_TYPE(inp->inp_socket) != SOCK_RAW &&
3009 	    SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) {
3010 		return EOPNOTSUPP;
3011 	}
3012 
3013 	switch (sopt->sopt_name) {
3014 	case IP_MULTICAST_IF:
3015 		error = inp_set_multicast_if(inp, sopt);
3016 		break;
3017 
3018 	case IP_MULTICAST_IFINDEX:
3019 		/*
3020 		 * Select the interface for outgoing multicast packets.
3021 		 */
3022 		error = sooptcopyin(sopt, &ifindex, sizeof(ifindex),
3023 		    sizeof(ifindex));
3024 		if (error) {
3025 			break;
3026 		}
3027 
3028 		imo = inp_findmoptions(inp);
3029 		if (imo == NULL) {
3030 			error = ENOMEM;
3031 			break;
3032 		}
3033 		/*
3034 		 * Index 0 is used to remove a previous selection.
3035 		 * When no interface is selected, a default one is
3036 		 * chosen every time a multicast packet is sent.
3037 		 */
3038 		if (ifindex == 0) {
3039 			IMO_LOCK(imo);
3040 			imo->imo_multicast_ifp = NULL;
3041 			IMO_UNLOCK(imo);
3042 			IMO_REMREF(imo);        /* from inp_findmoptions() */
3043 			break;
3044 		}
3045 
3046 		ifnet_head_lock_shared();
3047 		/* Don't need to check is ifindex is < 0 since it's unsigned */
3048 		if ((unsigned int)if_index < ifindex) {
3049 			ifnet_head_done();
3050 			IMO_REMREF(imo);        /* from inp_findmoptions() */
3051 			error = ENXIO;  /* per IPV6_MULTICAST_IF */
3052 			break;
3053 		}
3054 		ifp = ifindex2ifnet[ifindex];
3055 		ifnet_head_done();
3056 
3057 		/* If it's detached or isn't a multicast interface, bail out */
3058 		if (ifp == NULL || !(ifp->if_flags & IFF_MULTICAST)) {
3059 			IMO_REMREF(imo);        /* from inp_findmoptions() */
3060 			error = EADDRNOTAVAIL;
3061 			break;
3062 		}
3063 		IMO_LOCK(imo);
3064 		imo->imo_multicast_ifp = ifp;
3065 		/*
3066 		 * Clear out any remnants of past IP_MULTICAST_IF.  The addr
3067 		 * isn't really used anywhere in the kernel; we could have
3068 		 * iterated thru the addresses of the interface and pick one
3069 		 * here, but that is redundant since ip_getmoptions() already
3070 		 * takes care of that for INADDR_ANY.
3071 		 */
3072 		imo->imo_multicast_addr.s_addr = INADDR_ANY;
3073 		IMO_UNLOCK(imo);
3074 		IMO_REMREF(imo);        /* from inp_findmoptions() */
3075 		break;
3076 
3077 	case IP_MULTICAST_TTL: {
3078 		u_char ttl;
3079 
3080 		/*
3081 		 * Set the IP time-to-live for outgoing multicast packets.
3082 		 * The original multicast API required a char argument,
3083 		 * which is inconsistent with the rest of the socket API.
3084 		 * We allow either a char or an int.
3085 		 */
3086 		if (sopt->sopt_valsize == sizeof(u_char)) {
3087 			error = sooptcopyin(sopt, &ttl, sizeof(u_char),
3088 			    sizeof(u_char));
3089 			if (error) {
3090 				break;
3091 			}
3092 		} else {
3093 			u_int ittl;
3094 
3095 			error = sooptcopyin(sopt, &ittl, sizeof(u_int),
3096 			    sizeof(u_int));
3097 			if (error) {
3098 				break;
3099 			}
3100 			if (ittl > 255) {
3101 				error = EINVAL;
3102 				break;
3103 			}
3104 			ttl = (u_char)ittl;
3105 		}
3106 		imo = inp_findmoptions(inp);
3107 		if (imo == NULL) {
3108 			error = ENOMEM;
3109 			break;
3110 		}
3111 		IMO_LOCK(imo);
3112 		imo->imo_multicast_ttl = ttl;
3113 		IMO_UNLOCK(imo);
3114 		IMO_REMREF(imo);        /* from inp_findmoptions() */
3115 		break;
3116 	}
3117 
3118 	case IP_MULTICAST_LOOP: {
3119 		u_char loop;
3120 
3121 		/*
3122 		 * Set the loopback flag for outgoing multicast packets.
3123 		 * Must be zero or one.  The original multicast API required a
3124 		 * char argument, which is inconsistent with the rest
3125 		 * of the socket API.  We allow either a char or an int.
3126 		 */
3127 		if (sopt->sopt_valsize == sizeof(u_char)) {
3128 			error = sooptcopyin(sopt, &loop, sizeof(u_char),
3129 			    sizeof(u_char));
3130 			if (error) {
3131 				break;
3132 			}
3133 		} else {
3134 			u_int iloop;
3135 
3136 			error = sooptcopyin(sopt, &iloop, sizeof(u_int),
3137 			    sizeof(u_int));
3138 			if (error) {
3139 				break;
3140 			}
3141 			loop = (u_char)iloop;
3142 		}
3143 		imo = inp_findmoptions(inp);
3144 		if (imo == NULL) {
3145 			error = ENOMEM;
3146 			break;
3147 		}
3148 		IMO_LOCK(imo);
3149 		imo->imo_multicast_loop = !!loop;
3150 		IMO_UNLOCK(imo);
3151 		IMO_REMREF(imo);        /* from inp_findmoptions() */
3152 		break;
3153 	}
3154 
3155 	case IP_ADD_MEMBERSHIP:
3156 	case IP_ADD_SOURCE_MEMBERSHIP:
3157 	case MCAST_JOIN_GROUP:
3158 	case MCAST_JOIN_SOURCE_GROUP:
3159 		error = inp_join_group(inp, sopt);
3160 		break;
3161 
3162 	case IP_DROP_MEMBERSHIP:
3163 	case IP_DROP_SOURCE_MEMBERSHIP:
3164 	case MCAST_LEAVE_GROUP:
3165 	case MCAST_LEAVE_SOURCE_GROUP:
3166 		error = inp_leave_group(inp, sopt);
3167 		break;
3168 
3169 	case IP_BLOCK_SOURCE:
3170 	case IP_UNBLOCK_SOURCE:
3171 	case MCAST_BLOCK_SOURCE:
3172 	case MCAST_UNBLOCK_SOURCE:
3173 		error = inp_block_unblock_source(inp, sopt);
3174 		break;
3175 
3176 	case IP_MSFILTER:
3177 		error = inp_set_source_filters(inp, sopt);
3178 		break;
3179 
3180 	default:
3181 		error = EOPNOTSUPP;
3182 		break;
3183 	}
3184 
3185 	return error;
3186 }
3187 
3188 /*
3189  * Expose IGMP's multicast filter mode and source list(s) to userland,
3190  * keyed by (ifindex, group).
3191  * The filter mode is written out as a uint32_t, followed by
3192  * 0..n of struct in_addr.
3193  * For use by ifmcstat(8).
3194  */
3195 static int
3196 sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS
3197 {
3198 #pragma unused(oidp)
3199 
3200 	struct in_addr                   src = {}, group;
3201 	struct ifnet                    *ifp;
3202 	struct in_multi                 *inm;
3203 	struct in_multistep             step;
3204 	struct ip_msource               *ims;
3205 	int                             *name;
3206 	int                              retval = 0;
3207 	u_int                            namelen;
3208 	uint32_t                         fmode, ifindex;
3209 
3210 	name = (int *)arg1;
3211 	namelen = (u_int)arg2;
3212 
3213 	if (req->newptr != USER_ADDR_NULL) {
3214 		return EPERM;
3215 	}
3216 
3217 	if (namelen != 2) {
3218 		return EINVAL;
3219 	}
3220 
3221 	ifindex = name[0];
3222 	ifnet_head_lock_shared();
3223 	if (ifindex <= 0 || ifindex > (u_int)if_index) {
3224 		IGMP_PRINTF(("%s: ifindex %u out of range\n",
3225 		    __func__, ifindex));
3226 		ifnet_head_done();
3227 		return ENOENT;
3228 	}
3229 
3230 	group.s_addr = name[1];
3231 	if (!IN_MULTICAST(ntohl(group.s_addr))) {
3232 		IGMP_INET_PRINTF(group,
3233 		    ("%s: group %s is not multicast\n",
3234 		    __func__, _igmp_inet_buf));
3235 		ifnet_head_done();
3236 		return EINVAL;
3237 	}
3238 
3239 	ifp = ifindex2ifnet[ifindex];
3240 	ifnet_head_done();
3241 	if (ifp == NULL) {
3242 		IGMP_PRINTF(("%s: no ifp for ifindex %u\n", __func__, ifindex));
3243 		return ENOENT;
3244 	}
3245 
3246 	in_multihead_lock_shared();
3247 	IN_FIRST_MULTI(step, inm);
3248 	while (inm != NULL) {
3249 		INM_LOCK(inm);
3250 		if (inm->inm_ifp != ifp) {
3251 			goto next;
3252 		}
3253 
3254 		if (!in_hosteq(inm->inm_addr, group)) {
3255 			goto next;
3256 		}
3257 
3258 		fmode = inm->inm_st[1].iss_fmode;
3259 		retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t));
3260 		if (retval != 0) {
3261 			INM_UNLOCK(inm);
3262 			break;          /* abort */
3263 		}
3264 		RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
3265 #ifdef IGMP_DEBUG
3266 			struct in_addr ina;
3267 			ina.s_addr = htonl(ims->ims_haddr);
3268 			IGMP_INET_PRINTF(ina,
3269 			    ("%s: visit node %s\n", __func__, _igmp_inet_buf));
3270 #endif
3271 			/*
3272 			 * Only copy-out sources which are in-mode.
3273 			 */
3274 			if (fmode != ims_get_mode(inm, ims, 1)) {
3275 				IGMP_PRINTF(("%s: skip non-in-mode\n",
3276 				    __func__));
3277 				continue; /* process next source */
3278 			}
3279 			src.s_addr = htonl(ims->ims_haddr);
3280 			retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr));
3281 			if (retval != 0) {
3282 				break;  /* process next inm */
3283 			}
3284 		}
3285 next:
3286 		INM_UNLOCK(inm);
3287 		IN_NEXT_MULTI(step, inm);
3288 	}
3289 	in_multihead_lock_done();
3290 
3291 	return retval;
3292 }
3293 
3294 /*
3295  * XXX
3296  * The whole multicast option thing needs to be re-thought.
3297  * Several of these options are equally applicable to non-multicast
3298  * transmission, and one (IP_MULTICAST_TTL) totally duplicates a
3299  * standard option (IP_TTL).
3300  */
3301 /*
3302  * following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
3303  */
3304 static struct ifnet *
ip_multicast_if(struct in_addr * a,unsigned int * ifindexp)3305 ip_multicast_if(struct in_addr *a, unsigned int *ifindexp)
3306 {
3307 	unsigned int ifindex;
3308 	struct ifnet *ifp;
3309 
3310 	if (ifindexp != NULL) {
3311 		*ifindexp = 0;
3312 	}
3313 	if (ntohl(a->s_addr) >> 24 == 0) {
3314 		ifindex = ntohl(a->s_addr) & 0xffffff;
3315 		ifnet_head_lock_shared();
3316 		/* Don't need to check is ifindex is < 0 since it's unsigned */
3317 		if ((unsigned int)if_index < ifindex) {
3318 			ifnet_head_done();
3319 			return NULL;
3320 		}
3321 		ifp = ifindex2ifnet[ifindex];
3322 		ifnet_head_done();
3323 		if (ifp != NULL && ifindexp != NULL) {
3324 			*ifindexp = ifindex;
3325 		}
3326 	} else {
3327 		INADDR_TO_IFP(*a, ifp);
3328 	}
3329 	return ifp;
3330 }
3331 
3332 void
in_multi_init(void)3333 in_multi_init(void)
3334 {
3335 	TAILQ_INIT(&inm_trash_head);
3336 
3337 	vm_size_t inm_size = (inm_debug == 0) ? sizeof(struct in_multi) :
3338 	    sizeof(struct in_multi_dbg);
3339 	inm_zone = zone_create(INM_ZONE_NAME, inm_size, ZC_ZFREE_CLEARMEM);
3340 }
3341 
3342 static struct in_multi *
in_multi_alloc(zalloc_flags_t how)3343 in_multi_alloc(zalloc_flags_t how)
3344 {
3345 	struct in_multi *inm;
3346 
3347 	inm = zalloc_flags(inm_zone, how | Z_ZERO);
3348 	if (inm != NULL) {
3349 		lck_mtx_init(&inm->inm_lock, &in_multihead_lock_grp,
3350 		    &in_multihead_lock_attr);
3351 		inm->inm_debug |= IFD_ALLOC;
3352 		if (inm_debug != 0) {
3353 			inm->inm_debug |= IFD_DEBUG;
3354 			inm->inm_trace = inm_trace;
3355 		}
3356 	}
3357 	return inm;
3358 }
3359 
3360 static void
in_multi_free(struct in_multi * inm)3361 in_multi_free(struct in_multi *inm)
3362 {
3363 	INM_LOCK(inm);
3364 	if (inm->inm_debug & IFD_ATTACHED) {
3365 		panic("%s: attached inm=%p is being freed", __func__, inm);
3366 		/* NOTREACHED */
3367 	} else if (inm->inm_ifma != NULL) {
3368 		panic("%s: ifma not NULL for inm=%p", __func__, inm);
3369 		/* NOTREACHED */
3370 	} else if (!(inm->inm_debug & IFD_ALLOC)) {
3371 		panic("%s: inm %p cannot be freed", __func__, inm);
3372 		/* NOTREACHED */
3373 	} else if (inm->inm_refcount != 0) {
3374 		panic("%s: non-zero refcount inm=%p", __func__, inm);
3375 		/* NOTREACHED */
3376 	} else if (inm->inm_reqcnt != 0) {
3377 		panic("%s: non-zero reqcnt inm=%p", __func__, inm);
3378 		/* NOTREACHED */
3379 	}
3380 
3381 	/* Free any pending IGMPv3 state-change records */
3382 	IF_DRAIN(&inm->inm_scq);
3383 
3384 	inm->inm_debug &= ~IFD_ALLOC;
3385 	if ((inm->inm_debug & (IFD_DEBUG | IFD_TRASHED)) ==
3386 	    (IFD_DEBUG | IFD_TRASHED)) {
3387 		lck_mtx_lock(&inm_trash_lock);
3388 		TAILQ_REMOVE(&inm_trash_head, (struct in_multi_dbg *)inm,
3389 		    inm_trash_link);
3390 		lck_mtx_unlock(&inm_trash_lock);
3391 		inm->inm_debug &= ~IFD_TRASHED;
3392 	}
3393 	INM_UNLOCK(inm);
3394 
3395 	lck_mtx_destroy(&inm->inm_lock, &in_multihead_lock_grp);
3396 	zfree(inm_zone, inm);
3397 }
3398 
3399 static void
in_multi_attach(struct in_multi * inm)3400 in_multi_attach(struct in_multi *inm)
3401 {
3402 	in_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE);
3403 	INM_LOCK_ASSERT_HELD(inm);
3404 
3405 	if (inm->inm_debug & IFD_ATTACHED) {
3406 		panic("%s: Attempt to attach an already attached inm=%p",
3407 		    __func__, inm);
3408 		/* NOTREACHED */
3409 	} else if (inm->inm_debug & IFD_TRASHED) {
3410 		panic("%s: Attempt to reattach a detached inm=%p",
3411 		    __func__, inm);
3412 		/* NOTREACHED */
3413 	}
3414 
3415 	inm->inm_reqcnt++;
3416 	VERIFY(inm->inm_reqcnt == 1);
3417 	INM_ADDREF_LOCKED(inm);
3418 	inm->inm_debug |= IFD_ATTACHED;
3419 	/*
3420 	 * Reattach case:  If debugging is enabled, take it
3421 	 * out of the trash list and clear IFD_TRASHED.
3422 	 */
3423 	if ((inm->inm_debug & (IFD_DEBUG | IFD_TRASHED)) ==
3424 	    (IFD_DEBUG | IFD_TRASHED)) {
3425 		/* Become a regular mutex, just in case */
3426 		INM_CONVERT_LOCK(inm);
3427 		lck_mtx_lock(&inm_trash_lock);
3428 		TAILQ_REMOVE(&inm_trash_head, (struct in_multi_dbg *)inm,
3429 		    inm_trash_link);
3430 		lck_mtx_unlock(&inm_trash_lock);
3431 		inm->inm_debug &= ~IFD_TRASHED;
3432 	}
3433 
3434 	LIST_INSERT_HEAD(&in_multihead, inm, inm_link);
3435 }
3436 
3437 int
in_multi_detach(struct in_multi * inm)3438 in_multi_detach(struct in_multi *inm)
3439 {
3440 	in_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE);
3441 	INM_LOCK_ASSERT_HELD(inm);
3442 
3443 	if (inm->inm_reqcnt == 0) {
3444 		panic("%s: inm=%p negative reqcnt", __func__, inm);
3445 		/* NOTREACHED */
3446 	}
3447 
3448 	--inm->inm_reqcnt;
3449 	if (inm->inm_reqcnt > 0) {
3450 		return 0;
3451 	}
3452 
3453 	if (!(inm->inm_debug & IFD_ATTACHED)) {
3454 		panic("%s: Attempt to detach an unattached record inm=%p",
3455 		    __func__, inm);
3456 		/* NOTREACHED */
3457 	} else if (inm->inm_debug & IFD_TRASHED) {
3458 		panic("%s: inm %p is already in trash list", __func__, inm);
3459 		/* NOTREACHED */
3460 	}
3461 
3462 	/*
3463 	 * NOTE: Caller calls IFMA_REMREF
3464 	 */
3465 	inm->inm_debug &= ~IFD_ATTACHED;
3466 	LIST_REMOVE(inm, inm_link);
3467 
3468 	if (inm->inm_debug & IFD_DEBUG) {
3469 		/* Become a regular mutex, just in case */
3470 		INM_CONVERT_LOCK(inm);
3471 		lck_mtx_lock(&inm_trash_lock);
3472 		TAILQ_INSERT_TAIL(&inm_trash_head,
3473 		    (struct in_multi_dbg *)inm, inm_trash_link);
3474 		lck_mtx_unlock(&inm_trash_lock);
3475 		inm->inm_debug |= IFD_TRASHED;
3476 	}
3477 
3478 	return 1;
3479 }
3480 
3481 void
inm_addref(struct in_multi * inm,int locked)3482 inm_addref(struct in_multi *inm, int locked)
3483 {
3484 	if (!locked) {
3485 		INM_LOCK_SPIN(inm);
3486 	} else {
3487 		INM_LOCK_ASSERT_HELD(inm);
3488 	}
3489 
3490 	if (++inm->inm_refcount == 0) {
3491 		panic("%s: inm=%p wraparound refcnt", __func__, inm);
3492 		/* NOTREACHED */
3493 	} else if (inm->inm_trace != NULL) {
3494 		(*inm->inm_trace)(inm, TRUE);
3495 	}
3496 	if (!locked) {
3497 		INM_UNLOCK(inm);
3498 	}
3499 }
3500 
3501 void
inm_remref(struct in_multi * inm,int locked)3502 inm_remref(struct in_multi *inm, int locked)
3503 {
3504 	struct ifmultiaddr *ifma;
3505 	struct igmp_ifinfo *igi;
3506 
3507 	if (!locked) {
3508 		INM_LOCK_SPIN(inm);
3509 	} else {
3510 		INM_LOCK_ASSERT_HELD(inm);
3511 	}
3512 
3513 	if (inm->inm_refcount == 0 || (inm->inm_refcount == 1 && locked)) {
3514 		panic("%s: inm=%p negative/missing refcnt", __func__, inm);
3515 		/* NOTREACHED */
3516 	} else if (inm->inm_trace != NULL) {
3517 		(*inm->inm_trace)(inm, FALSE);
3518 	}
3519 
3520 	--inm->inm_refcount;
3521 	if (inm->inm_refcount > 0) {
3522 		if (!locked) {
3523 			INM_UNLOCK(inm);
3524 		}
3525 		return;
3526 	}
3527 
3528 	/*
3529 	 * Synchronization with in_getmulti().  In the event the inm has been
3530 	 * detached, the underlying ifma would still be in the if_multiaddrs
3531 	 * list, and thus can be looked up via if_addmulti().  At that point,
3532 	 * the only way to find this inm is via ifma_protospec.  To avoid
3533 	 * race conditions between the last inm_remref() of that inm and its
3534 	 * use via ifma_protospec, in_multihead lock is used for serialization.
3535 	 * In order to avoid violating the lock order, we must drop inm_lock
3536 	 * before acquiring in_multihead lock.  To prevent the inm from being
3537 	 * freed prematurely, we hold an extra reference.
3538 	 */
3539 	++inm->inm_refcount;
3540 	INM_UNLOCK(inm);
3541 	in_multihead_lock_shared();
3542 	INM_LOCK_SPIN(inm);
3543 	--inm->inm_refcount;
3544 	if (inm->inm_refcount > 0) {
3545 		/* We've lost the race, so abort since inm is still in use */
3546 		INM_UNLOCK(inm);
3547 		in_multihead_lock_done();
3548 		/* If it was locked, return it as such */
3549 		if (locked) {
3550 			INM_LOCK(inm);
3551 		}
3552 		return;
3553 	}
3554 	inm_purge(inm);
3555 	ifma = inm->inm_ifma;
3556 	inm->inm_ifma = NULL;
3557 	inm->inm_ifp = NULL;
3558 	igi = inm->inm_igi;
3559 	inm->inm_igi = NULL;
3560 	INM_UNLOCK(inm);
3561 	IFMA_LOCK_SPIN(ifma);
3562 	ifma->ifma_protospec = NULL;
3563 	IFMA_UNLOCK(ifma);
3564 	in_multihead_lock_done();
3565 
3566 	in_multi_free(inm);
3567 	if_delmulti_ifma(ifma);
3568 	/* Release reference held to the underlying ifmultiaddr */
3569 	IFMA_REMREF(ifma);
3570 
3571 	if (igi != NULL) {
3572 		IGI_REMREF(igi);
3573 	}
3574 }
3575 
3576 static void
inm_trace(struct in_multi * inm,int refhold)3577 inm_trace(struct in_multi *inm, int refhold)
3578 {
3579 	struct in_multi_dbg *inm_dbg = (struct in_multi_dbg *)inm;
3580 	ctrace_t *tr;
3581 	u_int32_t idx;
3582 	u_int16_t *cnt;
3583 
3584 	if (!(inm->inm_debug & IFD_DEBUG)) {
3585 		panic("%s: inm %p has no debug structure", __func__, inm);
3586 		/* NOTREACHED */
3587 	}
3588 	if (refhold) {
3589 		cnt = &inm_dbg->inm_refhold_cnt;
3590 		tr = inm_dbg->inm_refhold;
3591 	} else {
3592 		cnt = &inm_dbg->inm_refrele_cnt;
3593 		tr = inm_dbg->inm_refrele;
3594 	}
3595 
3596 	idx = atomic_add_16_ov(cnt, 1) % INM_TRACE_HIST_SIZE;
3597 	ctrace_record(&tr[idx]);
3598 }
3599 
3600 void
in_multihead_lock_exclusive(void)3601 in_multihead_lock_exclusive(void)
3602 {
3603 	lck_rw_lock_exclusive(&in_multihead_lock);
3604 }
3605 
3606 void
in_multihead_lock_shared(void)3607 in_multihead_lock_shared(void)
3608 {
3609 	lck_rw_lock_shared(&in_multihead_lock);
3610 }
3611 
3612 void
in_multihead_lock_assert(int what)3613 in_multihead_lock_assert(int what)
3614 {
3615 #if !MACH_ASSERT
3616 #pragma unused(what)
3617 #endif
3618 	LCK_RW_ASSERT(&in_multihead_lock, what);
3619 }
3620 
3621 void
in_multihead_lock_done(void)3622 in_multihead_lock_done(void)
3623 {
3624 	lck_rw_done(&in_multihead_lock);
3625 }
3626 
3627 static struct ip_msource *
ipms_alloc(zalloc_flags_t how)3628 ipms_alloc(zalloc_flags_t how)
3629 {
3630 	return zalloc_flags(ipms_zone, how | Z_ZERO);
3631 }
3632 
3633 static void
ipms_free(struct ip_msource * ims)3634 ipms_free(struct ip_msource *ims)
3635 {
3636 	zfree(ipms_zone, ims);
3637 }
3638 
3639 static struct in_msource *
inms_alloc(zalloc_flags_t how)3640 inms_alloc(zalloc_flags_t how)
3641 {
3642 	return zalloc_flags(inms_zone, how | Z_ZERO);
3643 }
3644 
3645 static void
inms_free(struct in_msource * inms)3646 inms_free(struct in_msource *inms)
3647 {
3648 	zfree(inms_zone, inms);
3649 }
3650 
3651 #ifdef IGMP_DEBUG
3652 
3653 static const char *inm_modestrs[] = { "un\n", "in", "ex" };
3654 
3655 static const char *
inm_mode_str(const int mode)3656 inm_mode_str(const int mode)
3657 {
3658 	if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) {
3659 		return inm_modestrs[mode];
3660 	}
3661 	return "??";
3662 }
3663 
3664 static const char *inm_statestrs[] = {
3665 	"not-member\n",
3666 	"silent\n",
3667 	"reporting\n",
3668 	"idle\n",
3669 	"lazy\n",
3670 	"sleeping\n",
3671 	"awakening\n",
3672 	"query-pending\n",
3673 	"sg-query-pending\n",
3674 	"leaving"
3675 };
3676 
3677 static const char *
inm_state_str(const int state)3678 inm_state_str(const int state)
3679 {
3680 	if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) {
3681 		return inm_statestrs[state];
3682 	}
3683 	return "??";
3684 }
3685 
3686 /*
3687  * Dump an in_multi structure to the console.
3688  */
3689 void
inm_print(const struct in_multi * inm)3690 inm_print(const struct in_multi *inm)
3691 {
3692 	int t;
3693 	char buf[MAX_IPv4_STR_LEN];
3694 
3695 	INM_LOCK_ASSERT_HELD(__DECONST(struct in_multi *, inm));
3696 
3697 	if (igmp_debug == 0) {
3698 		return;
3699 	}
3700 
3701 	inet_ntop(AF_INET, &inm->inm_addr, buf, sizeof(buf));
3702 	printf("%s: --- begin inm 0x%llx ---\n", __func__,
3703 	    (uint64_t)VM_KERNEL_ADDRPERM(inm));
3704 	printf("addr %s ifp 0x%llx(%s) ifma 0x%llx\n",
3705 	    buf,
3706 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifp),
3707 	    if_name(inm->inm_ifp),
3708 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifma));
3709 	printf("timer %u state %s refcount %u scq.len %u\n",
3710 	    inm->inm_timer,
3711 	    inm_state_str(inm->inm_state),
3712 	    inm->inm_refcount,
3713 	    inm->inm_scq.ifq_len);
3714 	printf("igi 0x%llx nsrc %lu sctimer %u scrv %u\n",
3715 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_igi),
3716 	    inm->inm_nsrc,
3717 	    inm->inm_sctimer,
3718 	    inm->inm_scrv);
3719 	for (t = 0; t < 2; t++) {
3720 		printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t,
3721 		    inm_mode_str(inm->inm_st[t].iss_fmode),
3722 		    inm->inm_st[t].iss_asm,
3723 		    inm->inm_st[t].iss_ex,
3724 		    inm->inm_st[t].iss_in,
3725 		    inm->inm_st[t].iss_rec);
3726 	}
3727 	printf("%s: --- end inm 0x%llx ---\n", __func__,
3728 	    (uint64_t)VM_KERNEL_ADDRPERM(inm));
3729 }
3730 
3731 #else
3732 
3733 void
inm_print(__unused const struct in_multi * inm)3734 inm_print(__unused const struct in_multi *inm)
3735 {
3736 }
3737 
3738 #endif
3739