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