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