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