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