2 * Copyright (c) 2010-2020 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright (c) 2009 Bruce Simpson.
30 * All rights reserved.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
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
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
58 * IPv6 multicast socket, group, and socket option processing module.
59 * Normative references: RFC 2292, RFC 3492, RFC 3542, RFC 3678, RFC 3810.
62 #include <sys/cdefs.h>
64 #include <sys/param.h>
65 #include <sys/systm.h>
66 #include <sys/kernel.h>
67 #include <sys/malloc.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>
75 #include <sys/mcache.h>
77 #include <kern/zalloc.h>
79 #include <pexpert/pexpert.h>
82 #include <net/if_dl.h>
83 #include <net/net_api_stats.h>
84 #include <net/route.h>
86 #include <netinet/in.h>
87 #include <netinet/in_var.h>
88 #include <netinet6/in6_var.h>
89 #include <netinet/ip6.h>
90 #include <netinet/icmp6.h>
91 #include <netinet6/ip6_var.h>
92 #include <netinet/in_pcb.h>
93 #include <netinet/tcp.h>
94 #include <netinet/tcp_seq.h>
95 #include <netinet/tcp_var.h>
96 #include <netinet6/nd6.h>
97 #include <netinet6/mld6_var.h>
98 #include <netinet6/scope6_var.h>
100 static void im6f_commit(struct in6_mfilter
*);
101 static int im6f_get_source(struct in6_mfilter
*imf
,
102 const struct sockaddr_in6
*psin
,
103 struct in6_msource
**);
104 static struct in6_msource
*
105 im6f_graft(struct in6_mfilter
*, const uint8_t,
106 const struct sockaddr_in6
*);
107 static int im6f_prune(struct in6_mfilter
*, const struct sockaddr_in6
*);
108 static void im6f_rollback(struct in6_mfilter
*);
109 static void im6f_reap(struct in6_mfilter
*);
110 static int im6o_grow(struct ip6_moptions
*);
111 static size_t im6o_match_group(const struct ip6_moptions
*,
112 const struct ifnet
*, const struct sockaddr_in6
*);
113 static struct in6_msource
*
114 im6o_match_source(const struct ip6_moptions
*,
115 const size_t, const struct sockaddr_in6
*);
116 static void im6s_merge(struct ip6_msource
*ims
,
117 const struct in6_msource
*lims
, const int rollback
);
118 static int in6_mc_get(struct ifnet
*, const struct in6_addr
*,
119 struct in6_multi
**);
120 static int in6m_get_source(struct in6_multi
*inm
,
121 const struct in6_addr
*addr
, const int noalloc
,
122 struct ip6_msource
**pims
);
123 static int in6m_is_ifp_detached(const struct in6_multi
*);
124 static int in6m_merge(struct in6_multi
*, /*const*/ struct in6_mfilter
*);
125 static void in6m_reap(struct in6_multi
*);
126 static struct ip6_moptions
*
127 in6p_findmoptions(struct inpcb
*);
128 static int in6p_get_source_filters(struct inpcb
*, struct sockopt
*);
129 static int in6p_lookup_v4addr(struct ipv6_mreq
*, struct ip_mreq
*);
130 static int in6p_join_group(struct inpcb
*, struct sockopt
*);
131 static int in6p_leave_group(struct inpcb
*, struct sockopt
*);
132 static struct ifnet
*
133 in6p_lookup_mcast_ifp(const struct inpcb
*,
134 const struct sockaddr_in6
*);
135 static int in6p_block_unblock_source(struct inpcb
*, struct sockopt
*);
136 static int in6p_set_multicast_if(struct inpcb
*, struct sockopt
*);
137 static int in6p_set_source_filters(struct inpcb
*, struct sockopt
*);
138 static int sysctl_ip6_mcast_filters SYSCTL_HANDLER_ARGS
;
139 static __inline__
int ip6_msource_cmp(const struct ip6_msource
*,
140 const struct ip6_msource
*);
142 SYSCTL_DECL(_net_inet6_ip6
); /* XXX Not in any common header. */
144 SYSCTL_NODE(_net_inet6_ip6
, OID_AUTO
, mcast
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, "IPv6 multicast");
146 static unsigned long in6_mcast_maxgrpsrc
= IPV6_MAX_GROUP_SRC_FILTER
;
147 SYSCTL_LONG(_net_inet6_ip6_mcast
, OID_AUTO
, maxgrpsrc
,
148 CTLFLAG_RW
| CTLFLAG_LOCKED
, &in6_mcast_maxgrpsrc
,
149 "Max source filters per group");
151 static unsigned long in6_mcast_maxsocksrc
= IPV6_MAX_SOCK_SRC_FILTER
;
152 SYSCTL_LONG(_net_inet6_ip6_mcast
, OID_AUTO
, maxsocksrc
,
153 CTLFLAG_RW
| CTLFLAG_LOCKED
, &in6_mcast_maxsocksrc
,
154 "Max source filters per socket");
156 int in6_mcast_loop
= IPV6_DEFAULT_MULTICAST_LOOP
;
157 SYSCTL_INT(_net_inet6_ip6_mcast
, OID_AUTO
, loop
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
158 &in6_mcast_loop
, 0, "Loopback multicast datagrams by default");
160 SYSCTL_NODE(_net_inet6_ip6_mcast
, OID_AUTO
, filters
,
161 CTLFLAG_RD
| CTLFLAG_LOCKED
, sysctl_ip6_mcast_filters
,
162 "Per-interface stack-wide source filters");
164 RB_GENERATE_PREV(ip6_msource_tree
, ip6_msource
, im6s_link
, ip6_msource_cmp
);
166 #define IN6M_TRACE_HIST_SIZE 32 /* size of trace history */
169 __private_extern__
unsigned int in6m_trace_hist_size
= IN6M_TRACE_HIST_SIZE
;
171 struct in6_multi_dbg
{
172 struct in6_multi in6m
; /* in6_multi */
173 u_int16_t in6m_refhold_cnt
; /* # of ref */
174 u_int16_t in6m_refrele_cnt
; /* # of rele */
176 * Circular lists of in6m_addref and in6m_remref callers.
178 ctrace_t in6m_refhold
[IN6M_TRACE_HIST_SIZE
];
179 ctrace_t in6m_refrele
[IN6M_TRACE_HIST_SIZE
];
183 TAILQ_ENTRY(in6_multi_dbg
) in6m_trash_link
;
186 /* List of trash in6_multi entries protected by in6m_trash_lock */
187 static TAILQ_HEAD(, in6_multi_dbg
) in6m_trash_head
;
188 static decl_lck_mtx_data(, in6m_trash_lock
);
191 static unsigned int in6m_debug
= 1; /* debugging (enabled) */
193 static unsigned int in6m_debug
; /* debugging (disabled) */
195 static struct zone
*in6m_zone
; /* zone for in6_multi */
196 #define IN6M_ZONE_NAME "in6_multi" /* zone name */
198 static ZONE_DECLARE(imm_zone
, "in6_multi_mship",
199 sizeof(struct in6_multi_mship
), ZC_ZFREE_CLEARMEM
);
201 static ZONE_DECLARE(ip6ms_zone
, "ip6_msource",
202 sizeof(struct ip6_msource
), ZC_ZFREE_CLEARMEM
);
204 static ZONE_DECLARE(in6ms_zone
, "in6_msource",
205 sizeof(struct in6_msource
), ZC_ZFREE_CLEARMEM
);
207 /* Lock group and attribute for in6_multihead_lock lock */
208 static lck_attr_t
*in6_multihead_lock_attr
;
209 static lck_grp_t
*in6_multihead_lock_grp
;
210 static lck_grp_attr_t
*in6_multihead_lock_grp_attr
;
212 static decl_lck_rw_data(, in6_multihead_lock
);
213 struct in6_multihead in6_multihead
;
215 static struct in6_multi
*in6_multi_alloc(zalloc_flags_t
);
216 static void in6_multi_free(struct in6_multi
*);
217 static void in6_multi_attach(struct in6_multi
*);
218 static struct in6_multi_mship
*in6_multi_mship_alloc(zalloc_flags_t
);
219 static void in6_multi_mship_free(struct in6_multi_mship
*);
220 static void in6m_trace(struct in6_multi
*, int);
222 static struct ip6_msource
*ip6ms_alloc(zalloc_flags_t
);
223 static void ip6ms_free(struct ip6_msource
*);
224 static struct in6_msource
*in6ms_alloc(zalloc_flags_t
);
225 static void in6ms_free(struct in6_msource
*);
228 * IPv6 source tree comparison function.
230 * An ordered predicate is necessary; bcmp() is not documented to return
231 * an indication of order, memcmp() is, and is an ISO C99 requirement.
234 ip6_msource_cmp(const struct ip6_msource
*a
, const struct ip6_msource
*b
)
236 return memcmp(&a
->im6s_addr
, &b
->im6s_addr
, sizeof(struct in6_addr
));
240 * Inline function which wraps assertions for a valid ifp.
242 static __inline__
int
243 in6m_is_ifp_detached(const struct in6_multi
*inm
)
245 VERIFY(inm
->in6m_ifma
!= NULL
);
246 VERIFY(inm
->in6m_ifp
== inm
->in6m_ifma
->ifma_ifp
);
248 return !ifnet_is_attached(inm
->in6m_ifp
, 0);
252 * Initialize an in6_mfilter structure to a known state at t0, t1
253 * with an empty source filter list.
255 static __inline__
void
256 im6f_init(struct in6_mfilter
*imf
, const uint8_t st0
, const uint8_t st1
)
258 memset(imf
, 0, sizeof(struct in6_mfilter
));
259 RB_INIT(&imf
->im6f_sources
);
260 imf
->im6f_st
[0] = st0
;
261 imf
->im6f_st
[1] = st1
;
265 * Resize the ip6_moptions vector to the next power-of-two minus 1.
268 im6o_grow(struct ip6_moptions
*imo
)
270 struct in6_multi
**nmships
;
271 struct in6_multi
**omships
;
272 struct in6_mfilter
*nmfilters
;
273 struct in6_mfilter
*omfilters
;
278 IM6O_LOCK_ASSERT_HELD(imo
);
282 omships
= imo
->im6o_membership
;
283 omfilters
= imo
->im6o_mfilters
;
284 oldmax
= imo
->im6o_max_memberships
;
285 newmax
= ((oldmax
+ 1) * 2) - 1;
287 if (newmax
> IPV6_MAX_MEMBERSHIPS
) {
291 if ((nmships
= (struct in6_multi
**)_REALLOC(omships
,
292 sizeof(struct in6_multi
*) * newmax
, M_IP6MOPTS
,
293 M_WAITOK
| M_ZERO
)) == NULL
) {
297 imo
->im6o_membership
= nmships
;
299 if ((nmfilters
= (struct in6_mfilter
*)_REALLOC(omfilters
,
300 sizeof(struct in6_mfilter
) * newmax
, M_IN6MFILTER
,
301 M_WAITOK
| M_ZERO
)) == NULL
) {
305 imo
->im6o_mfilters
= nmfilters
;
307 /* Initialize newly allocated source filter heads. */
308 for (idx
= oldmax
; idx
< newmax
; idx
++) {
309 im6f_init(&nmfilters
[idx
], MCAST_UNDEFINED
, MCAST_EXCLUDE
);
312 imo
->im6o_max_memberships
= (u_short
)newmax
;
318 * Find an IPv6 multicast group entry for this ip6_moptions instance
319 * which matches the specified group, and optionally an interface.
320 * Return its index into the array, or -1 if not found.
323 im6o_match_group(const struct ip6_moptions
*imo
, const struct ifnet
*ifp
,
324 const struct sockaddr_in6
*group
)
326 const struct sockaddr_in6
*gsin6
;
327 struct in6_multi
*pinm
;
331 IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions
*, imo
));
335 /* The im6o_membership array may be lazy allocated. */
336 if (imo
->im6o_membership
== NULL
|| imo
->im6o_num_memberships
== 0) {
340 nmships
= imo
->im6o_num_memberships
;
341 for (idx
= 0; idx
< nmships
; idx
++) {
342 pinm
= imo
->im6o_membership
[idx
];
347 if ((ifp
== NULL
|| (pinm
->in6m_ifp
== ifp
)) &&
348 IN6_ARE_ADDR_EQUAL(&pinm
->in6m_addr
,
349 &gsin6
->sin6_addr
)) {
355 if (idx
>= nmships
) {
363 * Find an IPv6 multicast source entry for this imo which matches
364 * the given group index for this socket, and source address.
366 * XXX TODO: The scope ID, if present in src, is stripped before
367 * any comparison. We SHOULD enforce scope/zone checks where the source
368 * filter entry has a link scope.
370 * NOTE: This does not check if the entry is in-mode, merely if
371 * it exists, which may not be the desired behaviour.
373 static struct in6_msource
*
374 im6o_match_source(const struct ip6_moptions
*imo
, const size_t gidx
,
375 const struct sockaddr_in6
*src
)
377 struct ip6_msource find
;
378 struct in6_mfilter
*imf
;
379 struct ip6_msource
*ims
;
380 const struct sockaddr_in6
*psa
;
382 IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions
*, imo
));
384 VERIFY(src
->sin6_family
== AF_INET6
);
385 VERIFY(gidx
!= (size_t)-1 && gidx
< imo
->im6o_num_memberships
);
387 /* The im6o_mfilters array may be lazy allocated. */
388 if (imo
->im6o_mfilters
== NULL
) {
391 imf
= &imo
->im6o_mfilters
[gidx
];
394 find
.im6s_addr
= psa
->sin6_addr
;
395 in6_clearscope(&find
.im6s_addr
); /* XXX */
396 ims
= RB_FIND(ip6_msource_tree
, &imf
->im6f_sources
, &find
);
398 return (struct in6_msource
*)ims
;
402 * Perform filtering for multicast datagrams on a socket by group and source.
404 * Returns 0 if a datagram should be allowed through, or various error codes
405 * if the socket was not a member of the group, or the source was muted, etc.
408 im6o_mc_filter(const struct ip6_moptions
*imo
, const struct ifnet
*ifp
,
409 const struct sockaddr_in6
*group
, const struct sockaddr_in6
*src
)
412 struct in6_msource
*ims
;
415 IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions
*, imo
));
418 gidx
= im6o_match_group(imo
, ifp
, group
);
419 if (gidx
== (size_t)-1) {
420 return MCAST_NOTGMEMBER
;
424 * Check if the source was included in an (S,G) join.
425 * Allow reception on exclusive memberships by default,
426 * reject reception on inclusive memberships by default.
427 * Exclude source only if an in-mode exclude filter exists.
428 * Include source only if an in-mode include filter exists.
429 * NOTE: We are comparing group state here at MLD t1 (now)
430 * with socket-layer t0 (since last downcall).
432 mode
= imo
->im6o_mfilters
[gidx
].im6f_st
[1];
433 ims
= im6o_match_source(imo
, gidx
, src
);
435 if ((ims
== NULL
&& mode
== MCAST_INCLUDE
) ||
436 (ims
!= NULL
&& ims
->im6sl_st
[0] != mode
)) {
437 return MCAST_NOTSMEMBER
;
444 * Find and return a reference to an in6_multi record for (ifp, group),
445 * and bump its reference count.
446 * If one does not exist, try to allocate it, and update link-layer multicast
447 * filters on ifp to listen for group.
448 * Assumes the IN6_MULTI lock is held across the call.
449 * Return 0 if successful, otherwise return an appropriate error code.
452 in6_mc_get(struct ifnet
*ifp
, const struct in6_addr
*group
,
453 struct in6_multi
**pinm
)
455 struct sockaddr_in6 gsin6
;
456 struct ifmultiaddr
*ifma
;
457 struct in6_multi
*inm
;
462 in6_multihead_lock_shared();
463 IN6_LOOKUP_MULTI(group
, ifp
, inm
);
466 VERIFY(inm
->in6m_reqcnt
>= 1);
468 VERIFY(inm
->in6m_reqcnt
!= 0);
471 in6_multihead_lock_done();
473 * We already joined this group; return the in6m
474 * with a refcount held (via lookup) for caller.
478 in6_multihead_lock_done();
480 memset(&gsin6
, 0, sizeof(gsin6
));
481 gsin6
.sin6_family
= AF_INET6
;
482 gsin6
.sin6_len
= sizeof(struct sockaddr_in6
);
483 gsin6
.sin6_addr
= *group
;
486 * Check if a link-layer group is already associated
487 * with this network-layer group on the given ifnet.
489 error
= if_addmulti(ifp
, (struct sockaddr
*)&gsin6
, &ifma
);
495 * See comments in in6m_remref() for access to ifma_protospec.
497 in6_multihead_lock_exclusive();
499 if ((inm
= ifma
->ifma_protospec
) != NULL
) {
500 VERIFY(ifma
->ifma_addr
!= NULL
);
501 VERIFY(ifma
->ifma_addr
->sa_family
== AF_INET6
);
502 IN6M_ADDREF(inm
); /* for caller */
505 VERIFY(inm
->in6m_ifma
== ifma
);
506 VERIFY(inm
->in6m_ifp
== ifp
);
507 VERIFY(IN6_ARE_ADDR_EQUAL(&inm
->in6m_addr
, group
));
508 if (inm
->in6m_debug
& IFD_ATTACHED
) {
509 VERIFY(inm
->in6m_reqcnt
>= 1);
511 VERIFY(inm
->in6m_reqcnt
!= 0);
514 in6_multihead_lock_done();
517 * We lost the race with another thread doing
518 * in6_mc_get(); since this group has already
519 * been joined; return the inm with a refcount
525 * We lost the race with another thread doing in6_delmulti();
526 * the inm referring to the ifma has been detached, thus we
527 * reattach it back to the in6_multihead list, and return the
528 * inm with a refcount held for the caller.
530 in6_multi_attach(inm
);
531 VERIFY((inm
->in6m_debug
&
532 (IFD_ATTACHED
| IFD_TRASHED
)) == IFD_ATTACHED
);
535 in6_multihead_lock_done();
542 * A new in6_multi record is needed; allocate and initialize it.
543 * We DO NOT perform an MLD join as the in6_ layer may need to
544 * push an initial source list down to MLD to support SSM.
546 * The initial source filter state is INCLUDE, {} as per the RFC.
547 * Pending state-changes per group are subject to a bounds check.
549 inm
= in6_multi_alloc(Z_WAITOK
);
552 inm
->in6m_addr
= *group
;
554 inm
->in6m_mli
= MLD_IFINFO(ifp
);
555 VERIFY(inm
->in6m_mli
!= NULL
);
556 MLI_ADDREF(inm
->in6m_mli
);
557 inm
->in6m_ifma
= ifma
; /* keep refcount from if_addmulti() */
558 inm
->in6m_state
= MLD_NOT_MEMBER
;
560 * Pending state-changes per group are subject to a bounds check.
562 inm
->in6m_scq
.ifq_maxlen
= MLD_MAX_STATE_CHANGES
;
563 inm
->in6m_st
[0].iss_fmode
= MCAST_UNDEFINED
;
564 inm
->in6m_st
[1].iss_fmode
= MCAST_UNDEFINED
;
565 RB_INIT(&inm
->in6m_srcs
);
567 in6_multi_attach(inm
);
568 VERIFY((inm
->in6m_debug
&
569 (IFD_ATTACHED
| IFD_TRASHED
)) == IFD_ATTACHED
);
570 IN6M_ADDREF_LOCKED(inm
); /* for caller */
574 VERIFY(ifma
->ifma_protospec
== NULL
);
575 ifma
->ifma_protospec
= inm
;
577 in6_multihead_lock_done();
583 * Clear recorded source entries for a group.
584 * Used by the MLD code. Caller must hold the IN6_MULTI lock.
585 * FIXME: Should reap.
588 in6m_clear_recorded(struct in6_multi
*inm
)
590 struct ip6_msource
*ims
;
592 IN6M_LOCK_ASSERT_HELD(inm
);
594 RB_FOREACH(ims
, ip6_msource_tree
, &inm
->in6m_srcs
) {
597 --inm
->in6m_st
[1].iss_rec
;
600 VERIFY(inm
->in6m_st
[1].iss_rec
== 0);
604 * Record a source as pending for a Source-Group MLDv2 query.
605 * This lives here as it modifies the shared tree.
607 * inm is the group descriptor.
608 * naddr is the address of the source to record in network-byte order.
610 * If the net.inet6.mld.sgalloc sysctl is non-zero, we will
611 * lazy-allocate a source node in response to an SG query.
612 * Otherwise, no allocation is performed. This saves some memory
613 * with the trade-off that the source will not be reported to the
614 * router if joined in the window between the query response and
615 * the group actually being joined on the local host.
617 * VIMAGE: XXX: Currently the mld_sgalloc feature has been removed.
618 * This turns off the allocation of a recorded source entry if
619 * the group has not been joined.
621 * Return 0 if the source didn't exist or was already marked as recorded.
622 * Return 1 if the source was marked as recorded by this function.
623 * Return <0 if any error occured (negated errno code).
626 in6m_record_source(struct in6_multi
*inm
, const struct in6_addr
*addr
)
628 struct ip6_msource find
;
629 struct ip6_msource
*ims
, *nims
;
631 IN6M_LOCK_ASSERT_HELD(inm
);
633 find
.im6s_addr
= *addr
;
634 ims
= RB_FIND(ip6_msource_tree
, &inm
->in6m_srcs
, &find
);
635 if (ims
&& ims
->im6s_stp
) {
639 if (inm
->in6m_nsrc
== in6_mcast_maxgrpsrc
) {
642 nims
= ip6ms_alloc(Z_WAITOK
);
643 nims
->im6s_addr
= find
.im6s_addr
;
644 RB_INSERT(ip6_msource_tree
, &inm
->in6m_srcs
, nims
);
650 * Mark the source as recorded and update the recorded
654 ++inm
->in6m_st
[1].iss_rec
;
660 * Return a pointer to an in6_msource owned by an in6_mfilter,
661 * given its source address.
662 * Lazy-allocate if needed. If this is a new entry its filter state is
665 * imf is the filter set being modified.
666 * addr is the source address.
668 * Caller is expected to be holding im6o_lock.
671 im6f_get_source(struct in6_mfilter
*imf
, const struct sockaddr_in6
*psin
,
672 struct in6_msource
**plims
)
674 struct ip6_msource find
;
675 struct ip6_msource
*ims
;
676 struct in6_msource
*lims
;
683 find
.im6s_addr
= psin
->sin6_addr
;
684 ims
= RB_FIND(ip6_msource_tree
, &imf
->im6f_sources
, &find
);
685 lims
= (struct in6_msource
*)ims
;
687 if (imf
->im6f_nsrc
== in6_mcast_maxsocksrc
) {
690 lims
= in6ms_alloc(Z_WAITOK
);
691 lims
->im6s_addr
= find
.im6s_addr
;
692 lims
->im6sl_st
[0] = MCAST_UNDEFINED
;
693 RB_INSERT(ip6_msource_tree
, &imf
->im6f_sources
,
694 (struct ip6_msource
*)lims
);
704 * Graft a source entry into an existing socket-layer filter set,
705 * maintaining any required invariants and checking allocations.
707 * The source is marked as being in the new filter mode at t1.
709 * Return the pointer to the new node, otherwise return NULL.
711 * Caller is expected to be holding im6o_lock.
713 static struct in6_msource
*
714 im6f_graft(struct in6_mfilter
*imf
, const uint8_t st1
,
715 const struct sockaddr_in6
*psin
)
717 struct in6_msource
*lims
;
719 lims
= in6ms_alloc(Z_WAITOK
);
720 lims
->im6s_addr
= psin
->sin6_addr
;
721 lims
->im6sl_st
[0] = MCAST_UNDEFINED
;
722 lims
->im6sl_st
[1] = st1
;
723 RB_INSERT(ip6_msource_tree
, &imf
->im6f_sources
,
724 (struct ip6_msource
*)lims
);
731 * Prune a source entry from an existing socket-layer filter set,
732 * maintaining any required invariants and checking allocations.
734 * The source is marked as being left at t1, it is not freed.
736 * Return 0 if no error occurred, otherwise return an errno value.
738 * Caller is expected to be holding im6o_lock.
741 im6f_prune(struct in6_mfilter
*imf
, const struct sockaddr_in6
*psin
)
743 struct ip6_msource find
;
744 struct ip6_msource
*ims
;
745 struct in6_msource
*lims
;
747 find
.im6s_addr
= psin
->sin6_addr
;
748 ims
= RB_FIND(ip6_msource_tree
, &imf
->im6f_sources
, &find
);
752 lims
= (struct in6_msource
*)ims
;
753 lims
->im6sl_st
[1] = MCAST_UNDEFINED
;
758 * Revert socket-layer filter set deltas at t1 to t0 state.
760 * Caller is expected to be holding im6o_lock.
763 im6f_rollback(struct in6_mfilter
*imf
)
765 struct ip6_msource
*ims
, *tims
;
766 struct in6_msource
*lims
;
768 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &imf
->im6f_sources
, tims
) {
769 lims
= (struct in6_msource
*)ims
;
770 if (lims
->im6sl_st
[0] == lims
->im6sl_st
[1]) {
771 /* no change at t1 */
773 } else if (lims
->im6sl_st
[0] != MCAST_UNDEFINED
) {
774 /* revert change to existing source at t1 */
775 lims
->im6sl_st
[1] = lims
->im6sl_st
[0];
777 /* revert source added t1 */
778 MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__
,
779 (uint64_t)VM_KERNEL_ADDRPERM(lims
)));
780 RB_REMOVE(ip6_msource_tree
, &imf
->im6f_sources
, ims
);
785 imf
->im6f_st
[1] = imf
->im6f_st
[0];
789 * Mark socket-layer filter set as INCLUDE {} at t1.
791 * Caller is expected to be holding im6o_lock.
794 im6f_leave(struct in6_mfilter
*imf
)
796 struct ip6_msource
*ims
;
797 struct in6_msource
*lims
;
799 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
800 lims
= (struct in6_msource
*)ims
;
801 lims
->im6sl_st
[1] = MCAST_UNDEFINED
;
803 imf
->im6f_st
[1] = MCAST_INCLUDE
;
807 * Mark socket-layer filter set deltas as committed.
809 * Caller is expected to be holding im6o_lock.
812 im6f_commit(struct in6_mfilter
*imf
)
814 struct ip6_msource
*ims
;
815 struct in6_msource
*lims
;
817 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
818 lims
= (struct in6_msource
*)ims
;
819 lims
->im6sl_st
[0] = lims
->im6sl_st
[1];
821 imf
->im6f_st
[0] = imf
->im6f_st
[1];
825 * Reap unreferenced sources from socket-layer filter set.
827 * Caller is expected to be holding im6o_lock.
830 im6f_reap(struct in6_mfilter
*imf
)
832 struct ip6_msource
*ims
, *tims
;
833 struct in6_msource
*lims
;
835 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &imf
->im6f_sources
, tims
) {
836 lims
= (struct in6_msource
*)ims
;
837 if ((lims
->im6sl_st
[0] == MCAST_UNDEFINED
) &&
838 (lims
->im6sl_st
[1] == MCAST_UNDEFINED
)) {
839 MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__
,
840 (uint64_t)VM_KERNEL_ADDRPERM(lims
)));
841 RB_REMOVE(ip6_msource_tree
, &imf
->im6f_sources
, ims
);
849 * Purge socket-layer filter set.
851 * Caller is expected to be holding im6o_lock.
854 im6f_purge(struct in6_mfilter
*imf
)
856 struct ip6_msource
*ims
, *tims
;
857 struct in6_msource
*lims
;
859 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &imf
->im6f_sources
, tims
) {
860 lims
= (struct in6_msource
*)ims
;
861 MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__
,
862 (uint64_t)VM_KERNEL_ADDRPERM(lims
)));
863 RB_REMOVE(ip6_msource_tree
, &imf
->im6f_sources
, ims
);
867 imf
->im6f_st
[0] = imf
->im6f_st
[1] = MCAST_UNDEFINED
;
868 VERIFY(RB_EMPTY(&imf
->im6f_sources
));
872 * Look up a source filter entry for a multicast group.
874 * inm is the group descriptor to work with.
875 * addr is the IPv6 address to look up.
876 * noalloc may be non-zero to suppress allocation of sources.
877 * *pims will be set to the address of the retrieved or allocated source.
879 * Return 0 if successful, otherwise return a non-zero error code.
882 in6m_get_source(struct in6_multi
*inm
, const struct in6_addr
*addr
,
883 const int noalloc
, struct ip6_msource
**pims
)
885 struct ip6_msource find
;
886 struct ip6_msource
*ims
, *nims
;
888 IN6M_LOCK_ASSERT_HELD(inm
);
890 find
.im6s_addr
= *addr
;
891 ims
= RB_FIND(ip6_msource_tree
, &inm
->in6m_srcs
, &find
);
892 if (ims
== NULL
&& !noalloc
) {
893 if (inm
->in6m_nsrc
== in6_mcast_maxgrpsrc
) {
896 nims
= ip6ms_alloc(Z_WAITOK
);
897 nims
->im6s_addr
= *addr
;
898 RB_INSERT(ip6_msource_tree
, &inm
->in6m_srcs
, nims
);
901 MLD_PRINTF(("%s: allocated %s as 0x%llx\n", __func__
,
902 ip6_sprintf(addr
), (uint64_t)VM_KERNEL_ADDRPERM(ims
)));
910 * Helper function to derive the filter mode on a source entry
911 * from its internal counters. Predicates are:
912 * A source is only excluded if all listeners exclude it.
913 * A source is only included if no listeners exclude it,
914 * and at least one listener includes it.
915 * May be used by ifmcstat(8).
918 im6s_get_mode(const struct in6_multi
*inm
, const struct ip6_msource
*ims
,
921 IN6M_LOCK_ASSERT_HELD(__DECONST(struct in6_multi
*, inm
));
924 if (inm
->in6m_st
[t
].iss_ex
> 0 &&
925 inm
->in6m_st
[t
].iss_ex
== ims
->im6s_st
[t
].ex
) {
926 return MCAST_EXCLUDE
;
927 } else if (ims
->im6s_st
[t
].in
> 0 && ims
->im6s_st
[t
].ex
== 0) {
928 return MCAST_INCLUDE
;
930 return MCAST_UNDEFINED
;
934 * Merge socket-layer source into MLD-layer source.
935 * If rollback is non-zero, perform the inverse of the merge.
938 im6s_merge(struct ip6_msource
*ims
, const struct in6_msource
*lims
,
941 int n
= rollback
? -1 : 1;
943 if (lims
->im6sl_st
[0] == MCAST_EXCLUDE
) {
944 MLD_PRINTF(("%s: t1 ex -= %d on %s\n", __func__
, n
,
945 ip6_sprintf(&lims
->im6s_addr
)));
946 ims
->im6s_st
[1].ex
-= n
;
947 } else if (lims
->im6sl_st
[0] == MCAST_INCLUDE
) {
948 MLD_PRINTF(("%s: t1 in -= %d on %s\n", __func__
, n
,
949 ip6_sprintf(&lims
->im6s_addr
)));
950 ims
->im6s_st
[1].in
-= n
;
953 if (lims
->im6sl_st
[1] == MCAST_EXCLUDE
) {
954 MLD_PRINTF(("%s: t1 ex += %d on %s\n", __func__
, n
,
955 ip6_sprintf(&lims
->im6s_addr
)));
956 ims
->im6s_st
[1].ex
+= n
;
957 } else if (lims
->im6sl_st
[1] == MCAST_INCLUDE
) {
958 MLD_PRINTF(("%s: t1 in += %d on %s\n", __func__
, n
,
959 ip6_sprintf(&lims
->im6s_addr
)));
960 ims
->im6s_st
[1].in
+= n
;
965 * Atomically update the global in6_multi state, when a membership's
966 * filter list is being updated in any way.
968 * imf is the per-inpcb-membership group filter pointer.
969 * A fake imf may be passed for in-kernel consumers.
971 * XXX This is a candidate for a set-symmetric-difference style loop
972 * which would eliminate the repeated lookup from root of ims nodes,
973 * as they share the same key space.
975 * If any error occurred this function will back out of refcounts
976 * and return a non-zero value.
979 in6m_merge(struct in6_multi
*inm
, /*const*/ struct in6_mfilter
*imf
)
981 struct ip6_msource
*ims
, *nims
= NULL
;
982 struct in6_msource
*lims
;
986 IN6M_LOCK_ASSERT_HELD(inm
);
993 * Update the source filters first, as this may fail.
994 * Maintain count of in-mode filters at t0, t1. These are
995 * used to work out if we transition into ASM mode or not.
996 * Maintain a count of source filters whose state was
997 * actually modified by this operation.
999 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
1000 lims
= (struct in6_msource
*)ims
;
1001 if (lims
->im6sl_st
[0] == imf
->im6f_st
[0]) {
1004 if (lims
->im6sl_st
[1] == imf
->im6f_st
[1]) {
1007 if (lims
->im6sl_st
[0] == lims
->im6sl_st
[1]) {
1010 error
= in6m_get_source(inm
, &lims
->im6s_addr
, 0, &nims
);
1015 im6s_merge(nims
, lims
, 0);
1018 struct ip6_msource
*bims
;
1020 RB_FOREACH_REVERSE_FROM(ims
, ip6_msource_tree
, nims
) {
1021 lims
= (struct in6_msource
*)ims
;
1022 if (lims
->im6sl_st
[0] == lims
->im6sl_st
[1]) {
1025 (void) in6m_get_source(inm
, &lims
->im6s_addr
, 1, &bims
);
1029 im6s_merge(bims
, lims
, 1);
1034 MLD_PRINTF(("%s: imf filters in-mode: %d at t0, %d at t1\n",
1035 __func__
, nsrc0
, nsrc1
));
1037 /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */
1038 if (imf
->im6f_st
[0] == imf
->im6f_st
[1] &&
1039 imf
->im6f_st
[1] == MCAST_INCLUDE
) {
1041 MLD_PRINTF(("%s: --in on inm at t1\n", __func__
));
1042 --inm
->in6m_st
[1].iss_in
;
1046 /* Handle filter mode transition on socket. */
1047 if (imf
->im6f_st
[0] != imf
->im6f_st
[1]) {
1048 MLD_PRINTF(("%s: imf transition %d to %d\n",
1049 __func__
, imf
->im6f_st
[0], imf
->im6f_st
[1]));
1051 if (imf
->im6f_st
[0] == MCAST_EXCLUDE
) {
1052 MLD_PRINTF(("%s: --ex on inm at t1\n", __func__
));
1053 --inm
->in6m_st
[1].iss_ex
;
1054 } else if (imf
->im6f_st
[0] == MCAST_INCLUDE
) {
1055 MLD_PRINTF(("%s: --in on inm at t1\n", __func__
));
1056 --inm
->in6m_st
[1].iss_in
;
1059 if (imf
->im6f_st
[1] == MCAST_EXCLUDE
) {
1060 MLD_PRINTF(("%s: ex++ on inm at t1\n", __func__
));
1061 inm
->in6m_st
[1].iss_ex
++;
1062 } else if (imf
->im6f_st
[1] == MCAST_INCLUDE
&& nsrc1
> 0) {
1063 MLD_PRINTF(("%s: in++ on inm at t1\n", __func__
));
1064 inm
->in6m_st
[1].iss_in
++;
1069 * Track inm filter state in terms of listener counts.
1070 * If there are any exclusive listeners, stack-wide
1071 * membership is exclusive.
1072 * Otherwise, if only inclusive listeners, stack-wide is inclusive.
1073 * If no listeners remain, state is undefined at t1,
1074 * and the MLD lifecycle for this group should finish.
1076 if (inm
->in6m_st
[1].iss_ex
> 0) {
1077 MLD_PRINTF(("%s: transition to EX\n", __func__
));
1078 inm
->in6m_st
[1].iss_fmode
= MCAST_EXCLUDE
;
1079 } else if (inm
->in6m_st
[1].iss_in
> 0) {
1080 MLD_PRINTF(("%s: transition to IN\n", __func__
));
1081 inm
->in6m_st
[1].iss_fmode
= MCAST_INCLUDE
;
1083 MLD_PRINTF(("%s: transition to UNDEF\n", __func__
));
1084 inm
->in6m_st
[1].iss_fmode
= MCAST_UNDEFINED
;
1087 /* Decrement ASM listener count on transition out of ASM mode. */
1088 if (imf
->im6f_st
[0] == MCAST_EXCLUDE
&& nsrc0
== 0) {
1089 if ((imf
->im6f_st
[1] != MCAST_EXCLUDE
) ||
1090 (imf
->im6f_st
[1] == MCAST_EXCLUDE
&& nsrc1
> 0)) {
1091 MLD_PRINTF(("%s: --asm on inm at t1\n", __func__
));
1092 --inm
->in6m_st
[1].iss_asm
;
1096 /* Increment ASM listener count on transition to ASM mode. */
1097 if (imf
->im6f_st
[1] == MCAST_EXCLUDE
&& nsrc1
== 0) {
1098 MLD_PRINTF(("%s: asm++ on inm at t1\n", __func__
));
1099 inm
->in6m_st
[1].iss_asm
++;
1102 MLD_PRINTF(("%s: merged imf 0x%llx to inm 0x%llx\n", __func__
,
1103 (uint64_t)VM_KERNEL_ADDRPERM(imf
),
1104 (uint64_t)VM_KERNEL_ADDRPERM(inm
)));
1109 MLD_PRINTF(("%s: sources changed; reaping\n", __func__
));
1116 * Mark an in6_multi's filter set deltas as committed.
1117 * Called by MLD after a state change has been enqueued.
1120 in6m_commit(struct in6_multi
*inm
)
1122 struct ip6_msource
*ims
;
1124 IN6M_LOCK_ASSERT_HELD(inm
);
1126 MLD_PRINTF(("%s: commit inm 0x%llx\n", __func__
,
1127 (uint64_t)VM_KERNEL_ADDRPERM(inm
)));
1128 MLD_PRINTF(("%s: pre commit:\n", __func__
));
1131 RB_FOREACH(ims
, ip6_msource_tree
, &inm
->in6m_srcs
) {
1132 ims
->im6s_st
[0] = ims
->im6s_st
[1];
1134 inm
->in6m_st
[0] = inm
->in6m_st
[1];
1138 * Reap unreferenced nodes from an in6_multi's filter set.
1141 in6m_reap(struct in6_multi
*inm
)
1143 struct ip6_msource
*ims
, *tims
;
1145 IN6M_LOCK_ASSERT_HELD(inm
);
1147 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &inm
->in6m_srcs
, tims
) {
1148 if (ims
->im6s_st
[0].ex
> 0 || ims
->im6s_st
[0].in
> 0 ||
1149 ims
->im6s_st
[1].ex
> 0 || ims
->im6s_st
[1].in
> 0 ||
1150 ims
->im6s_stp
!= 0) {
1153 MLD_PRINTF(("%s: free ims 0x%llx\n", __func__
,
1154 (uint64_t)VM_KERNEL_ADDRPERM(ims
)));
1155 RB_REMOVE(ip6_msource_tree
, &inm
->in6m_srcs
, ims
);
1162 * Purge all source nodes from an in6_multi's filter set.
1165 in6m_purge(struct in6_multi
*inm
)
1167 struct ip6_msource
*ims
, *tims
;
1169 IN6M_LOCK_ASSERT_HELD(inm
);
1171 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &inm
->in6m_srcs
, tims
) {
1172 MLD_PRINTF(("%s: free ims 0x%llx\n", __func__
,
1173 (uint64_t)VM_KERNEL_ADDRPERM(ims
)));
1174 RB_REMOVE(ip6_msource_tree
, &inm
->in6m_srcs
, ims
);
1181 * Join a multicast address w/o sources.
1182 * KAME compatibility entry point.
1185 struct in6_multi_mship
*
1186 in6_joingroup(struct ifnet
*ifp
, struct in6_addr
*mcaddr
,
1187 int *errorp
, int delay
)
1189 struct in6_multi_mship
*imm
;
1194 imm
= in6_multi_mship_alloc(Z_WAITOK
);
1196 error
= in6_mc_join(ifp
, mcaddr
, NULL
, &imm
->i6mm_maddr
, delay
);
1199 in6_multi_mship_free(imm
);
1207 * Leave a multicast address w/o sources.
1208 * KAME compatibility entry point.
1211 in6_leavegroup(struct in6_multi_mship
*imm
)
1213 if (imm
->i6mm_maddr
!= NULL
) {
1214 in6_mc_leave(imm
->i6mm_maddr
, NULL
);
1215 IN6M_REMREF(imm
->i6mm_maddr
);
1216 imm
->i6mm_maddr
= NULL
;
1218 in6_multi_mship_free(imm
);
1223 * Join a multicast group; real entry point.
1225 * Only preserves atomicity at inm level.
1226 * NOTE: imf argument cannot be const due to sys/tree.h limitations.
1228 * If the MLD downcall fails, the group is not joined, and an error
1232 in6_mc_join(struct ifnet
*ifp
, const struct in6_addr
*mcaddr
,
1233 /*const*/ struct in6_mfilter
*imf
, struct in6_multi
**pinm
,
1236 struct in6_mfilter timf
;
1237 struct in6_multi
*inm
= NULL
;
1239 struct mld_tparams mtp
;
1242 * Sanity: Check scope zone ID was set for ifp, if and
1243 * only if group is scoped to an interface.
1245 VERIFY(IN6_IS_ADDR_MULTICAST(mcaddr
));
1246 if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr
) ||
1247 IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr
)) {
1248 VERIFY(mcaddr
->s6_addr16
[1] != 0);
1251 MLD_PRINTF(("%s: join %s on 0x%llx(%s))\n", __func__
,
1252 ip6_sprintf(mcaddr
), (uint64_t)VM_KERNEL_ADDRPERM(ifp
),
1255 bzero(&mtp
, sizeof(mtp
));
1259 * If no imf was specified (i.e. kernel consumer),
1260 * fake one up and assume it is an ASM join.
1263 im6f_init(&timf
, MCAST_UNDEFINED
, MCAST_EXCLUDE
);
1267 error
= in6_mc_get(ifp
, mcaddr
, &inm
);
1269 MLD_PRINTF(("%s: in6_mc_get() failure\n", __func__
));
1273 MLD_PRINTF(("%s: merge inm state\n", __func__
));
1276 error
= in6m_merge(inm
, imf
);
1278 MLD_PRINTF(("%s: failed to merge inm state\n", __func__
));
1279 goto out_in6m_release
;
1282 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
1283 error
= mld_change_state(inm
, &mtp
, delay
);
1285 MLD_PRINTF(("%s: failed to update source\n", __func__
));
1287 goto out_in6m_release
;
1292 MLD_PRINTF(("%s: dropping ref on 0x%llx\n", __func__
,
1293 (uint64_t)VM_KERNEL_ADDRPERM(inm
)));
1298 *pinm
= inm
; /* keep refcount from in6_mc_get() */
1301 /* schedule timer now that we've dropped the lock(s) */
1302 mld_set_timeout(&mtp
);
1308 * Leave a multicast group; real entry point.
1309 * All source filters will be expunged.
1311 * Only preserves atomicity at inm level.
1313 * Holding the write lock for the INP which contains imf
1314 * is highly advisable. We can't assert for it as imf does not
1315 * contain a back-pointer to the owning inp.
1317 * Note: This is not the same as in6m_release(*) as this function also
1318 * makes a state change downcall into MLD.
1321 in6_mc_leave(struct in6_multi
*inm
, /*const*/ struct in6_mfilter
*imf
)
1323 struct in6_mfilter timf
;
1325 struct mld_tparams mtp
;
1327 bzero(&mtp
, sizeof(mtp
));
1330 IN6M_LOCK_ASSERT_NOTHELD(inm
);
1332 in6_multihead_lock_exclusive();
1335 MLD_PRINTF(("%s: leave inm 0x%llx, %s/%s%d, imf 0x%llx\n", __func__
,
1336 (uint64_t)VM_KERNEL_ADDRPERM(inm
), ip6_sprintf(&inm
->in6m_addr
),
1337 (in6m_is_ifp_detached(inm
) ? "null" : inm
->in6m_ifp
->if_name
),
1338 inm
->in6m_ifp
->if_unit
, (uint64_t)VM_KERNEL_ADDRPERM(imf
)));
1341 * If no imf was specified (i.e. kernel consumer),
1342 * fake one up and assume it is an ASM join.
1345 im6f_init(&timf
, MCAST_EXCLUDE
, MCAST_UNDEFINED
);
1350 * Begin state merge transaction at MLD layer.
1352 * As this particular invocation should not cause any memory
1353 * to be allocated, and there is no opportunity to roll back
1354 * the transaction, it MUST NOT fail.
1356 MLD_PRINTF(("%s: merge inm state\n", __func__
));
1358 error
= in6m_merge(inm
, imf
);
1359 KASSERT(error
== 0, ("%s: failed to merge inm state\n", __func__
));
1361 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
1362 error
= mld_change_state(inm
, &mtp
, 0);
1365 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
1368 lastref
= in6_multi_detach(inm
);
1369 VERIFY(!lastref
|| (!(inm
->in6m_debug
& IFD_ATTACHED
) &&
1370 inm
->in6m_reqcnt
== 0));
1372 in6_multihead_lock_done();
1375 IN6M_REMREF(inm
); /* for in6_multihead list */
1377 /* schedule timer now that we've dropped the lock(s) */
1378 mld_set_timeout(&mtp
);
1384 * Block or unblock an ASM multicast source on an inpcb.
1385 * This implements the delta-based API described in RFC 3678.
1387 * The delta-based API applies only to exclusive-mode memberships.
1388 * An MLD downcall will be performed.
1390 * Return 0 if successful, otherwise return an appropriate error code.
1393 in6p_block_unblock_source(struct inpcb
*inp
, struct sockopt
*sopt
)
1395 struct group_source_req gsr
;
1396 struct sockaddr_in6
*gsa
, *ssa
;
1398 struct in6_mfilter
*imf
;
1399 struct ip6_moptions
*imo
;
1400 struct in6_msource
*ims
;
1401 struct in6_multi
*inm
;
1405 struct mld_tparams mtp
;
1407 bzero(&mtp
, sizeof(mtp
));
1412 memset(&gsr
, 0, sizeof(struct group_source_req
));
1413 gsa
= (struct sockaddr_in6
*)&gsr
.gsr_group
;
1414 ssa
= (struct sockaddr_in6
*)&gsr
.gsr_source
;
1416 switch (sopt
->sopt_name
) {
1417 case MCAST_BLOCK_SOURCE
:
1418 case MCAST_UNBLOCK_SOURCE
:
1419 error
= sooptcopyin(sopt
, &gsr
,
1420 sizeof(struct group_source_req
),
1421 sizeof(struct group_source_req
));
1426 if (gsa
->sin6_family
!= AF_INET6
||
1427 gsa
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
1431 if (ssa
->sin6_family
!= AF_INET6
||
1432 ssa
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
1436 ifnet_head_lock_shared();
1437 if (gsr
.gsr_interface
== 0 ||
1438 (u_int
)if_index
< gsr
.gsr_interface
) {
1440 return EADDRNOTAVAIL
;
1443 ifp
= ifindex2ifnet
[gsr
.gsr_interface
];
1447 return EADDRNOTAVAIL
;
1450 if (sopt
->sopt_name
== MCAST_BLOCK_SOURCE
) {
1456 MLD_PRINTF(("%s: unknown sopt_name %d\n",
1457 __func__
, sopt
->sopt_name
));
1461 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6_addr
)) {
1465 (void) in6_setscope(&gsa
->sin6_addr
, ifp
, NULL
);
1468 * Check if we are actually a member of this group.
1470 imo
= in6p_findmoptions(inp
);
1476 idx
= im6o_match_group(imo
, ifp
, gsa
);
1477 if (idx
== (size_t)-1 || imo
->im6o_mfilters
== NULL
) {
1478 error
= EADDRNOTAVAIL
;
1479 goto out_imo_locked
;
1482 VERIFY(imo
->im6o_mfilters
!= NULL
);
1483 imf
= &imo
->im6o_mfilters
[idx
];
1484 inm
= imo
->im6o_membership
[idx
];
1487 * Attempting to use the delta-based API on an
1488 * non exclusive-mode membership is an error.
1490 fmode
= imf
->im6f_st
[0];
1491 if (fmode
!= MCAST_EXCLUDE
) {
1493 goto out_imo_locked
;
1497 * Deal with error cases up-front:
1498 * Asked to block, but already blocked; or
1499 * Asked to unblock, but nothing to unblock.
1500 * If adding a new block entry, allocate it.
1502 ims
= im6o_match_source(imo
, idx
, ssa
);
1503 if ((ims
!= NULL
&& doblock
) || (ims
== NULL
&& !doblock
)) {
1504 MLD_PRINTF(("%s: source %s %spresent\n", __func__
,
1505 ip6_sprintf(&ssa
->sin6_addr
),
1506 doblock
? "" : "not "));
1507 error
= EADDRNOTAVAIL
;
1508 goto out_imo_locked
;
1512 * Begin state merge transaction at socket layer.
1515 MLD_PRINTF(("%s: %s source\n", __func__
, "block"));
1516 ims
= im6f_graft(imf
, fmode
, ssa
);
1521 MLD_PRINTF(("%s: %s source\n", __func__
, "allow"));
1522 error
= im6f_prune(imf
, ssa
);
1526 MLD_PRINTF(("%s: merge imf state failed\n", __func__
));
1527 goto out_im6f_rollback
;
1531 * Begin state merge transaction at MLD layer.
1534 MLD_PRINTF(("%s: merge inm state\n", __func__
));
1535 error
= in6m_merge(inm
, imf
);
1537 MLD_PRINTF(("%s: failed to merge inm state\n", __func__
));
1539 goto out_im6f_rollback
;
1542 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
1543 error
= mld_change_state(inm
, &mtp
, 0);
1547 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
1562 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
1564 /* schedule timer now that we've dropped the lock(s) */
1565 mld_set_timeout(&mtp
);
1571 * Given an inpcb, return its multicast options structure pointer. Accepts
1572 * an unlocked inpcb pointer, but will return it locked. May sleep.
1575 static struct ip6_moptions
*
1576 in6p_findmoptions(struct inpcb
*inp
)
1578 struct ip6_moptions
*imo
;
1579 struct in6_multi
**immp
;
1580 struct in6_mfilter
*imfp
;
1583 if ((imo
= inp
->in6p_moptions
) != NULL
) {
1584 IM6O_ADDREF(imo
); /* for caller */
1588 imo
= ip6_allocmoptions(Z_WAITOK
);
1593 immp
= _MALLOC(sizeof(*immp
) * IPV6_MIN_MEMBERSHIPS
, M_IP6MOPTS
,
1600 imfp
= _MALLOC(sizeof(struct in6_mfilter
) * IPV6_MIN_MEMBERSHIPS
,
1601 M_IN6MFILTER
, M_WAITOK
| M_ZERO
);
1603 _FREE(immp
, M_IP6MOPTS
);
1608 imo
->im6o_multicast_ifp
= NULL
;
1609 imo
->im6o_multicast_hlim
= (u_char
)ip6_defmcasthlim
;
1610 imo
->im6o_multicast_loop
= (u_char
)in6_mcast_loop
;
1611 imo
->im6o_num_memberships
= 0;
1612 imo
->im6o_max_memberships
= IPV6_MIN_MEMBERSHIPS
;
1613 imo
->im6o_membership
= immp
;
1615 /* Initialize per-group source filters. */
1616 for (idx
= 0; idx
< IPV6_MIN_MEMBERSHIPS
; idx
++) {
1617 im6f_init(&imfp
[idx
], MCAST_UNDEFINED
, MCAST_EXCLUDE
);
1620 imo
->im6o_mfilters
= imfp
;
1621 inp
->in6p_moptions
= imo
; /* keep reference from ip6_allocmoptions() */
1622 IM6O_ADDREF(imo
); /* for caller */
1628 * Atomically get source filters on a socket for an IPv6 multicast group.
1629 * Called with INP lock held; returns with lock released.
1632 in6p_get_source_filters(struct inpcb
*inp
, struct sockopt
*sopt
)
1634 struct __msfilterreq64 msfr
= {}, msfr64
;
1635 struct __msfilterreq32 msfr32
;
1636 struct sockaddr_in6
*gsa
;
1638 struct ip6_moptions
*imo
;
1639 struct in6_mfilter
*imf
;
1640 struct ip6_msource
*ims
;
1641 struct in6_msource
*lims
;
1642 struct sockaddr_in6
*psin
;
1643 struct sockaddr_storage
*ptss
;
1644 struct sockaddr_storage
*tss
;
1646 size_t idx
, nsrcs
, ncsrcs
;
1647 user_addr_t tmp_ptr
;
1649 imo
= inp
->in6p_moptions
;
1650 VERIFY(imo
!= NULL
);
1652 if (IS_64BIT_PROCESS(current_proc())) {
1653 error
= sooptcopyin(sopt
, &msfr64
,
1654 sizeof(struct __msfilterreq64
),
1655 sizeof(struct __msfilterreq64
));
1659 /* we never use msfr.msfr_srcs; */
1660 memcpy(&msfr
, &msfr64
, sizeof(msfr64
));
1662 error
= sooptcopyin(sopt
, &msfr32
,
1663 sizeof(struct __msfilterreq32
),
1664 sizeof(struct __msfilterreq32
));
1668 /* we never use msfr.msfr_srcs; */
1669 memcpy(&msfr
, &msfr32
, sizeof(msfr32
));
1672 if (msfr
.msfr_group
.ss_family
!= AF_INET6
||
1673 msfr
.msfr_group
.ss_len
!= sizeof(struct sockaddr_in6
)) {
1677 gsa
= (struct sockaddr_in6
*)&msfr
.msfr_group
;
1678 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6_addr
)) {
1682 ifnet_head_lock_shared();
1683 if (msfr
.msfr_ifindex
== 0 || (u_int
)if_index
< msfr
.msfr_ifindex
) {
1685 return EADDRNOTAVAIL
;
1687 ifp
= ifindex2ifnet
[msfr
.msfr_ifindex
];
1691 return EADDRNOTAVAIL
;
1694 if ((size_t) msfr
.msfr_nsrcs
>
1695 UINT32_MAX
/ sizeof(struct sockaddr_storage
)) {
1696 msfr
.msfr_nsrcs
= UINT32_MAX
/ sizeof(struct sockaddr_storage
);
1699 if (msfr
.msfr_nsrcs
> in6_mcast_maxsocksrc
) {
1700 msfr
.msfr_nsrcs
= (uint32_t)in6_mcast_maxsocksrc
;
1703 (void)in6_setscope(&gsa
->sin6_addr
, ifp
, NULL
);
1707 * Lookup group on the socket.
1709 idx
= im6o_match_group(imo
, ifp
, gsa
);
1710 if (idx
== (size_t)-1 || imo
->im6o_mfilters
== NULL
) {
1712 return EADDRNOTAVAIL
;
1714 imf
= &imo
->im6o_mfilters
[idx
];
1717 * Ignore memberships which are in limbo.
1719 if (imf
->im6f_st
[1] == MCAST_UNDEFINED
) {
1723 msfr
.msfr_fmode
= imf
->im6f_st
[1];
1726 * If the user specified a buffer, copy out the source filter
1727 * entries to userland gracefully.
1728 * We only copy out the number of entries which userland
1729 * has asked for, but we always tell userland how big the
1730 * buffer really needs to be.
1734 if (IS_64BIT_PROCESS(current_proc())) {
1735 tmp_ptr
= (user_addr_t
)msfr64
.msfr_srcs
;
1737 tmp_ptr
= CAST_USER_ADDR_T(msfr32
.msfr_srcs
);
1740 if (tmp_ptr
!= USER_ADDR_NULL
&& msfr
.msfr_nsrcs
> 0) {
1741 tss
= _MALLOC((size_t) msfr
.msfr_nsrcs
* sizeof(*tss
),
1742 M_TEMP
, M_WAITOK
| M_ZERO
);
1750 * Count number of sources in-mode at t0.
1751 * If buffer space exists and remains, copy out source entries.
1753 nsrcs
= msfr
.msfr_nsrcs
;
1756 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
1757 lims
= (struct in6_msource
*)ims
;
1758 if (lims
->im6sl_st
[0] == MCAST_UNDEFINED
||
1759 lims
->im6sl_st
[0] != imf
->im6f_st
[0]) {
1762 if (tss
!= NULL
&& nsrcs
> 0) {
1763 psin
= (struct sockaddr_in6
*)ptss
;
1764 psin
->sin6_family
= AF_INET6
;
1765 psin
->sin6_len
= sizeof(struct sockaddr_in6
);
1766 psin
->sin6_addr
= lims
->im6s_addr
;
1767 psin
->sin6_port
= 0;
1777 error
= copyout(tss
, tmp_ptr
, ncsrcs
* sizeof(*tss
));
1784 msfr
.msfr_nsrcs
= (uint32_t)ncsrcs
;
1785 if (IS_64BIT_PROCESS(current_proc())) {
1786 msfr64
.msfr_ifindex
= msfr
.msfr_ifindex
;
1787 msfr64
.msfr_fmode
= msfr
.msfr_fmode
;
1788 msfr64
.msfr_nsrcs
= msfr
.msfr_nsrcs
;
1789 memcpy(&msfr64
.msfr_group
, &msfr
.msfr_group
,
1790 sizeof(struct sockaddr_storage
));
1791 error
= sooptcopyout(sopt
, &msfr64
,
1792 sizeof(struct __msfilterreq64
));
1794 msfr32
.msfr_ifindex
= msfr
.msfr_ifindex
;
1795 msfr32
.msfr_fmode
= msfr
.msfr_fmode
;
1796 msfr32
.msfr_nsrcs
= msfr
.msfr_nsrcs
;
1797 memcpy(&msfr32
.msfr_group
, &msfr
.msfr_group
,
1798 sizeof(struct sockaddr_storage
));
1799 error
= sooptcopyout(sopt
, &msfr32
,
1800 sizeof(struct __msfilterreq32
));
1807 * Return the IP multicast options in response to user getsockopt().
1810 ip6_getmoptions(struct inpcb
*inp
, struct sockopt
*sopt
)
1812 struct ip6_moptions
*im6o
;
1816 im6o
= inp
->in6p_moptions
;
1818 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
1819 * or is a divert socket, reject it.
1821 if (SOCK_PROTO(inp
->inp_socket
) == IPPROTO_DIVERT
||
1822 (SOCK_TYPE(inp
->inp_socket
) != SOCK_RAW
&&
1823 SOCK_TYPE(inp
->inp_socket
) != SOCK_DGRAM
)) {
1828 switch (sopt
->sopt_name
) {
1829 case IPV6_MULTICAST_IF
:
1833 if (im6o
== NULL
|| im6o
->im6o_multicast_ifp
== NULL
) {
1836 optval
= im6o
->im6o_multicast_ifp
->if_index
;
1841 error
= sooptcopyout(sopt
, &optval
, sizeof(u_int
));
1844 case IPV6_MULTICAST_HOPS
:
1846 optval
= ip6_defmcasthlim
;
1849 optval
= im6o
->im6o_multicast_hlim
;
1852 error
= sooptcopyout(sopt
, &optval
, sizeof(u_int
));
1855 case IPV6_MULTICAST_LOOP
:
1857 optval
= in6_mcast_loop
; /* XXX VIMAGE */
1860 optval
= im6o
->im6o_multicast_loop
;
1863 error
= sooptcopyout(sopt
, &optval
, sizeof(u_int
));
1868 error
= EADDRNOTAVAIL
;
1870 error
= in6p_get_source_filters(inp
, sopt
);
1875 error
= ENOPROTOOPT
;
1883 * Look up the ifnet to use for a multicast group membership,
1884 * given the address of an IPv6 group.
1886 * This routine exists to support legacy IPv6 multicast applications.
1888 * If inp is non-NULL and is bound to an interface, use this socket's
1889 * inp_boundif for any required routing table lookup.
1891 * If the route lookup fails, return NULL.
1893 * FUTURE: Support multiple forwarding tables for IPv6.
1895 * Returns NULL if no ifp could be found.
1897 static struct ifnet
*
1898 in6p_lookup_mcast_ifp(const struct inpcb
*in6p
,
1899 const struct sockaddr_in6
*gsin6
)
1901 struct route_in6 ro6
;
1903 unsigned int ifscope
= IFSCOPE_NONE
;
1905 VERIFY(in6p
== NULL
|| (in6p
->inp_vflag
& INP_IPV6
));
1906 VERIFY(gsin6
->sin6_family
== AF_INET6
);
1907 if (IN6_IS_ADDR_MULTICAST(&gsin6
->sin6_addr
) == 0) {
1911 if (in6p
!= NULL
&& (in6p
->inp_flags
& INP_BOUND_IF
)) {
1912 ifscope
= in6p
->inp_boundifp
->if_index
;
1916 memset(&ro6
, 0, sizeof(struct route_in6
));
1917 memcpy(&ro6
.ro_dst
, gsin6
, sizeof(struct sockaddr_in6
));
1918 rtalloc_scoped_ign((struct route
*)&ro6
, 0, ifscope
);
1919 if (ro6
.ro_rt
!= NULL
) {
1920 ifp
= ro6
.ro_rt
->rt_ifp
;
1921 VERIFY(ifp
!= NULL
);
1923 ROUTE_RELEASE(&ro6
);
1929 * Since ipv6_mreq contains an ifindex and ip_mreq contains an AF_INET
1930 * address, we need to lookup the AF_INET address when translating an
1931 * ipv6_mreq structure into an ipmreq structure.
1932 * This is used when userland performs multicast setsockopt() on AF_INET6
1933 * sockets with AF_INET multicast addresses (IPv6 v4 mapped addresses).
1936 in6p_lookup_v4addr(struct ipv6_mreq
*mreq
, struct ip_mreq
*v4mreq
)
1940 struct sockaddr_in
*sin
;
1942 ifnet_head_lock_shared();
1943 if (mreq
->ipv6mr_interface
> (unsigned int)if_index
) {
1945 return EADDRNOTAVAIL
;
1947 ifp
= ifindex2ifnet
[mreq
->ipv6mr_interface
];
1951 return EADDRNOTAVAIL
;
1953 ifa
= ifa_ifpgetprimary(ifp
, AF_INET
);
1955 return EADDRNOTAVAIL
;
1957 sin
= (struct sockaddr_in
*)(uintptr_t)(size_t)ifa
->ifa_addr
;
1958 v4mreq
->imr_interface
.s_addr
= sin
->sin_addr
.s_addr
;
1965 * Join an IPv6 multicast group, possibly with a source.
1967 * FIXME: The KAME use of the unspecified address (::)
1968 * to join *all* multicast groups is currently unsupported.
1971 in6p_join_group(struct inpcb
*inp
, struct sockopt
*sopt
)
1973 struct group_source_req gsr
;
1974 struct sockaddr_in6
*gsa
, *ssa
;
1976 struct in6_mfilter
*imf
;
1977 struct ip6_moptions
*imo
;
1978 struct in6_multi
*inm
= NULL
;
1979 struct in6_msource
*lims
= NULL
;
1982 uint32_t scopeid
= 0;
1983 struct mld_tparams mtp
;
1985 bzero(&mtp
, sizeof(mtp
));
1991 memset(&gsr
, 0, sizeof(struct group_source_req
));
1992 gsa
= (struct sockaddr_in6
*)&gsr
.gsr_group
;
1993 ssa
= (struct sockaddr_in6
*)&gsr
.gsr_source
;
1996 * Chew everything into struct group_source_req.
1997 * Overwrite the port field if present, as the sockaddr
1998 * being copied in may be matched with a binary comparison.
1999 * Ignore passed-in scope ID.
2001 switch (sopt
->sopt_name
) {
2002 case IPV6_JOIN_GROUP
: {
2003 struct ipv6_mreq mreq
;
2005 error
= sooptcopyin(sopt
, &mreq
, sizeof(struct ipv6_mreq
),
2006 sizeof(struct ipv6_mreq
));
2010 if (IN6_IS_ADDR_V4MAPPED(&mreq
.ipv6mr_multiaddr
)) {
2011 struct ip_mreq v4mreq
;
2012 struct sockopt v4sopt
;
2014 v4mreq
.imr_multiaddr
.s_addr
=
2015 mreq
.ipv6mr_multiaddr
.s6_addr32
[3];
2016 if (mreq
.ipv6mr_interface
== 0) {
2017 v4mreq
.imr_interface
.s_addr
= INADDR_ANY
;
2019 error
= in6p_lookup_v4addr(&mreq
, &v4mreq
);
2024 v4sopt
.sopt_dir
= SOPT_SET
;
2025 v4sopt
.sopt_level
= sopt
->sopt_level
;
2026 v4sopt
.sopt_name
= IP_ADD_MEMBERSHIP
;
2027 v4sopt
.sopt_val
= CAST_USER_ADDR_T(&v4mreq
);
2028 v4sopt
.sopt_valsize
= sizeof(v4mreq
);
2029 v4sopt
.sopt_p
= kernproc
;
2031 return inp_join_group(inp
, &v4sopt
);
2033 gsa
->sin6_family
= AF_INET6
;
2034 gsa
->sin6_len
= sizeof(struct sockaddr_in6
);
2035 gsa
->sin6_addr
= mreq
.ipv6mr_multiaddr
;
2037 /* Only allow IPv6 multicast addresses */
2038 if (IN6_IS_ADDR_MULTICAST(&gsa
->sin6_addr
) == 0) {
2042 if (mreq
.ipv6mr_interface
== 0) {
2043 ifp
= in6p_lookup_mcast_ifp(inp
, gsa
);
2045 ifnet_head_lock_shared();
2046 if ((u_int
)if_index
< mreq
.ipv6mr_interface
) {
2048 return EADDRNOTAVAIL
;
2050 ifp
= ifindex2ifnet
[mreq
.ipv6mr_interface
];
2053 MLD_PRINTF(("%s: ipv6mr_interface = %d, ifp = 0x%llx\n",
2054 __func__
, mreq
.ipv6mr_interface
,
2055 (uint64_t)VM_KERNEL_ADDRPERM(ifp
)));
2059 case MCAST_JOIN_GROUP
:
2060 case MCAST_JOIN_SOURCE_GROUP
:
2061 if (sopt
->sopt_name
== MCAST_JOIN_GROUP
) {
2062 error
= sooptcopyin(sopt
, &gsr
,
2063 sizeof(struct group_req
),
2064 sizeof(struct group_req
));
2065 } else if (sopt
->sopt_name
== MCAST_JOIN_SOURCE_GROUP
) {
2066 error
= sooptcopyin(sopt
, &gsr
,
2067 sizeof(struct group_source_req
),
2068 sizeof(struct group_source_req
));
2074 if (gsa
->sin6_family
!= AF_INET6
||
2075 gsa
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
2079 if (sopt
->sopt_name
== MCAST_JOIN_SOURCE_GROUP
) {
2080 if (ssa
->sin6_family
!= AF_INET6
||
2081 ssa
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
2084 if (IN6_IS_ADDR_MULTICAST(&ssa
->sin6_addr
)) {
2088 * TODO: Validate embedded scope ID in source
2089 * list entry against passed-in ifp, if and only
2090 * if source list filter entry is iface or node local.
2092 in6_clearscope(&ssa
->sin6_addr
);
2094 ssa
->sin6_scope_id
= 0;
2097 ifnet_head_lock_shared();
2098 if (gsr
.gsr_interface
== 0 ||
2099 (u_int
)if_index
< gsr
.gsr_interface
) {
2101 return EADDRNOTAVAIL
;
2103 ifp
= ifindex2ifnet
[gsr
.gsr_interface
];
2108 MLD_PRINTF(("%s: unknown sopt_name %d\n",
2109 __func__
, sopt
->sopt_name
));
2113 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6_addr
)) {
2117 if (ifp
== NULL
|| (ifp
->if_flags
& IFF_MULTICAST
) == 0) {
2118 return EADDRNOTAVAIL
;
2121 INC_ATOMIC_INT64_LIM(net_api_stats
.nas_socket_mcast_join_total
);
2123 * TBD: revisit the criteria for non-OS initiated joins
2125 if (inp
->inp_lport
== htons(5353)) {
2126 INC_ATOMIC_INT64_LIM(net_api_stats
.nas_socket_mcast_join_os_total
);
2130 gsa
->sin6_scope_id
= 0;
2133 * Always set the scope zone ID on memberships created from userland.
2134 * Use the passed-in ifp to do this.
2136 (void)in6_setscope(&gsa
->sin6_addr
, ifp
, &scopeid
);
2138 * Some addresses are not valid without an embedded scopeid.
2139 * This check must be present because otherwise we will later hit
2140 * a VERIFY() in in6_mc_join().
2142 if ((IN6_IS_ADDR_MC_LINKLOCAL(&gsa
->sin6_addr
) ||
2143 IN6_IS_ADDR_MC_INTFACELOCAL(&gsa
->sin6_addr
)) &&
2144 (scopeid
== 0 || gsa
->sin6_addr
.s6_addr16
[1] == 0)) {
2148 imo
= in6p_findmoptions(inp
);
2154 idx
= im6o_match_group(imo
, ifp
, gsa
);
2155 if (idx
== (size_t)-1) {
2158 inm
= imo
->im6o_membership
[idx
];
2159 imf
= &imo
->im6o_mfilters
[idx
];
2160 if (ssa
->sin6_family
!= AF_UNSPEC
) {
2162 * MCAST_JOIN_SOURCE_GROUP on an exclusive membership
2163 * is an error. On an existing inclusive membership,
2164 * it just adds the source to the filter list.
2166 if (imf
->im6f_st
[1] != MCAST_INCLUDE
) {
2168 goto out_imo_locked
;
2171 * Throw out duplicates.
2173 * XXX FIXME: This makes a naive assumption that
2174 * even if entries exist for *ssa in this imf,
2175 * they will be rejected as dupes, even if they
2176 * are not valid in the current mode (in-mode).
2178 * in6_msource is transactioned just as for anything
2179 * else in SSM -- but note naive use of in6m_graft()
2180 * below for allocating new filter entries.
2182 * This is only an issue if someone mixes the
2183 * full-state SSM API with the delta-based API,
2184 * which is discouraged in the relevant RFCs.
2186 lims
= im6o_match_source(imo
, idx
, ssa
);
2187 if (lims
!= NULL
/*&&
2188 * lims->im6sl_st[1] == MCAST_INCLUDE*/) {
2189 error
= EADDRNOTAVAIL
;
2190 goto out_imo_locked
;
2194 * MCAST_JOIN_GROUP on an existing exclusive
2195 * membership is an error; return EADDRINUSE
2196 * to preserve 4.4BSD API idempotence, and
2197 * avoid tedious detour to code below.
2198 * NOTE: This is bending RFC 3678 a bit.
2200 * On an existing inclusive membership, this is also
2201 * an error; if you want to change filter mode,
2202 * you must use the userland API setsourcefilter().
2203 * XXX We don't reject this for imf in UNDEFINED
2204 * state at t1, because allocation of a filter
2205 * is atomic with allocation of a membership.
2208 /* See comments above for EADDRINUSE */
2209 if (imf
->im6f_st
[1] == MCAST_EXCLUDE
) {
2212 goto out_imo_locked
;
2217 * Begin state merge transaction at socket layer.
2221 if (imo
->im6o_num_memberships
== imo
->im6o_max_memberships
) {
2222 error
= im6o_grow(imo
);
2224 goto out_imo_locked
;
2228 * Allocate the new slot upfront so we can deal with
2229 * grafting the new source filter in same code path
2230 * as for join-source on existing membership.
2232 idx
= imo
->im6o_num_memberships
;
2233 imo
->im6o_membership
[idx
] = NULL
;
2234 imo
->im6o_num_memberships
++;
2235 VERIFY(imo
->im6o_mfilters
!= NULL
);
2236 imf
= &imo
->im6o_mfilters
[idx
];
2237 VERIFY(RB_EMPTY(&imf
->im6f_sources
));
2241 * Graft new source into filter list for this inpcb's
2242 * membership of the group. The in6_multi may not have
2243 * been allocated yet if this is a new membership, however,
2244 * the in_mfilter slot will be allocated and must be initialized.
2246 * Note: Grafting of exclusive mode filters doesn't happen
2248 * XXX: Should check for non-NULL lims (node exists but may
2249 * not be in-mode) for interop with full-state API.
2251 if (ssa
->sin6_family
!= AF_UNSPEC
) {
2252 /* Membership starts in IN mode */
2254 MLD_PRINTF(("%s: new join w/source\n", __func__
);
2255 im6f_init(imf
, MCAST_UNDEFINED
, MCAST_INCLUDE
));
2257 MLD_PRINTF(("%s: %s source\n", __func__
, "allow"));
2259 lims
= im6f_graft(imf
, MCAST_INCLUDE
, ssa
);
2261 MLD_PRINTF(("%s: merge imf state failed\n",
2267 /* No address specified; Membership starts in EX mode */
2269 MLD_PRINTF(("%s: new join w/o source", __func__
));
2270 im6f_init(imf
, MCAST_UNDEFINED
, MCAST_EXCLUDE
);
2275 * Begin state merge transaction at MLD layer.
2280 * See inp_join_group() for why we need to unlock
2282 IM6O_ADDREF_LOCKED(imo
);
2284 socket_unlock(inp
->inp_socket
, 0);
2286 VERIFY(inm
== NULL
);
2287 error
= in6_mc_join(ifp
, &gsa
->sin6_addr
, imf
, &inm
, 0);
2288 VERIFY(inm
!= NULL
|| error
!= 0);
2290 socket_lock(inp
->inp_socket
, 0);
2297 imo
->im6o_membership
[idx
] = inm
; /* from in6_mc_join() */
2299 MLD_PRINTF(("%s: merge inm state\n", __func__
));
2301 error
= in6m_merge(inm
, imf
);
2303 MLD_PRINTF(("%s: failed to merge inm state\n",
2306 goto out_im6f_rollback
;
2308 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
2309 error
= mld_change_state(inm
, &mtp
, 0);
2312 MLD_PRINTF(("%s: failed mld downcall\n",
2314 goto out_im6f_rollback
;
2331 if (error
&& is_new
) {
2332 VERIFY(inm
== NULL
);
2333 imo
->im6o_membership
[idx
] = NULL
;
2334 --imo
->im6o_num_memberships
;
2339 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2341 /* schedule timer now that we've dropped the lock(s) */
2342 mld_set_timeout(&mtp
);
2348 * Leave an IPv6 multicast group on an inpcb, possibly with a source.
2351 in6p_leave_group(struct inpcb
*inp
, struct sockopt
*sopt
)
2353 struct ipv6_mreq mreq
;
2354 struct group_source_req gsr
;
2355 struct sockaddr_in6
*gsa
, *ssa
;
2357 struct in6_mfilter
*imf
;
2358 struct ip6_moptions
*imo
;
2359 struct in6_msource
*ims
;
2360 struct in6_multi
*inm
= NULL
;
2361 uint32_t ifindex
= 0;
2363 int error
, is_final
;
2364 struct mld_tparams mtp
;
2366 bzero(&mtp
, sizeof(mtp
));
2371 memset(&gsr
, 0, sizeof(struct group_source_req
));
2372 gsa
= (struct sockaddr_in6
*)&gsr
.gsr_group
;
2373 ssa
= (struct sockaddr_in6
*)&gsr
.gsr_source
;
2376 * Chew everything passed in up into a struct group_source_req
2377 * as that is easier to process.
2378 * Note: Any embedded scope ID in the multicast group passed
2379 * in by userland is ignored, the interface index is the recommended
2380 * mechanism to specify an interface; see below.
2382 switch (sopt
->sopt_name
) {
2383 case IPV6_LEAVE_GROUP
: {
2384 error
= sooptcopyin(sopt
, &mreq
, sizeof(struct ipv6_mreq
),
2385 sizeof(struct ipv6_mreq
));
2389 if (IN6_IS_ADDR_V4MAPPED(&mreq
.ipv6mr_multiaddr
)) {
2390 struct ip_mreq v4mreq
;
2391 struct sockopt v4sopt
;
2393 v4mreq
.imr_multiaddr
.s_addr
=
2394 mreq
.ipv6mr_multiaddr
.s6_addr32
[3];
2395 if (mreq
.ipv6mr_interface
== 0) {
2396 v4mreq
.imr_interface
.s_addr
= INADDR_ANY
;
2398 error
= in6p_lookup_v4addr(&mreq
, &v4mreq
);
2403 v4sopt
.sopt_dir
= SOPT_SET
;
2404 v4sopt
.sopt_level
= sopt
->sopt_level
;
2405 v4sopt
.sopt_name
= IP_DROP_MEMBERSHIP
;
2406 v4sopt
.sopt_val
= CAST_USER_ADDR_T(&v4mreq
);
2407 v4sopt
.sopt_valsize
= sizeof(v4mreq
);
2408 v4sopt
.sopt_p
= kernproc
;
2410 return inp_leave_group(inp
, &v4sopt
);
2412 gsa
->sin6_family
= AF_INET6
;
2413 gsa
->sin6_len
= sizeof(struct sockaddr_in6
);
2414 gsa
->sin6_addr
= mreq
.ipv6mr_multiaddr
;
2416 gsa
->sin6_scope_id
= 0;
2417 ifindex
= mreq
.ipv6mr_interface
;
2418 /* Only allow IPv6 multicast addresses */
2419 if (IN6_IS_ADDR_MULTICAST(&gsa
->sin6_addr
) == 0) {
2425 case MCAST_LEAVE_GROUP
:
2426 case MCAST_LEAVE_SOURCE_GROUP
:
2427 if (sopt
->sopt_name
== MCAST_LEAVE_GROUP
) {
2428 error
= sooptcopyin(sopt
, &gsr
,
2429 sizeof(struct group_req
),
2430 sizeof(struct group_req
));
2431 } else if (sopt
->sopt_name
== MCAST_LEAVE_SOURCE_GROUP
) {
2432 error
= sooptcopyin(sopt
, &gsr
,
2433 sizeof(struct group_source_req
),
2434 sizeof(struct group_source_req
));
2440 if (gsa
->sin6_family
!= AF_INET6
||
2441 gsa
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
2444 if (sopt
->sopt_name
== MCAST_LEAVE_SOURCE_GROUP
) {
2445 if (ssa
->sin6_family
!= AF_INET6
||
2446 ssa
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
2449 if (IN6_IS_ADDR_MULTICAST(&ssa
->sin6_addr
)) {
2453 * TODO: Validate embedded scope ID in source
2454 * list entry against passed-in ifp, if and only
2455 * if source list filter entry is iface or node local.
2457 in6_clearscope(&ssa
->sin6_addr
);
2460 gsa
->sin6_scope_id
= 0;
2461 ifindex
= gsr
.gsr_interface
;
2465 MLD_PRINTF(("%s: unknown sopt_name %d\n",
2466 __func__
, sopt
->sopt_name
));
2470 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6_addr
)) {
2475 * Validate interface index if provided. If no interface index
2476 * was provided separately, attempt to look the membership up
2477 * from the default scope as a last resort to disambiguate
2478 * the membership we are being asked to leave.
2479 * XXX SCOPE6 lock potentially taken here.
2482 ifnet_head_lock_shared();
2483 if ((u_int
)if_index
< ifindex
) {
2485 return EADDRNOTAVAIL
;
2487 ifp
= ifindex2ifnet
[ifindex
];
2490 return EADDRNOTAVAIL
;
2492 (void) in6_setscope(&gsa
->sin6_addr
, ifp
, NULL
);
2494 error
= sa6_embedscope(gsa
, ip6_use_defzone
);
2496 return EADDRNOTAVAIL
;
2499 * Some badly behaved applications don't pass an ifindex
2500 * or a scope ID, which is an API violation. In this case,
2501 * perform a lookup as per a v6 join.
2503 * XXX For now, stomp on zone ID for the corner case.
2504 * This is not the 'KAME way', but we need to see the ifp
2505 * directly until such time as this implementation is
2506 * refactored, assuming the scope IDs are the way to go.
2508 ifindex
= ntohs(gsa
->sin6_addr
.s6_addr16
[1]);
2510 MLD_PRINTF(("%s: warning: no ifindex, looking up "
2511 "ifp for group %s.\n", __func__
,
2512 ip6_sprintf(&gsa
->sin6_addr
)));
2513 ifp
= in6p_lookup_mcast_ifp(inp
, gsa
);
2515 if (!IF_INDEX_IN_RANGE(ifindex
)) {
2516 return EADDRNOTAVAIL
;
2518 ifnet_head_lock_shared();
2519 ifp
= ifindex2ifnet
[ifindex
];
2523 return EADDRNOTAVAIL
;
2527 VERIFY(ifp
!= NULL
);
2528 MLD_PRINTF(("%s: ifp = 0x%llx\n", __func__
,
2529 (uint64_t)VM_KERNEL_ADDRPERM(ifp
)));
2532 * Find the membership in the membership array.
2534 imo
= in6p_findmoptions(inp
);
2540 idx
= im6o_match_group(imo
, ifp
, gsa
);
2541 if (idx
== (size_t)-1) {
2542 error
= EADDRNOTAVAIL
;
2545 inm
= imo
->im6o_membership
[idx
];
2546 imf
= &imo
->im6o_mfilters
[idx
];
2548 if (ssa
->sin6_family
!= AF_UNSPEC
) {
2553 * Begin state merge transaction at socket layer.
2557 * If we were instructed only to leave a given source, do so.
2558 * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
2563 if (imf
->im6f_st
[0] == MCAST_EXCLUDE
) {
2564 error
= EADDRNOTAVAIL
;
2567 ims
= im6o_match_source(imo
, idx
, ssa
);
2569 MLD_PRINTF(("%s: source %s %spresent\n", __func__
,
2570 ip6_sprintf(&ssa
->sin6_addr
),
2572 error
= EADDRNOTAVAIL
;
2575 MLD_PRINTF(("%s: %s source\n", __func__
, "block"));
2576 error
= im6f_prune(imf
, ssa
);
2578 MLD_PRINTF(("%s: merge imf state failed\n",
2585 * Begin state merge transaction at MLD layer.
2590 * Give up the multicast address record to which
2591 * the membership points. Reference held in im6o
2592 * will be released below.
2594 (void) in6_mc_leave(inm
, imf
);
2596 MLD_PRINTF(("%s: merge inm state\n", __func__
));
2598 error
= in6m_merge(inm
, imf
);
2600 MLD_PRINTF(("%s: failed to merge inm state\n",
2603 goto out_im6f_rollback
;
2606 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
2607 error
= mld_change_state(inm
, &mtp
, 0);
2609 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
2624 /* Remove the gap in the membership array. */
2625 VERIFY(inm
== imo
->im6o_membership
[idx
]);
2626 imo
->im6o_membership
[idx
] = NULL
;
2629 * See inp_join_group() for why we need to unlock
2631 IM6O_ADDREF_LOCKED(imo
);
2633 socket_unlock(inp
->inp_socket
, 0);
2637 socket_lock(inp
->inp_socket
, 0);
2641 for (++idx
; idx
< imo
->im6o_num_memberships
; ++idx
) {
2642 imo
->im6o_membership
[idx
- 1] = imo
->im6o_membership
[idx
];
2643 imo
->im6o_mfilters
[idx
- 1] = imo
->im6o_mfilters
[idx
];
2645 imo
->im6o_num_memberships
--;
2650 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2652 /* schedule timer now that we've dropped the lock(s) */
2653 mld_set_timeout(&mtp
);
2659 * Select the interface for transmitting IPv6 multicast datagrams.
2661 * Either an instance of struct in6_addr or an instance of struct ipv6_mreqn
2662 * may be passed to this socket option. An address of in6addr_any or an
2663 * interface index of 0 is used to remove a previous selection.
2664 * When no interface is selected, one is chosen for every send.
2667 in6p_set_multicast_if(struct inpcb
*inp
, struct sockopt
*sopt
)
2670 struct ip6_moptions
*imo
;
2674 if (sopt
->sopt_valsize
!= sizeof(u_int
)) {
2678 error
= sooptcopyin(sopt
, &ifindex
, sizeof(u_int
), sizeof(u_int
));
2683 ifnet_head_lock_shared();
2684 if ((u_int
)if_index
< ifindex
) {
2689 ifp
= ifindex2ifnet
[ifindex
];
2691 if (ifp
== NULL
|| (ifp
->if_flags
& IFF_MULTICAST
) == 0) {
2692 return EADDRNOTAVAIL
;
2695 imo
= in6p_findmoptions(inp
);
2701 imo
->im6o_multicast_ifp
= ifp
;
2703 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2709 * Atomically set source filters on a socket for an IPv6 multicast group.
2713 in6p_set_source_filters(struct inpcb
*inp
, struct sockopt
*sopt
)
2715 struct __msfilterreq64 msfr
= {}, msfr64
;
2716 struct __msfilterreq32 msfr32
;
2717 struct sockaddr_in6
*gsa
;
2719 struct in6_mfilter
*imf
;
2720 struct ip6_moptions
*imo
;
2721 struct in6_multi
*inm
;
2724 user_addr_t tmp_ptr
;
2725 struct mld_tparams mtp
;
2727 bzero(&mtp
, sizeof(mtp
));
2729 if (IS_64BIT_PROCESS(current_proc())) {
2730 error
= sooptcopyin(sopt
, &msfr64
,
2731 sizeof(struct __msfilterreq64
),
2732 sizeof(struct __msfilterreq64
));
2736 /* we never use msfr.msfr_srcs; */
2737 memcpy(&msfr
, &msfr64
, sizeof(msfr64
));
2739 error
= sooptcopyin(sopt
, &msfr32
,
2740 sizeof(struct __msfilterreq32
),
2741 sizeof(struct __msfilterreq32
));
2745 /* we never use msfr.msfr_srcs; */
2746 memcpy(&msfr
, &msfr32
, sizeof(msfr32
));
2749 if ((size_t) msfr
.msfr_nsrcs
>
2750 UINT32_MAX
/ sizeof(struct sockaddr_storage
)) {
2751 msfr
.msfr_nsrcs
= UINT32_MAX
/ sizeof(struct sockaddr_storage
);
2754 if (msfr
.msfr_nsrcs
> in6_mcast_maxsocksrc
) {
2758 if (msfr
.msfr_fmode
!= MCAST_EXCLUDE
&&
2759 msfr
.msfr_fmode
!= MCAST_INCLUDE
) {
2763 if (msfr
.msfr_group
.ss_family
!= AF_INET6
||
2764 msfr
.msfr_group
.ss_len
!= sizeof(struct sockaddr_in6
)) {
2768 gsa
= (struct sockaddr_in6
*)&msfr
.msfr_group
;
2769 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6_addr
)) {
2773 gsa
->sin6_port
= 0; /* ignore port */
2775 ifnet_head_lock_shared();
2776 if (msfr
.msfr_ifindex
== 0 || (u_int
)if_index
< msfr
.msfr_ifindex
) {
2778 return EADDRNOTAVAIL
;
2780 ifp
= ifindex2ifnet
[msfr
.msfr_ifindex
];
2783 return EADDRNOTAVAIL
;
2786 (void)in6_setscope(&gsa
->sin6_addr
, ifp
, NULL
);
2789 * Take the INP write lock.
2790 * Check if this socket is a member of this group.
2792 imo
= in6p_findmoptions(inp
);
2798 idx
= im6o_match_group(imo
, ifp
, gsa
);
2799 if (idx
== (size_t)-1 || imo
->im6o_mfilters
== NULL
) {
2800 error
= EADDRNOTAVAIL
;
2801 goto out_imo_locked
;
2803 inm
= imo
->im6o_membership
[idx
];
2804 imf
= &imo
->im6o_mfilters
[idx
];
2807 * Begin state merge transaction at socket layer.
2810 imf
->im6f_st
[1] = (uint8_t)msfr
.msfr_fmode
;
2813 * Apply any new source filters, if present.
2814 * Make a copy of the user-space source vector so
2815 * that we may copy them with a single copyin. This
2816 * allows us to deal with page faults up-front.
2818 if (msfr
.msfr_nsrcs
> 0) {
2819 struct in6_msource
*lims
;
2820 struct sockaddr_in6
*psin
;
2821 struct sockaddr_storage
*kss
, *pkss
;
2824 if (IS_64BIT_PROCESS(current_proc())) {
2825 tmp_ptr
= (user_addr_t
)msfr64
.msfr_srcs
;
2827 tmp_ptr
= CAST_USER_ADDR_T(msfr32
.msfr_srcs
);
2830 MLD_PRINTF(("%s: loading %lu source list entries\n",
2831 __func__
, (unsigned long)msfr
.msfr_nsrcs
));
2832 kss
= _MALLOC((size_t) msfr
.msfr_nsrcs
* sizeof(*kss
),
2836 goto out_imo_locked
;
2839 error
= copyin(tmp_ptr
, kss
,
2840 (size_t) msfr
.msfr_nsrcs
* sizeof(*kss
));
2843 goto out_imo_locked
;
2847 * Mark all source filters as UNDEFINED at t1.
2848 * Restore new group filter mode, as im6f_leave()
2849 * will set it to INCLUDE.
2852 imf
->im6f_st
[1] = (uint8_t)msfr
.msfr_fmode
;
2855 * Update socket layer filters at t1, lazy-allocating
2856 * new entries. This saves a bunch of memory at the
2857 * cost of one RB_FIND() per source entry; duplicate
2858 * entries in the msfr_nsrcs vector are ignored.
2859 * If we encounter an error, rollback transaction.
2861 * XXX This too could be replaced with a set-symmetric
2862 * difference like loop to avoid walking from root
2863 * every time, as the key space is common.
2865 for (i
= 0, pkss
= kss
; i
< msfr
.msfr_nsrcs
; i
++, pkss
++) {
2866 psin
= (struct sockaddr_in6
*)pkss
;
2867 if (psin
->sin6_family
!= AF_INET6
) {
2868 error
= EAFNOSUPPORT
;
2871 if (psin
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
2875 if (IN6_IS_ADDR_MULTICAST(&psin
->sin6_addr
)) {
2880 * TODO: Validate embedded scope ID in source
2881 * list entry against passed-in ifp, if and only
2882 * if source list filter entry is iface or node local.
2884 in6_clearscope(&psin
->sin6_addr
);
2885 error
= im6f_get_source(imf
, psin
, &lims
);
2889 lims
->im6sl_st
[1] = imf
->im6f_st
[1];
2895 goto out_im6f_rollback
;
2899 * Begin state merge transaction at MLD layer.
2902 MLD_PRINTF(("%s: merge inm state\n", __func__
));
2903 error
= in6m_merge(inm
, imf
);
2905 MLD_PRINTF(("%s: failed to merge inm state\n", __func__
));
2907 goto out_im6f_rollback
;
2910 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
2911 error
= mld_change_state(inm
, &mtp
, 0);
2915 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
2930 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2932 /* schedule timer now that we've dropped the lock(s) */
2933 mld_set_timeout(&mtp
);
2939 * Set the IP multicast options in response to user setsockopt().
2941 * Many of the socket options handled in this function duplicate the
2942 * functionality of socket options in the regular unicast API. However,
2943 * it is not possible to merge the duplicate code, because the idempotence
2944 * of the IPv6 multicast part of the BSD Sockets API must be preserved;
2945 * the effects of these options must be treated as separate and distinct.
2949 ip6_setmoptions(struct inpcb
*inp
, struct sockopt
*sopt
)
2951 struct ip6_moptions
*im6o
;
2957 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
2958 * or is a divert socket, reject it.
2960 if (SOCK_PROTO(inp
->inp_socket
) == IPPROTO_DIVERT
||
2961 (SOCK_TYPE(inp
->inp_socket
) != SOCK_RAW
&&
2962 SOCK_TYPE(inp
->inp_socket
) != SOCK_DGRAM
)) {
2966 switch (sopt
->sopt_name
) {
2967 case IPV6_MULTICAST_IF
:
2968 error
= in6p_set_multicast_if(inp
, sopt
);
2971 case IPV6_MULTICAST_HOPS
: {
2974 if (sopt
->sopt_valsize
!= sizeof(int)) {
2978 error
= sooptcopyin(sopt
, &hlim
, sizeof(hlim
), sizeof(int));
2982 if (hlim
< -1 || hlim
> IPV6_MAXHLIM
) {
2985 } else if (hlim
== -1) {
2986 hlim
= ip6_defmcasthlim
;
2988 im6o
= in6p_findmoptions(inp
);
2994 im6o
->im6o_multicast_hlim
= (u_char
)hlim
;
2996 IM6O_REMREF(im6o
); /* from in6p_findmoptions() */
3000 case IPV6_MULTICAST_LOOP
: {
3004 * Set the loopback flag for outgoing multicast packets.
3005 * Must be zero or one.
3007 if (sopt
->sopt_valsize
!= sizeof(u_int
)) {
3011 error
= sooptcopyin(sopt
, &loop
, sizeof(u_int
), sizeof(u_int
));
3019 im6o
= in6p_findmoptions(inp
);
3025 im6o
->im6o_multicast_loop
= (u_char
)loop
;
3027 IM6O_REMREF(im6o
); /* from in6p_findmoptions() */
3031 case IPV6_JOIN_GROUP
:
3032 case MCAST_JOIN_GROUP
:
3033 case MCAST_JOIN_SOURCE_GROUP
:
3034 error
= in6p_join_group(inp
, sopt
);
3037 case IPV6_LEAVE_GROUP
:
3038 case MCAST_LEAVE_GROUP
:
3039 case MCAST_LEAVE_SOURCE_GROUP
:
3040 error
= in6p_leave_group(inp
, sopt
);
3043 case MCAST_BLOCK_SOURCE
:
3044 case MCAST_UNBLOCK_SOURCE
:
3045 error
= in6p_block_unblock_source(inp
, sopt
);
3049 error
= in6p_set_source_filters(inp
, sopt
);
3060 * Expose MLD's multicast filter mode and source list(s) to userland,
3061 * keyed by (ifindex, group).
3062 * The filter mode is written out as a uint32_t, followed by
3063 * 0..n of struct in6_addr.
3064 * For use by ifmcstat(8).
3067 sysctl_ip6_mcast_filters SYSCTL_HANDLER_ARGS
3069 #pragma unused(oidp)
3071 struct in6_addr mcaddr
;
3072 struct in6_addr src
;
3074 struct in6_multi
*inm
;
3075 struct in6_multistep step
;
3076 struct ip6_msource
*ims
;
3080 uint32_t fmode
, ifindex
;
3085 if (req
->newptr
!= USER_ADDR_NULL
) {
3089 /* int: ifindex + 4 * 32 bits of IPv6 address */
3095 ifnet_head_lock_shared();
3096 if (ifindex
<= 0 || ifindex
> (u_int
)if_index
) {
3097 MLD_PRINTF(("%s: ifindex %u out of range\n",
3098 __func__
, ifindex
));
3103 memcpy(&mcaddr
, &name
[1], sizeof(struct in6_addr
));
3104 if (!IN6_IS_ADDR_MULTICAST(&mcaddr
)) {
3105 MLD_PRINTF(("%s: group %s is not multicast\n",
3106 __func__
, ip6_sprintf(&mcaddr
)));
3111 ifp
= ifindex2ifnet
[ifindex
];
3114 MLD_PRINTF(("%s: no ifp for ifindex %u\n", __func__
, ifindex
));
3118 * Internal MLD lookups require that scope/zone ID is set.
3120 (void)in6_setscope(&mcaddr
, ifp
, NULL
);
3122 in6_multihead_lock_shared();
3123 IN6_FIRST_MULTI(step
, inm
);
3124 while (inm
!= NULL
) {
3126 if (inm
->in6m_ifp
!= ifp
) {
3130 if (!IN6_ARE_ADDR_EQUAL(&inm
->in6m_addr
, &mcaddr
)) {
3134 fmode
= inm
->in6m_st
[1].iss_fmode
;
3135 retval
= SYSCTL_OUT(req
, &fmode
, sizeof(uint32_t));
3140 RB_FOREACH(ims
, ip6_msource_tree
, &inm
->in6m_srcs
) {
3141 MLD_PRINTF(("%s: visit node 0x%llx\n", __func__
,
3142 (uint64_t)VM_KERNEL_ADDRPERM(ims
)));
3144 * Only copy-out sources which are in-mode.
3146 if (fmode
!= im6s_get_mode(inm
, ims
, 1)) {
3147 MLD_PRINTF(("%s: skip non-in-mode\n",
3149 continue; /* process next source */
3151 src
= ims
->im6s_addr
;
3152 retval
= SYSCTL_OUT(req
, &src
, sizeof(struct in6_addr
));
3154 break; /* process next inm */
3159 IN6_NEXT_MULTI(step
, inm
);
3161 in6_multihead_lock_done();
3167 in6_multi_init(void)
3169 PE_parse_boot_argn("ifa_debug", &in6m_debug
, sizeof(in6m_debug
));
3171 /* Setup lock group and attribute for in6_multihead */
3172 in6_multihead_lock_grp_attr
= lck_grp_attr_alloc_init();
3173 in6_multihead_lock_grp
= lck_grp_alloc_init("in6_multihead",
3174 in6_multihead_lock_grp_attr
);
3175 in6_multihead_lock_attr
= lck_attr_alloc_init();
3176 lck_rw_init(&in6_multihead_lock
, in6_multihead_lock_grp
,
3177 in6_multihead_lock_attr
);
3179 lck_mtx_init(&in6m_trash_lock
, in6_multihead_lock_grp
,
3180 in6_multihead_lock_attr
);
3181 TAILQ_INIT(&in6m_trash_head
);
3183 vm_size_t in6m_size
= (in6m_debug
== 0) ? sizeof(struct in6_multi
) :
3184 sizeof(struct in6_multi_dbg
);
3185 in6m_zone
= zone_create(IN6M_ZONE_NAME
, in6m_size
, ZC_ZFREE_CLEARMEM
);
3188 static struct in6_multi
*
3189 in6_multi_alloc(zalloc_flags_t how
)
3191 struct in6_multi
*in6m
;
3193 in6m
= zalloc_flags(in6m_zone
, how
| Z_ZERO
);
3195 lck_mtx_init(&in6m
->in6m_lock
, in6_multihead_lock_grp
,
3196 in6_multihead_lock_attr
);
3197 in6m
->in6m_debug
|= IFD_ALLOC
;
3198 if (in6m_debug
!= 0) {
3199 in6m
->in6m_debug
|= IFD_DEBUG
;
3200 in6m
->in6m_trace
= in6m_trace
;
3207 in6_multi_free(struct in6_multi
*in6m
)
3210 if (in6m
->in6m_debug
& IFD_ATTACHED
) {
3211 panic("%s: attached in6m=%p is being freed", __func__
, in6m
);
3213 } else if (in6m
->in6m_ifma
!= NULL
) {
3214 panic("%s: ifma not NULL for in6m=%p", __func__
, in6m
);
3216 } else if (!(in6m
->in6m_debug
& IFD_ALLOC
)) {
3217 panic("%s: in6m %p cannot be freed", __func__
, in6m
);
3219 } else if (in6m
->in6m_refcount
!= 0) {
3220 panic("%s: non-zero refcount in6m=%p", __func__
, in6m
);
3222 } else if (in6m
->in6m_reqcnt
!= 0) {
3223 panic("%s: non-zero reqcnt in6m=%p", __func__
, in6m
);
3227 /* Free any pending MLDv2 state-change records */
3228 IF_DRAIN(&in6m
->in6m_scq
);
3230 in6m
->in6m_debug
&= ~IFD_ALLOC
;
3231 if ((in6m
->in6m_debug
& (IFD_DEBUG
| IFD_TRASHED
)) ==
3232 (IFD_DEBUG
| IFD_TRASHED
)) {
3233 lck_mtx_lock(&in6m_trash_lock
);
3234 TAILQ_REMOVE(&in6m_trash_head
, (struct in6_multi_dbg
*)in6m
,
3236 lck_mtx_unlock(&in6m_trash_lock
);
3237 in6m
->in6m_debug
&= ~IFD_TRASHED
;
3241 lck_mtx_destroy(&in6m
->in6m_lock
, in6_multihead_lock_grp
);
3242 zfree(in6m_zone
, in6m
);
3246 in6_multi_attach(struct in6_multi
*in6m
)
3248 in6_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE
);
3249 IN6M_LOCK_ASSERT_HELD(in6m
);
3251 if (in6m
->in6m_debug
& IFD_ATTACHED
) {
3252 panic("%s: Attempt to attach an already attached in6m=%p",
3257 in6m
->in6m_reqcnt
++;
3258 VERIFY(in6m
->in6m_reqcnt
== 1);
3259 IN6M_ADDREF_LOCKED(in6m
);
3260 in6m
->in6m_debug
|= IFD_ATTACHED
;
3262 * Reattach case: If debugging is enabled, take it
3263 * out of the trash list and clear IFD_TRASHED.
3265 if ((in6m
->in6m_debug
& (IFD_DEBUG
| IFD_TRASHED
)) ==
3266 (IFD_DEBUG
| IFD_TRASHED
)) {
3267 /* Become a regular mutex, just in case */
3268 IN6M_CONVERT_LOCK(in6m
);
3269 lck_mtx_lock(&in6m_trash_lock
);
3270 TAILQ_REMOVE(&in6m_trash_head
, (struct in6_multi_dbg
*)in6m
,
3272 lck_mtx_unlock(&in6m_trash_lock
);
3273 in6m
->in6m_debug
&= ~IFD_TRASHED
;
3276 LIST_INSERT_HEAD(&in6_multihead
, in6m
, in6m_entry
);
3280 in6_multi_detach(struct in6_multi
*in6m
)
3282 in6_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE
);
3283 IN6M_LOCK_ASSERT_HELD(in6m
);
3285 if (in6m
->in6m_reqcnt
== 0) {
3286 panic("%s: in6m=%p negative reqcnt", __func__
, in6m
);
3290 --in6m
->in6m_reqcnt
;
3291 if (in6m
->in6m_reqcnt
> 0) {
3295 if (!(in6m
->in6m_debug
& IFD_ATTACHED
)) {
3296 panic("%s: Attempt to detach an unattached record in6m=%p",
3299 } else if (in6m
->in6m_debug
& IFD_TRASHED
) {
3300 panic("%s: in6m %p is already in trash list", __func__
, in6m
);
3305 * NOTE: Caller calls IFMA_REMREF
3307 in6m
->in6m_debug
&= ~IFD_ATTACHED
;
3308 LIST_REMOVE(in6m
, in6m_entry
);
3310 if (in6m
->in6m_debug
& IFD_DEBUG
) {
3311 /* Become a regular mutex, just in case */
3312 IN6M_CONVERT_LOCK(in6m
);
3313 lck_mtx_lock(&in6m_trash_lock
);
3314 TAILQ_INSERT_TAIL(&in6m_trash_head
,
3315 (struct in6_multi_dbg
*)in6m
, in6m_trash_link
);
3316 lck_mtx_unlock(&in6m_trash_lock
);
3317 in6m
->in6m_debug
|= IFD_TRASHED
;
3324 in6m_addref(struct in6_multi
*in6m
, int locked
)
3327 IN6M_LOCK_SPIN(in6m
);
3329 IN6M_LOCK_ASSERT_HELD(in6m
);
3332 if (++in6m
->in6m_refcount
== 0) {
3333 panic("%s: in6m=%p wraparound refcnt", __func__
, in6m
);
3335 } else if (in6m
->in6m_trace
!= NULL
) {
3336 (*in6m
->in6m_trace
)(in6m
, TRUE
);
3344 in6m_remref(struct in6_multi
*in6m
, int locked
)
3346 struct ifmultiaddr
*ifma
;
3347 struct mld_ifinfo
*mli
;
3350 IN6M_LOCK_SPIN(in6m
);
3352 IN6M_LOCK_ASSERT_HELD(in6m
);
3355 if (in6m
->in6m_refcount
== 0 || (in6m
->in6m_refcount
== 1 && locked
)) {
3356 panic("%s: in6m=%p negative refcnt", __func__
, in6m
);
3358 } else if (in6m
->in6m_trace
!= NULL
) {
3359 (*in6m
->in6m_trace
)(in6m
, FALSE
);
3362 --in6m
->in6m_refcount
;
3363 if (in6m
->in6m_refcount
> 0) {
3371 * Synchronization with in6_mc_get(). In the event the in6m has been
3372 * detached, the underlying ifma would still be in the if_multiaddrs
3373 * list, and thus can be looked up via if_addmulti(). At that point,
3374 * the only way to find this in6m is via ifma_protospec. To avoid
3375 * race conditions between the last in6m_remref() of that in6m and its
3376 * use via ifma_protospec, in6_multihead lock is used for serialization.
3377 * In order to avoid violating the lock order, we must drop in6m_lock
3378 * before acquiring in6_multihead lock. To prevent the in6m from being
3379 * freed prematurely, we hold an extra reference.
3381 ++in6m
->in6m_refcount
;
3383 in6_multihead_lock_shared();
3384 IN6M_LOCK_SPIN(in6m
);
3385 --in6m
->in6m_refcount
;
3386 if (in6m
->in6m_refcount
> 0) {
3387 /* We've lost the race, so abort since in6m is still in use */
3389 in6_multihead_lock_done();
3390 /* If it was locked, return it as such */
3397 ifma
= in6m
->in6m_ifma
;
3398 in6m
->in6m_ifma
= NULL
;
3399 in6m
->in6m_ifp
= NULL
;
3400 mli
= in6m
->in6m_mli
;
3401 in6m
->in6m_mli
= NULL
;
3403 IFMA_LOCK_SPIN(ifma
);
3404 ifma
->ifma_protospec
= NULL
;
3406 in6_multihead_lock_done();
3408 in6_multi_free(in6m
);
3409 if_delmulti_ifma(ifma
);
3410 /* Release reference held to the underlying ifmultiaddr */
3419 in6m_trace(struct in6_multi
*in6m
, int refhold
)
3421 struct in6_multi_dbg
*in6m_dbg
= (struct in6_multi_dbg
*)in6m
;
3426 if (!(in6m
->in6m_debug
& IFD_DEBUG
)) {
3427 panic("%s: in6m %p has no debug structure", __func__
, in6m
);
3431 cnt
= &in6m_dbg
->in6m_refhold_cnt
;
3432 tr
= in6m_dbg
->in6m_refhold
;
3434 cnt
= &in6m_dbg
->in6m_refrele_cnt
;
3435 tr
= in6m_dbg
->in6m_refrele
;
3438 idx
= atomic_add_16_ov(cnt
, 1) % IN6M_TRACE_HIST_SIZE
;
3439 ctrace_record(&tr
[idx
]);
3442 static struct in6_multi_mship
*
3443 in6_multi_mship_alloc(zalloc_flags_t how
)
3445 return zalloc_flags(imm_zone
, how
| Z_ZERO
);
3449 in6_multi_mship_free(struct in6_multi_mship
*imm
)
3451 if (imm
->i6mm_maddr
!= NULL
) {
3452 panic("%s: i6mm_maddr not NULL for imm=%p", __func__
, imm
);
3455 zfree(imm_zone
, imm
);
3459 in6_multihead_lock_exclusive(void)
3461 lck_rw_lock_exclusive(&in6_multihead_lock
);
3465 in6_multihead_lock_shared(void)
3467 lck_rw_lock_shared(&in6_multihead_lock
);
3471 in6_multihead_lock_assert(int what
)
3474 #pragma unused(what)
3476 LCK_RW_ASSERT(&in6_multihead_lock
, what
);
3480 in6_multihead_lock_done(void)
3482 lck_rw_done(&in6_multihead_lock
);
3485 static struct ip6_msource
*
3486 ip6ms_alloc(zalloc_flags_t how
)
3488 return zalloc_flags(ip6ms_zone
, how
| Z_ZERO
);
3492 ip6ms_free(struct ip6_msource
*i6ms
)
3494 zfree(ip6ms_zone
, i6ms
);
3497 static struct in6_msource
*
3498 in6ms_alloc(zalloc_flags_t how
)
3500 return zalloc_flags(in6ms_zone
, how
| Z_ZERO
);
3504 in6ms_free(struct in6_msource
*in6ms
)
3506 zfree(in6ms_zone
, in6ms
);
3511 static const char *in6m_modestrs
[] = { "un\n", "in", "ex" };
3514 in6m_mode_str(const int mode
)
3516 if (mode
>= MCAST_UNDEFINED
&& mode
<= MCAST_EXCLUDE
) {
3517 return in6m_modestrs
[mode
];
3522 static const char *in6m_statestrs
[] = {
3531 "sg-query-pending\n",
3536 in6m_state_str(const int state
)
3538 if (state
>= MLD_NOT_MEMBER
&& state
<= MLD_LEAVING_MEMBER
) {
3539 return in6m_statestrs
[state
];
3545 * Dump an in6_multi structure to the console.
3548 in6m_print(const struct in6_multi
*inm
)
3552 IN6M_LOCK_ASSERT_HELD(__DECONST(struct in6_multi
*, inm
));
3554 if (mld_debug
== 0) {
3558 printf("%s: --- begin in6m 0x%llx ---\n", __func__
,
3559 (uint64_t)VM_KERNEL_ADDRPERM(inm
));
3560 printf("addr %s ifp 0x%llx(%s) ifma 0x%llx\n",
3561 ip6_sprintf(&inm
->in6m_addr
),
3562 (uint64_t)VM_KERNEL_ADDRPERM(inm
->in6m_ifp
),
3563 if_name(inm
->in6m_ifp
),
3564 (uint64_t)VM_KERNEL_ADDRPERM(inm
->in6m_ifma
));
3565 printf("timer %u state %s refcount %u scq.len %u\n",
3567 in6m_state_str(inm
->in6m_state
),
3569 inm
->in6m_scq
.ifq_len
);
3570 printf("mli 0x%llx nsrc %lu sctimer %u scrv %u\n",
3571 (uint64_t)VM_KERNEL_ADDRPERM(inm
->in6m_mli
),
3575 for (t
= 0; t
< 2; t
++) {
3576 printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t
,
3577 in6m_mode_str(inm
->in6m_st
[t
].iss_fmode
),
3578 inm
->in6m_st
[t
].iss_asm
,
3579 inm
->in6m_st
[t
].iss_ex
,
3580 inm
->in6m_st
[t
].iss_in
,
3581 inm
->in6m_st
[t
].iss_rec
);
3583 printf("%s: --- end in6m 0x%llx ---\n", __func__
,
3584 (uint64_t)VM_KERNEL_ADDRPERM(inm
));
3590 in6m_print(__unused
const struct in6_multi
*inm
)