2 * Copyright (c) 2010-2012 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/route.h>
85 #include <netinet/in.h>
86 #include <netinet/in_var.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet/ip6.h>
89 #include <netinet/icmp6.h>
90 #include <netinet6/ip6_var.h>
91 #include <netinet/in_pcb.h>
92 #include <netinet/tcp.h>
93 #include <netinet/tcp_seq.h>
94 #include <netinet/tcp_var.h>
95 #include <netinet6/nd6.h>
96 #include <netinet6/mld6_var.h>
97 #include <netinet6/scope6_var.h>
99 #ifndef __SOCKUNION_DECLARED
101 struct sockaddr_storage ss
;
103 struct sockaddr_dl sdl
;
104 struct sockaddr_in6 sin6
;
106 typedef union sockunion sockunion_t
;
107 #define __SOCKUNION_DECLARED
108 #endif /* __SOCKUNION_DECLARED */
110 static void im6f_commit(struct in6_mfilter
*);
111 static int im6f_get_source(struct in6_mfilter
*imf
,
112 const struct sockaddr_in6
*psin
,
113 struct in6_msource
**);
114 static struct in6_msource
*
115 im6f_graft(struct in6_mfilter
*, const uint8_t,
116 const struct sockaddr_in6
*);
117 static int im6f_prune(struct in6_mfilter
*, const struct sockaddr_in6
*);
118 static void im6f_rollback(struct in6_mfilter
*);
119 static void im6f_reap(struct in6_mfilter
*);
120 static int im6o_grow(struct ip6_moptions
*, size_t);
121 static size_t im6o_match_group(const struct ip6_moptions
*,
122 const struct ifnet
*, const struct sockaddr
*);
123 static struct in6_msource
*
124 im6o_match_source(const struct ip6_moptions
*, const size_t,
125 const struct sockaddr
*);
126 static void im6s_merge(struct ip6_msource
*ims
,
127 const struct in6_msource
*lims
, const int rollback
);
128 static int in6_mc_get(struct ifnet
*, const struct in6_addr
*,
129 struct in6_multi
**);
130 static int in6m_get_source(struct in6_multi
*inm
,
131 const struct in6_addr
*addr
, const int noalloc
,
132 struct ip6_msource
**pims
);
133 static int in6m_is_ifp_detached(const struct in6_multi
*);
134 static int in6m_merge(struct in6_multi
*, /*const*/ struct in6_mfilter
*);
135 static void in6m_reap(struct in6_multi
*);
136 static struct ip6_moptions
*
137 in6p_findmoptions(struct inpcb
*);
138 static int in6p_get_source_filters(struct inpcb
*, struct sockopt
*);
139 static int in6p_lookup_v4addr(struct ipv6_mreq
*, struct ip_mreq
*);
140 static int in6p_join_group(struct inpcb
*, struct sockopt
*);
141 static int in6p_leave_group(struct inpcb
*, struct sockopt
*);
142 static struct ifnet
*
143 in6p_lookup_mcast_ifp(const struct inpcb
*,
144 const struct sockaddr_in6
*);
145 static int in6p_block_unblock_source(struct inpcb
*, struct sockopt
*);
146 static int in6p_set_multicast_if(struct inpcb
*, struct sockopt
*);
147 static int in6p_set_source_filters(struct inpcb
*, struct sockopt
*);
148 static int sysctl_ip6_mcast_filters SYSCTL_HANDLER_ARGS
;
149 static __inline__
int ip6_msource_cmp(const struct ip6_msource
*,
150 const struct ip6_msource
*);
152 SYSCTL_DECL(_net_inet6_ip6
); /* XXX Not in any common header. */
154 SYSCTL_NODE(_net_inet6_ip6
, OID_AUTO
, mcast
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, "IPv6 multicast");
156 static unsigned long in6_mcast_maxgrpsrc
= IPV6_MAX_GROUP_SRC_FILTER
;
157 SYSCTL_LONG(_net_inet6_ip6_mcast
, OID_AUTO
, maxgrpsrc
,
158 CTLFLAG_RW
| CTLFLAG_LOCKED
, &in6_mcast_maxgrpsrc
,
159 "Max source filters per group");
161 static unsigned long in6_mcast_maxsocksrc
= IPV6_MAX_SOCK_SRC_FILTER
;
162 SYSCTL_LONG(_net_inet6_ip6_mcast
, OID_AUTO
, maxsocksrc
,
163 CTLFLAG_RW
| CTLFLAG_LOCKED
, &in6_mcast_maxsocksrc
,
164 "Max source filters per socket");
166 int in6_mcast_loop
= IPV6_DEFAULT_MULTICAST_LOOP
;
167 SYSCTL_INT(_net_inet6_ip6_mcast
, OID_AUTO
, loop
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
168 &in6_mcast_loop
, 0, "Loopback multicast datagrams by default");
170 SYSCTL_NODE(_net_inet6_ip6_mcast
, OID_AUTO
, filters
,
171 CTLFLAG_RD
| CTLFLAG_LOCKED
, sysctl_ip6_mcast_filters
,
172 "Per-interface stack-wide source filters");
174 RB_GENERATE_PREV(ip6_msource_tree
, ip6_msource
, im6s_link
, ip6_msource_cmp
);
176 #define IN6M_TRACE_HIST_SIZE 32 /* size of trace history */
179 __private_extern__
unsigned int in6m_trace_hist_size
= IN6M_TRACE_HIST_SIZE
;
181 struct in6_multi_dbg
{
182 struct in6_multi in6m
; /* in6_multi */
183 u_int16_t in6m_refhold_cnt
; /* # of ref */
184 u_int16_t in6m_refrele_cnt
; /* # of rele */
186 * Circular lists of in6m_addref and in6m_remref callers.
188 ctrace_t in6m_refhold
[IN6M_TRACE_HIST_SIZE
];
189 ctrace_t in6m_refrele
[IN6M_TRACE_HIST_SIZE
];
193 TAILQ_ENTRY(in6_multi_dbg
) in6m_trash_link
;
196 /* List of trash in6_multi entries protected by in6m_trash_lock */
197 static TAILQ_HEAD(, in6_multi_dbg
) in6m_trash_head
;
198 static decl_lck_mtx_data(, in6m_trash_lock
);
201 static unsigned int in6m_debug
= 1; /* debugging (enabled) */
203 static unsigned int in6m_debug
; /* debugging (disabled) */
205 static unsigned int in6m_size
; /* size of zone element */
206 static struct zone
*in6m_zone
; /* zone for in6_multi */
208 #define IN6M_ZONE_MAX 64 /* maximum elements in zone */
209 #define IN6M_ZONE_NAME "in6_multi" /* zone name */
211 static unsigned int imm_size
; /* size of zone element */
212 static struct zone
*imm_zone
; /* zone for in6_multi_mship */
214 #define IMM_ZONE_MAX 64 /* maximum elements in zone */
215 #define IMM_ZONE_NAME "in6_multi_mship" /* zone name */
217 #define IP6MS_ZONE_MAX 64 /* maximum elements in zone */
218 #define IP6MS_ZONE_NAME "ip6_msource" /* zone name */
220 static unsigned int ip6ms_size
; /* size of zone element */
221 static struct zone
*ip6ms_zone
; /* zone for ip6_msource */
223 #define IN6MS_ZONE_MAX 64 /* maximum elements in zone */
224 #define IN6MS_ZONE_NAME "in6_msource" /* zone name */
226 static unsigned int in6ms_size
; /* size of zone element */
227 static struct zone
*in6ms_zone
; /* zone for in6_msource */
229 /* Lock group and attribute for in6_multihead_lock lock */
230 static lck_attr_t
*in6_multihead_lock_attr
;
231 static lck_grp_t
*in6_multihead_lock_grp
;
232 static lck_grp_attr_t
*in6_multihead_lock_grp_attr
;
234 static decl_lck_rw_data(, in6_multihead_lock
);
235 struct in6_multihead in6_multihead
;
237 static struct in6_multi
*in6_multi_alloc(int);
238 static void in6_multi_free(struct in6_multi
*);
239 static void in6_multi_attach(struct in6_multi
*);
240 static struct in6_multi_mship
*in6_multi_mship_alloc(int);
241 static void in6_multi_mship_free(struct in6_multi_mship
*);
242 static void in6m_trace(struct in6_multi
*, int);
244 static struct ip6_msource
*ip6ms_alloc(int);
245 static void ip6ms_free(struct ip6_msource
*);
246 static struct in6_msource
*in6ms_alloc(int);
247 static void in6ms_free(struct in6_msource
*);
249 #define IM6O_CAST_TO_NONCONST(x) ((struct ip6_moptions *)(void *)(uintptr_t)x)
250 #define IN6M_CAST_TO_NONCONST(x) ((struct in6_multi *)(void *)(uintptr_t)x)
253 * IPv6 source tree comparison function.
255 * An ordered predicate is necessary; bcmp() is not documented to return
256 * an indication of order, memcmp() is, and is an ISO C99 requirement.
259 ip6_msource_cmp(const struct ip6_msource
*a
, const struct ip6_msource
*b
)
261 return (memcmp(&a
->im6s_addr
, &b
->im6s_addr
, sizeof(struct in6_addr
)));
265 * Inline function which wraps assertions for a valid ifp.
267 static __inline__
int
268 in6m_is_ifp_detached(const struct in6_multi
*inm
)
270 VERIFY(inm
->in6m_ifma
!= NULL
);
271 VERIFY(inm
->in6m_ifp
== inm
->in6m_ifma
->ifma_ifp
);
273 return (!ifnet_is_attached(inm
->in6m_ifp
, 0));
277 * Initialize an in6_mfilter structure to a known state at t0, t1
278 * with an empty source filter list.
280 static __inline__
void
281 im6f_init(struct in6_mfilter
*imf
, const int st0
, const int st1
)
283 memset(imf
, 0, sizeof(struct in6_mfilter
));
284 RB_INIT(&imf
->im6f_sources
);
285 imf
->im6f_st
[0] = st0
;
286 imf
->im6f_st
[1] = st1
;
290 * Resize the ip6_moptions vector to the next power-of-two minus 1.
293 im6o_grow(struct ip6_moptions
*imo
, size_t newmax
)
295 struct in6_multi
**nmships
;
296 struct in6_multi
**omships
;
297 struct in6_mfilter
*nmfilters
;
298 struct in6_mfilter
*omfilters
;
302 IM6O_LOCK_ASSERT_HELD(imo
);
306 omships
= imo
->im6o_membership
;
307 omfilters
= imo
->im6o_mfilters
;
308 oldmax
= imo
->im6o_max_memberships
;
310 newmax
= ((oldmax
+ 1) * 2) - 1;
312 if (newmax
> IPV6_MAX_MEMBERSHIPS
)
313 return (ETOOMANYREFS
);
315 if ((nmships
= (struct in6_multi
**)_REALLOC(omships
,
316 sizeof (struct in6_multi
*) * newmax
, M_IP6MOPTS
,
317 M_WAITOK
| M_ZERO
)) == NULL
)
320 imo
->im6o_membership
= nmships
;
322 if ((nmfilters
= (struct in6_mfilter
*)_REALLOC(omfilters
,
323 sizeof (struct in6_mfilter
) * newmax
, M_IN6MFILTER
,
324 M_WAITOK
| M_ZERO
)) == NULL
)
327 imo
->im6o_mfilters
= nmfilters
;
329 /* Initialize newly allocated source filter heads. */
330 for (idx
= oldmax
; idx
< newmax
; idx
++)
331 im6f_init(&nmfilters
[idx
], MCAST_UNDEFINED
, MCAST_EXCLUDE
);
333 imo
->im6o_max_memberships
= newmax
;
339 * Find an IPv6 multicast group entry for this ip6_moptions instance
340 * which matches the specified group, and optionally an interface.
341 * Return its index into the array, or -1 if not found.
344 im6o_match_group(const struct ip6_moptions
*imo
, const struct ifnet
*ifp
,
345 const struct sockaddr
*group
)
347 const struct sockaddr_in6
*gsin6
;
348 struct in6_multi
*pinm
;
352 IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo
));
354 gsin6
= (struct sockaddr_in6
*)(uintptr_t)(size_t)group
;
356 /* The im6o_membership array may be lazy allocated. */
357 if (imo
->im6o_membership
== NULL
|| imo
->im6o_num_memberships
== 0)
360 nmships
= imo
->im6o_num_memberships
;
361 for (idx
= 0; idx
< nmships
; idx
++) {
362 pinm
= imo
->im6o_membership
[idx
];
366 if ((ifp
== NULL
|| (pinm
->in6m_ifp
== ifp
)) &&
367 IN6_ARE_ADDR_EQUAL(&pinm
->in6m_addr
,
368 &gsin6
->sin6_addr
)) {
381 * Find an IPv6 multicast source entry for this imo which matches
382 * the given group index for this socket, and source address.
384 * XXX TODO: The scope ID, if present in src, is stripped before
385 * any comparison. We SHOULD enforce scope/zone checks where the source
386 * filter entry has a link scope.
388 * NOTE: This does not check if the entry is in-mode, merely if
389 * it exists, which may not be the desired behaviour.
391 static struct in6_msource
*
392 im6o_match_source(const struct ip6_moptions
*imo
, const size_t gidx
,
393 const struct sockaddr
*src
)
395 struct ip6_msource find
;
396 struct in6_mfilter
*imf
;
397 struct ip6_msource
*ims
;
398 const sockunion_t
*psa
;
400 IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo
));
402 VERIFY(src
->sa_family
== AF_INET6
);
403 VERIFY(gidx
!= (size_t)-1 && gidx
< imo
->im6o_num_memberships
);
405 /* The im6o_mfilters array may be lazy allocated. */
406 if (imo
->im6o_mfilters
== NULL
)
408 imf
= &imo
->im6o_mfilters
[gidx
];
410 psa
= (sockunion_t
*)(uintptr_t)(size_t)src
;
411 find
.im6s_addr
= psa
->sin6
.sin6_addr
;
412 in6_clearscope(&find
.im6s_addr
); /* XXX */
413 ims
= RB_FIND(ip6_msource_tree
, &imf
->im6f_sources
, &find
);
415 return ((struct in6_msource
*)ims
);
419 * Perform filtering for multicast datagrams on a socket by group and source.
421 * Returns 0 if a datagram should be allowed through, or various error codes
422 * if the socket was not a member of the group, or the source was muted, etc.
425 im6o_mc_filter(const struct ip6_moptions
*imo
, const struct ifnet
*ifp
,
426 const struct sockaddr
*group
, const struct sockaddr
*src
)
429 struct in6_msource
*ims
;
432 IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo
));
435 gidx
= im6o_match_group(imo
, ifp
, group
);
436 if (gidx
== (size_t)-1)
437 return (MCAST_NOTGMEMBER
);
440 * Check if the source was included in an (S,G) join.
441 * Allow reception on exclusive memberships by default,
442 * reject reception on inclusive memberships by default.
443 * Exclude source only if an in-mode exclude filter exists.
444 * Include source only if an in-mode include filter exists.
445 * NOTE: We are comparing group state here at MLD t1 (now)
446 * with socket-layer t0 (since last downcall).
448 mode
= imo
->im6o_mfilters
[gidx
].im6f_st
[1];
449 ims
= im6o_match_source(imo
, gidx
, src
);
451 if ((ims
== NULL
&& mode
== MCAST_INCLUDE
) ||
452 (ims
!= NULL
&& ims
->im6sl_st
[0] != mode
))
453 return (MCAST_NOTSMEMBER
);
459 * Find and return a reference to an in6_multi record for (ifp, group),
460 * and bump its reference count.
461 * If one does not exist, try to allocate it, and update link-layer multicast
462 * filters on ifp to listen for group.
463 * Assumes the IN6_MULTI lock is held across the call.
464 * Return 0 if successful, otherwise return an appropriate error code.
467 in6_mc_get(struct ifnet
*ifp
, const struct in6_addr
*group
,
468 struct in6_multi
**pinm
)
470 struct sockaddr_in6 gsin6
;
471 struct ifmultiaddr
*ifma
;
472 struct in6_multi
*inm
;
477 in6_multihead_lock_shared();
478 IN6_LOOKUP_MULTI(group
, ifp
, inm
);
481 VERIFY(inm
->in6m_reqcnt
>= 1);
483 VERIFY(inm
->in6m_reqcnt
!= 0);
486 in6_multihead_lock_done();
488 * We already joined this group; return the in6m
489 * with a refcount held (via lookup) for caller.
493 in6_multihead_lock_done();
495 memset(&gsin6
, 0, sizeof(gsin6
));
496 gsin6
.sin6_family
= AF_INET6
;
497 gsin6
.sin6_len
= sizeof(struct sockaddr_in6
);
498 gsin6
.sin6_addr
= *group
;
501 * Check if a link-layer group is already associated
502 * with this network-layer group on the given ifnet.
504 error
= if_addmulti(ifp
, (struct sockaddr
*)&gsin6
, &ifma
);
509 * See comments in in6m_remref() for access to ifma_protospec.
511 in6_multihead_lock_exclusive();
513 if ((inm
= ifma
->ifma_protospec
) != NULL
) {
514 VERIFY(ifma
->ifma_addr
!= NULL
);
515 VERIFY(ifma
->ifma_addr
->sa_family
== AF_INET6
);
516 IN6M_ADDREF(inm
); /* for caller */
519 VERIFY(inm
->in6m_ifma
== ifma
);
520 VERIFY(inm
->in6m_ifp
== ifp
);
521 VERIFY(IN6_ARE_ADDR_EQUAL(&inm
->in6m_addr
, group
));
522 if (inm
->in6m_debug
& IFD_ATTACHED
) {
523 VERIFY(inm
->in6m_reqcnt
>= 1);
525 VERIFY(inm
->in6m_reqcnt
!= 0);
528 in6_multihead_lock_done();
531 * We lost the race with another thread doing
532 * in6_mc_get(); since this group has already
533 * been joined; return the inm with a refcount
539 * We lost the race with another thread doing in6_delmulti();
540 * the inm referring to the ifma has been detached, thus we
541 * reattach it back to the in6_multihead list, and return the
542 * inm with a refcount held for the caller.
544 in6_multi_attach(inm
);
545 VERIFY((inm
->in6m_debug
&
546 (IFD_ATTACHED
| IFD_TRASHED
)) == IFD_ATTACHED
);
549 in6_multihead_lock_done();
556 * A new in6_multi record is needed; allocate and initialize it.
557 * We DO NOT perform an MLD join as the in6_ layer may need to
558 * push an initial source list down to MLD to support SSM.
560 * The initial source filter state is INCLUDE, {} as per the RFC.
561 * Pending state-changes per group are subject to a bounds check.
563 inm
= in6_multi_alloc(M_WAITOK
);
565 in6_multihead_lock_done();
570 inm
->in6m_addr
= *group
;
572 inm
->in6m_mli
= MLD_IFINFO(ifp
);
573 VERIFY(inm
->in6m_mli
!= NULL
);
574 MLI_ADDREF(inm
->in6m_mli
);
575 inm
->in6m_ifma
= ifma
; /* keep refcount from if_addmulti() */
576 inm
->in6m_state
= MLD_NOT_MEMBER
;
578 * Pending state-changes per group are subject to a bounds check.
580 inm
->in6m_scq
.ifq_maxlen
= MLD_MAX_STATE_CHANGES
;
581 inm
->in6m_st
[0].iss_fmode
= MCAST_UNDEFINED
;
582 inm
->in6m_st
[1].iss_fmode
= MCAST_UNDEFINED
;
583 RB_INIT(&inm
->in6m_srcs
);
585 in6_multi_attach(inm
);
586 VERIFY((inm
->in6m_debug
&
587 (IFD_ATTACHED
| IFD_TRASHED
)) == IFD_ATTACHED
);
588 IN6M_ADDREF_LOCKED(inm
); /* for caller */
592 VERIFY(ifma
->ifma_protospec
== NULL
);
593 ifma
->ifma_protospec
= inm
;
595 in6_multihead_lock_done();
601 * Clear recorded source entries for a group.
602 * Used by the MLD code. Caller must hold the IN6_MULTI lock.
603 * FIXME: Should reap.
606 in6m_clear_recorded(struct in6_multi
*inm
)
608 struct ip6_msource
*ims
;
610 IN6M_LOCK_ASSERT_HELD(inm
);
612 RB_FOREACH(ims
, ip6_msource_tree
, &inm
->in6m_srcs
) {
615 --inm
->in6m_st
[1].iss_rec
;
618 VERIFY(inm
->in6m_st
[1].iss_rec
== 0);
622 * Record a source as pending for a Source-Group MLDv2 query.
623 * This lives here as it modifies the shared tree.
625 * inm is the group descriptor.
626 * naddr is the address of the source to record in network-byte order.
628 * If the net.inet6.mld.sgalloc sysctl is non-zero, we will
629 * lazy-allocate a source node in response to an SG query.
630 * Otherwise, no allocation is performed. This saves some memory
631 * with the trade-off that the source will not be reported to the
632 * router if joined in the window between the query response and
633 * the group actually being joined on the local host.
635 * VIMAGE: XXX: Currently the mld_sgalloc feature has been removed.
636 * This turns off the allocation of a recorded source entry if
637 * the group has not been joined.
639 * Return 0 if the source didn't exist or was already marked as recorded.
640 * Return 1 if the source was marked as recorded by this function.
641 * Return <0 if any error occured (negated errno code).
644 in6m_record_source(struct in6_multi
*inm
, const struct in6_addr
*addr
)
646 struct ip6_msource find
;
647 struct ip6_msource
*ims
, *nims
;
649 IN6M_LOCK_ASSERT_HELD(inm
);
651 find
.im6s_addr
= *addr
;
652 ims
= RB_FIND(ip6_msource_tree
, &inm
->in6m_srcs
, &find
);
653 if (ims
&& ims
->im6s_stp
)
656 if (inm
->in6m_nsrc
== in6_mcast_maxgrpsrc
)
658 nims
= ip6ms_alloc(M_WAITOK
);
661 nims
->im6s_addr
= find
.im6s_addr
;
662 RB_INSERT(ip6_msource_tree
, &inm
->in6m_srcs
, nims
);
668 * Mark the source as recorded and update the recorded
672 ++inm
->in6m_st
[1].iss_rec
;
678 * Return a pointer to an in6_msource owned by an in6_mfilter,
679 * given its source address.
680 * Lazy-allocate if needed. If this is a new entry its filter state is
683 * imf is the filter set being modified.
684 * addr is the source address.
686 * Caller is expected to be holding im6o_lock.
689 im6f_get_source(struct in6_mfilter
*imf
, const struct sockaddr_in6
*psin
,
690 struct in6_msource
**plims
)
692 struct ip6_msource find
;
693 struct ip6_msource
*ims
;
694 struct in6_msource
*lims
;
701 find
.im6s_addr
= psin
->sin6_addr
;
702 ims
= RB_FIND(ip6_msource_tree
, &imf
->im6f_sources
, &find
);
703 lims
= (struct in6_msource
*)ims
;
705 if (imf
->im6f_nsrc
== in6_mcast_maxsocksrc
)
707 lims
= in6ms_alloc(M_WAITOK
);
710 lims
->im6s_addr
= find
.im6s_addr
;
711 lims
->im6sl_st
[0] = MCAST_UNDEFINED
;
712 RB_INSERT(ip6_msource_tree
, &imf
->im6f_sources
,
713 (struct ip6_msource
*)lims
);
723 * Graft a source entry into an existing socket-layer filter set,
724 * maintaining any required invariants and checking allocations.
726 * The source is marked as being in the new filter mode at t1.
728 * Return the pointer to the new node, otherwise return NULL.
730 * Caller is expected to be holding im6o_lock.
732 static struct in6_msource
*
733 im6f_graft(struct in6_mfilter
*imf
, const uint8_t st1
,
734 const struct sockaddr_in6
*psin
)
736 struct in6_msource
*lims
;
738 lims
= in6ms_alloc(M_WAITOK
);
741 lims
->im6s_addr
= psin
->sin6_addr
;
742 lims
->im6sl_st
[0] = MCAST_UNDEFINED
;
743 lims
->im6sl_st
[1] = st1
;
744 RB_INSERT(ip6_msource_tree
, &imf
->im6f_sources
,
745 (struct ip6_msource
*)lims
);
752 * Prune a source entry from an existing socket-layer filter set,
753 * maintaining any required invariants and checking allocations.
755 * The source is marked as being left at t1, it is not freed.
757 * Return 0 if no error occurred, otherwise return an errno value.
759 * Caller is expected to be holding im6o_lock.
762 im6f_prune(struct in6_mfilter
*imf
, const struct sockaddr_in6
*psin
)
764 struct ip6_msource find
;
765 struct ip6_msource
*ims
;
766 struct in6_msource
*lims
;
768 find
.im6s_addr
= psin
->sin6_addr
;
769 ims
= RB_FIND(ip6_msource_tree
, &imf
->im6f_sources
, &find
);
772 lims
= (struct in6_msource
*)ims
;
773 lims
->im6sl_st
[1] = MCAST_UNDEFINED
;
778 * Revert socket-layer filter set deltas at t1 to t0 state.
780 * Caller is expected to be holding im6o_lock.
783 im6f_rollback(struct in6_mfilter
*imf
)
785 struct ip6_msource
*ims
, *tims
;
786 struct in6_msource
*lims
;
788 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &imf
->im6f_sources
, tims
) {
789 lims
= (struct in6_msource
*)ims
;
790 if (lims
->im6sl_st
[0] == lims
->im6sl_st
[1]) {
791 /* no change at t1 */
793 } else if (lims
->im6sl_st
[0] != MCAST_UNDEFINED
) {
794 /* revert change to existing source at t1 */
795 lims
->im6sl_st
[1] = lims
->im6sl_st
[0];
797 /* revert source added t1 */
798 MLD_PRINTF(("%s: free in6ms %p\n", __func__
, lims
));
799 RB_REMOVE(ip6_msource_tree
, &imf
->im6f_sources
, ims
);
804 imf
->im6f_st
[1] = imf
->im6f_st
[0];
808 * Mark socket-layer filter set as INCLUDE {} at t1.
810 * Caller is expected to be holding im6o_lock.
813 im6f_leave(struct in6_mfilter
*imf
)
815 struct ip6_msource
*ims
;
816 struct in6_msource
*lims
;
818 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
819 lims
= (struct in6_msource
*)ims
;
820 lims
->im6sl_st
[1] = MCAST_UNDEFINED
;
822 imf
->im6f_st
[1] = MCAST_INCLUDE
;
826 * Mark socket-layer filter set deltas as committed.
828 * Caller is expected to be holding im6o_lock.
831 im6f_commit(struct in6_mfilter
*imf
)
833 struct ip6_msource
*ims
;
834 struct in6_msource
*lims
;
836 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
837 lims
= (struct in6_msource
*)ims
;
838 lims
->im6sl_st
[0] = lims
->im6sl_st
[1];
840 imf
->im6f_st
[0] = imf
->im6f_st
[1];
844 * Reap unreferenced sources from socket-layer filter set.
846 * Caller is expected to be holding im6o_lock.
849 im6f_reap(struct in6_mfilter
*imf
)
851 struct ip6_msource
*ims
, *tims
;
852 struct in6_msource
*lims
;
854 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &imf
->im6f_sources
, tims
) {
855 lims
= (struct in6_msource
*)ims
;
856 if ((lims
->im6sl_st
[0] == MCAST_UNDEFINED
) &&
857 (lims
->im6sl_st
[1] == MCAST_UNDEFINED
)) {
858 MLD_PRINTF(("%s: free in6ms %p\n", __func__
, lims
));
859 RB_REMOVE(ip6_msource_tree
, &imf
->im6f_sources
, ims
);
867 * Purge socket-layer filter set.
869 * Caller is expected to be holding im6o_lock.
872 im6f_purge(struct in6_mfilter
*imf
)
874 struct ip6_msource
*ims
, *tims
;
875 struct in6_msource
*lims
;
877 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &imf
->im6f_sources
, tims
) {
878 lims
= (struct in6_msource
*)ims
;
879 MLD_PRINTF(("%s: free in6ms %p\n", __func__
, lims
));
880 RB_REMOVE(ip6_msource_tree
, &imf
->im6f_sources
, ims
);
884 imf
->im6f_st
[0] = imf
->im6f_st
[1] = MCAST_UNDEFINED
;
885 VERIFY(RB_EMPTY(&imf
->im6f_sources
));
889 * Look up a source filter entry for a multicast group.
891 * inm is the group descriptor to work with.
892 * addr is the IPv6 address to look up.
893 * noalloc may be non-zero to suppress allocation of sources.
894 * *pims will be set to the address of the retrieved or allocated source.
896 * Return 0 if successful, otherwise return a non-zero error code.
899 in6m_get_source(struct in6_multi
*inm
, const struct in6_addr
*addr
,
900 const int noalloc
, struct ip6_msource
**pims
)
902 struct ip6_msource find
;
903 struct ip6_msource
*ims
, *nims
;
905 IN6M_LOCK_ASSERT_HELD(inm
);
907 find
.im6s_addr
= *addr
;
908 ims
= RB_FIND(ip6_msource_tree
, &inm
->in6m_srcs
, &find
);
909 if (ims
== NULL
&& !noalloc
) {
910 if (inm
->in6m_nsrc
== in6_mcast_maxgrpsrc
)
912 nims
= ip6ms_alloc(M_WAITOK
);
915 nims
->im6s_addr
= *addr
;
916 RB_INSERT(ip6_msource_tree
, &inm
->in6m_srcs
, nims
);
919 MLD_PRINTF(("%s: allocated %s as %p\n", __func__
,
920 ip6_sprintf(addr
), ims
));
928 * Helper function to derive the filter mode on a source entry
929 * from its internal counters. Predicates are:
930 * A source is only excluded if all listeners exclude it.
931 * A source is only included if no listeners exclude it,
932 * and at least one listener includes it.
933 * May be used by ifmcstat(8).
936 im6s_get_mode(const struct in6_multi
*inm
, const struct ip6_msource
*ims
,
939 IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm
));
942 if (inm
->in6m_st
[t
].iss_ex
> 0 &&
943 inm
->in6m_st
[t
].iss_ex
== ims
->im6s_st
[t
].ex
)
944 return (MCAST_EXCLUDE
);
945 else if (ims
->im6s_st
[t
].in
> 0 && ims
->im6s_st
[t
].ex
== 0)
946 return (MCAST_INCLUDE
);
947 return (MCAST_UNDEFINED
);
951 * Merge socket-layer source into MLD-layer source.
952 * If rollback is non-zero, perform the inverse of the merge.
955 im6s_merge(struct ip6_msource
*ims
, const struct in6_msource
*lims
,
958 int n
= rollback
? -1 : 1;
960 if (lims
->im6sl_st
[0] == MCAST_EXCLUDE
) {
961 MLD_PRINTF(("%s: t1 ex -= %d on %s\n", __func__
, n
,
962 ip6_sprintf(&lims
->im6s_addr
)));
963 ims
->im6s_st
[1].ex
-= n
;
964 } else if (lims
->im6sl_st
[0] == MCAST_INCLUDE
) {
965 MLD_PRINTF(("%s: t1 in -= %d on %s\n", __func__
, n
,
966 ip6_sprintf(&lims
->im6s_addr
)));
967 ims
->im6s_st
[1].in
-= n
;
970 if (lims
->im6sl_st
[1] == MCAST_EXCLUDE
) {
971 MLD_PRINTF(("%s: t1 ex += %d on %s\n", __func__
, n
,
972 ip6_sprintf(&lims
->im6s_addr
)));
973 ims
->im6s_st
[1].ex
+= n
;
974 } else if (lims
->im6sl_st
[1] == MCAST_INCLUDE
) {
975 MLD_PRINTF(("%s: t1 in += %d on %s\n", __func__
, n
,
976 ip6_sprintf(&lims
->im6s_addr
)));
977 ims
->im6s_st
[1].in
+= n
;
982 * Atomically update the global in6_multi state, when a membership's
983 * filter list is being updated in any way.
985 * imf is the per-inpcb-membership group filter pointer.
986 * A fake imf may be passed for in-kernel consumers.
988 * XXX This is a candidate for a set-symmetric-difference style loop
989 * which would eliminate the repeated lookup from root of ims nodes,
990 * as they share the same key space.
992 * If any error occurred this function will back out of refcounts
993 * and return a non-zero value.
996 in6m_merge(struct in6_multi
*inm
, /*const*/ struct in6_mfilter
*imf
)
998 struct ip6_msource
*ims
, *nims
;
999 struct in6_msource
*lims
;
1000 int schanged
, error
;
1003 IN6M_LOCK_ASSERT_HELD(inm
);
1010 * Update the source filters first, as this may fail.
1011 * Maintain count of in-mode filters at t0, t1. These are
1012 * used to work out if we transition into ASM mode or not.
1013 * Maintain a count of source filters whose state was
1014 * actually modified by this operation.
1016 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
1017 lims
= (struct in6_msource
*)ims
;
1018 if (lims
->im6sl_st
[0] == imf
->im6f_st
[0]) nsrc0
++;
1019 if (lims
->im6sl_st
[1] == imf
->im6f_st
[1]) nsrc1
++;
1020 if (lims
->im6sl_st
[0] == lims
->im6sl_st
[1]) continue;
1021 error
= in6m_get_source(inm
, &lims
->im6s_addr
, 0, &nims
);
1025 im6s_merge(nims
, lims
, 0);
1028 struct ip6_msource
*bims
;
1030 RB_FOREACH_REVERSE_FROM(ims
, ip6_msource_tree
, nims
) {
1031 lims
= (struct in6_msource
*)ims
;
1032 if (lims
->im6sl_st
[0] == lims
->im6sl_st
[1])
1034 (void) in6m_get_source(inm
, &lims
->im6s_addr
, 1, &bims
);
1037 im6s_merge(bims
, lims
, 1);
1042 MLD_PRINTF(("%s: imf filters in-mode: %d at t0, %d at t1\n",
1043 __func__
, nsrc0
, nsrc1
));
1045 /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */
1046 if (imf
->im6f_st
[0] == imf
->im6f_st
[1] &&
1047 imf
->im6f_st
[1] == MCAST_INCLUDE
) {
1049 MLD_PRINTF(("%s: --in on inm at t1\n", __func__
));
1050 --inm
->in6m_st
[1].iss_in
;
1054 /* Handle filter mode transition on socket. */
1055 if (imf
->im6f_st
[0] != imf
->im6f_st
[1]) {
1056 MLD_PRINTF(("%s: imf transition %d to %d\n",
1057 __func__
, imf
->im6f_st
[0], imf
->im6f_st
[1]));
1059 if (imf
->im6f_st
[0] == 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
[0] == MCAST_INCLUDE
) {
1063 MLD_PRINTF(("%s: --in on inm at t1\n", __func__
));
1064 --inm
->in6m_st
[1].iss_in
;
1067 if (imf
->im6f_st
[1] == MCAST_EXCLUDE
) {
1068 MLD_PRINTF(("%s: ex++ on inm at t1\n", __func__
));
1069 inm
->in6m_st
[1].iss_ex
++;
1070 } else if (imf
->im6f_st
[1] == MCAST_INCLUDE
&& nsrc1
> 0) {
1071 MLD_PRINTF(("%s: in++ on inm at t1\n", __func__
));
1072 inm
->in6m_st
[1].iss_in
++;
1077 * Track inm filter state in terms of listener counts.
1078 * If there are any exclusive listeners, stack-wide
1079 * membership is exclusive.
1080 * Otherwise, if only inclusive listeners, stack-wide is inclusive.
1081 * If no listeners remain, state is undefined at t1,
1082 * and the MLD lifecycle for this group should finish.
1084 if (inm
->in6m_st
[1].iss_ex
> 0) {
1085 MLD_PRINTF(("%s: transition to EX\n", __func__
));
1086 inm
->in6m_st
[1].iss_fmode
= MCAST_EXCLUDE
;
1087 } else if (inm
->in6m_st
[1].iss_in
> 0) {
1088 MLD_PRINTF(("%s: transition to IN\n", __func__
));
1089 inm
->in6m_st
[1].iss_fmode
= MCAST_INCLUDE
;
1091 MLD_PRINTF(("%s: transition to UNDEF\n", __func__
));
1092 inm
->in6m_st
[1].iss_fmode
= MCAST_UNDEFINED
;
1095 /* Decrement ASM listener count on transition out of ASM mode. */
1096 if (imf
->im6f_st
[0] == MCAST_EXCLUDE
&& nsrc0
== 0) {
1097 if ((imf
->im6f_st
[1] != MCAST_EXCLUDE
) ||
1098 (imf
->im6f_st
[1] == MCAST_EXCLUDE
&& nsrc1
> 0)) {
1099 MLD_PRINTF(("%s: --asm on inm at t1\n", __func__
));
1100 --inm
->in6m_st
[1].iss_asm
;
1104 /* Increment ASM listener count on transition to ASM mode. */
1105 if (imf
->im6f_st
[1] == MCAST_EXCLUDE
&& nsrc1
== 0) {
1106 MLD_PRINTF(("%s: asm++ on inm at t1\n", __func__
));
1107 inm
->in6m_st
[1].iss_asm
++;
1110 MLD_PRINTF(("%s: merged imf %p to inm %p\n", __func__
, imf
, inm
));
1115 MLD_PRINTF(("%s: sources changed; reaping\n", __func__
));
1122 * Mark an in6_multi's filter set deltas as committed.
1123 * Called by MLD after a state change has been enqueued.
1126 in6m_commit(struct in6_multi
*inm
)
1128 struct ip6_msource
*ims
;
1130 IN6M_LOCK_ASSERT_HELD(inm
);
1132 MLD_PRINTF(("%s: commit inm %p\n", __func__
, inm
));
1133 MLD_PRINTF(("%s: pre commit:\n", __func__
));
1136 RB_FOREACH(ims
, ip6_msource_tree
, &inm
->in6m_srcs
) {
1137 ims
->im6s_st
[0] = ims
->im6s_st
[1];
1139 inm
->in6m_st
[0] = inm
->in6m_st
[1];
1143 * Reap unreferenced nodes from an in6_multi's filter set.
1146 in6m_reap(struct in6_multi
*inm
)
1148 struct ip6_msource
*ims
, *tims
;
1150 IN6M_LOCK_ASSERT_HELD(inm
);
1152 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &inm
->in6m_srcs
, tims
) {
1153 if (ims
->im6s_st
[0].ex
> 0 || ims
->im6s_st
[0].in
> 0 ||
1154 ims
->im6s_st
[1].ex
> 0 || ims
->im6s_st
[1].in
> 0 ||
1157 MLD_PRINTF(("%s: free ims %p\n", __func__
, ims
));
1158 RB_REMOVE(ip6_msource_tree
, &inm
->in6m_srcs
, ims
);
1165 * Purge all source nodes from an in6_multi's filter set.
1168 in6m_purge(struct in6_multi
*inm
)
1170 struct ip6_msource
*ims
, *tims
;
1172 IN6M_LOCK_ASSERT_HELD(inm
);
1174 RB_FOREACH_SAFE(ims
, ip6_msource_tree
, &inm
->in6m_srcs
, tims
) {
1175 MLD_PRINTF(("%s: free ims %p\n", __func__
, ims
));
1176 RB_REMOVE(ip6_msource_tree
, &inm
->in6m_srcs
, ims
);
1183 * Join a multicast address w/o sources.
1184 * KAME compatibility entry point.
1187 struct in6_multi_mship
*
1188 in6_joingroup(struct ifnet
*ifp
, struct in6_addr
*mcaddr
,
1189 int *errorp
, int delay
)
1191 struct in6_multi_mship
*imm
;
1196 imm
= in6_multi_mship_alloc(M_WAITOK
);
1202 delay
= (delay
* PR_SLOWHZ
) / hz
;
1204 error
= in6_mc_join(ifp
, mcaddr
, NULL
, &imm
->i6mm_maddr
, delay
);
1207 in6_multi_mship_free(imm
);
1215 * Leave a multicast address w/o sources.
1216 * KAME compatibility entry point.
1219 in6_leavegroup(struct in6_multi_mship
*imm
)
1221 if (imm
->i6mm_maddr
!= NULL
) {
1222 in6_mc_leave(imm
->i6mm_maddr
, NULL
);
1223 IN6M_REMREF(imm
->i6mm_maddr
);
1224 imm
->i6mm_maddr
= NULL
;
1226 in6_multi_mship_free(imm
);
1231 * Join a multicast group; real entry point.
1233 * Only preserves atomicity at inm level.
1234 * NOTE: imf argument cannot be const due to sys/tree.h limitations.
1236 * If the MLD downcall fails, the group is not joined, and an error
1240 in6_mc_join(struct ifnet
*ifp
, const struct in6_addr
*mcaddr
,
1241 /*const*/ struct in6_mfilter
*imf
, struct in6_multi
**pinm
,
1244 struct in6_mfilter timf
;
1245 struct in6_multi
*inm
= NULL
;
1249 * Sanity: Check scope zone ID was set for ifp, if and
1250 * only if group is scoped to an interface.
1252 VERIFY(IN6_IS_ADDR_MULTICAST(mcaddr
));
1253 if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr
) ||
1254 IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr
)) {
1255 VERIFY(mcaddr
->s6_addr16
[1] != 0);
1258 MLD_PRINTF(("%s: join %s on %p(%s%d))\n", __func__
,
1259 ip6_sprintf(mcaddr
), ifp
, ifp
->if_name
, ifp
->if_unit
));
1264 * If no imf was specified (i.e. kernel consumer),
1265 * fake one up and assume it is an ASM join.
1268 im6f_init(&timf
, MCAST_UNDEFINED
, MCAST_EXCLUDE
);
1272 error
= in6_mc_get(ifp
, mcaddr
, &inm
);
1274 MLD_PRINTF(("%s: in6_mc_get() failure\n", __func__
));
1278 MLD_PRINTF(("%s: merge inm state\n", __func__
));
1281 error
= in6m_merge(inm
, imf
);
1283 MLD_PRINTF(("%s: failed to merge inm state\n", __func__
));
1284 goto out_in6m_release
;
1287 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
1288 error
= mld_change_state(inm
, delay
);
1290 MLD_PRINTF(("%s: failed to update source\n", __func__
));
1291 goto out_in6m_release
;
1296 MLD_PRINTF(("%s: dropping ref on %p\n", __func__
, inm
));
1301 *pinm
= inm
; /* keep refcount from in6_mc_get() */
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
;
1328 IN6M_LOCK_ASSERT_NOTHELD(inm
);
1330 in6_multihead_lock_exclusive();
1333 MLD_PRINTF(("%s: leave inm %p, %s/%s%d, imf %p\n", __func__
,
1334 inm
, ip6_sprintf(&inm
->in6m_addr
),
1335 (in6m_is_ifp_detached(inm
) ? "null" : inm
->in6m_ifp
->if_name
),
1336 inm
->in6m_ifp
->if_unit
, imf
));
1339 * If no imf was specified (i.e. kernel consumer),
1340 * fake one up and assume it is an ASM join.
1343 im6f_init(&timf
, MCAST_EXCLUDE
, MCAST_UNDEFINED
);
1348 * Begin state merge transaction at MLD layer.
1350 * As this particular invocation should not cause any memory
1351 * to be allocated, and there is no opportunity to roll back
1352 * the transaction, it MUST NOT fail.
1354 MLD_PRINTF(("%s: merge inm state\n", __func__
));
1356 error
= in6m_merge(inm
, imf
);
1357 KASSERT(error
== 0, ("%s: failed to merge inm state\n", __func__
));
1359 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
1360 error
= mld_change_state(inm
, 0);
1363 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
1365 lastref
= in6_multi_detach(inm
);
1366 VERIFY(!lastref
|| (!(inm
->in6m_debug
& IFD_ATTACHED
) &&
1367 inm
->in6m_reqcnt
== 0));
1369 in6_multihead_lock_done();
1372 IN6M_REMREF(inm
); /* for in6_multihead list */
1378 * Block or unblock an ASM multicast source on an inpcb.
1379 * This implements the delta-based API described in RFC 3678.
1381 * The delta-based API applies only to exclusive-mode memberships.
1382 * An MLD downcall will be performed.
1384 * Return 0 if successful, otherwise return an appropriate error code.
1387 in6p_block_unblock_source(struct inpcb
*inp
, struct sockopt
*sopt
)
1389 struct group_source_req gsr
;
1390 sockunion_t
*gsa
, *ssa
;
1392 struct in6_mfilter
*imf
;
1393 struct ip6_moptions
*imo
;
1394 struct in6_msource
*ims
;
1395 struct in6_multi
*inm
;
1404 memset(&gsr
, 0, sizeof(struct group_source_req
));
1405 gsa
= (sockunion_t
*)&gsr
.gsr_group
;
1406 ssa
= (sockunion_t
*)&gsr
.gsr_source
;
1408 switch (sopt
->sopt_name
) {
1409 case MCAST_BLOCK_SOURCE
:
1410 case MCAST_UNBLOCK_SOURCE
:
1411 error
= sooptcopyin(sopt
, &gsr
,
1412 sizeof(struct group_source_req
),
1413 sizeof(struct group_source_req
));
1417 if (gsa
->sin6
.sin6_family
!= AF_INET6
||
1418 gsa
->sin6
.sin6_len
!= sizeof(struct sockaddr_in6
))
1421 if (ssa
->sin6
.sin6_family
!= AF_INET6
||
1422 ssa
->sin6
.sin6_len
!= sizeof(struct sockaddr_in6
))
1425 ifnet_head_lock_shared();
1426 if (gsr
.gsr_interface
== 0 ||
1427 (u_int
)if_index
< gsr
.gsr_interface
) {
1429 return (EADDRNOTAVAIL
);
1432 ifp
= ifindex2ifnet
[gsr
.gsr_interface
];
1436 return (EADDRNOTAVAIL
);
1438 if (sopt
->sopt_name
== MCAST_BLOCK_SOURCE
)
1443 MLD_PRINTF(("%s: unknown sopt_name %d\n",
1444 __func__
, sopt
->sopt_name
));
1445 return (EOPNOTSUPP
);
1449 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6
.sin6_addr
))
1452 (void) in6_setscope(&gsa
->sin6
.sin6_addr
, ifp
, NULL
);
1455 * Check if we are actually a member of this group.
1457 imo
= in6p_findmoptions(inp
);
1462 idx
= im6o_match_group(imo
, ifp
, &gsa
->sa
);
1463 if (idx
== (size_t)-1 || imo
->im6o_mfilters
== NULL
) {
1464 error
= EADDRNOTAVAIL
;
1465 goto out_imo_locked
;
1468 VERIFY(imo
->im6o_mfilters
!= NULL
);
1469 imf
= &imo
->im6o_mfilters
[idx
];
1470 inm
= imo
->im6o_membership
[idx
];
1473 * Attempting to use the delta-based API on an
1474 * non exclusive-mode membership is an error.
1476 fmode
= imf
->im6f_st
[0];
1477 if (fmode
!= MCAST_EXCLUDE
) {
1479 goto out_imo_locked
;
1483 * Deal with error cases up-front:
1484 * Asked to block, but already blocked; or
1485 * Asked to unblock, but nothing to unblock.
1486 * If adding a new block entry, allocate it.
1488 ims
= im6o_match_source(imo
, idx
, &ssa
->sa
);
1489 if ((ims
!= NULL
&& doblock
) || (ims
== NULL
&& !doblock
)) {
1490 MLD_PRINTF(("%s: source %s %spresent\n", __func__
,
1491 ip6_sprintf(&ssa
->sin6
.sin6_addr
),
1492 doblock
? "" : "not "));
1493 error
= EADDRNOTAVAIL
;
1494 goto out_imo_locked
;
1498 * Begin state merge transaction at socket layer.
1501 MLD_PRINTF(("%s: %s source\n", __func__
, "block"));
1502 ims
= im6f_graft(imf
, fmode
, &ssa
->sin6
);
1506 MLD_PRINTF(("%s: %s source\n", __func__
, "allow"));
1507 error
= im6f_prune(imf
, &ssa
->sin6
);
1511 MLD_PRINTF(("%s: merge imf state failed\n", __func__
));
1512 goto out_im6f_rollback
;
1516 * Begin state merge transaction at MLD layer.
1519 MLD_PRINTF(("%s: merge inm state\n", __func__
));
1520 error
= in6m_merge(inm
, imf
);
1522 MLD_PRINTF(("%s: failed to merge inm state\n", __func__
));
1524 goto out_im6f_rollback
;
1527 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
1528 error
= mld_change_state(inm
, 0);
1532 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
1545 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
1550 * Given an inpcb, return its multicast options structure pointer. Accepts
1551 * an unlocked inpcb pointer, but will return it locked. May sleep.
1554 static struct ip6_moptions
*
1555 in6p_findmoptions(struct inpcb
*inp
)
1557 struct ip6_moptions
*imo
;
1558 struct in6_multi
**immp
;
1559 struct in6_mfilter
*imfp
;
1562 if ((imo
= inp
->in6p_moptions
) != NULL
) {
1563 IM6O_ADDREF(imo
); /* for caller */
1567 imo
= ip6_allocmoptions(M_WAITOK
);
1571 immp
= _MALLOC(sizeof (*immp
) * IPV6_MIN_MEMBERSHIPS
, M_IP6MOPTS
,
1578 imfp
= _MALLOC(sizeof (struct in6_mfilter
) * IPV6_MIN_MEMBERSHIPS
,
1579 M_IN6MFILTER
, M_WAITOK
| M_ZERO
);
1581 _FREE(immp
, M_IP6MOPTS
);
1586 imo
->im6o_multicast_ifp
= NULL
;
1587 imo
->im6o_multicast_hlim
= ip6_defmcasthlim
;
1588 imo
->im6o_multicast_loop
= in6_mcast_loop
;
1589 imo
->im6o_num_memberships
= 0;
1590 imo
->im6o_max_memberships
= IPV6_MIN_MEMBERSHIPS
;
1591 imo
->im6o_membership
= immp
;
1593 /* Initialize per-group source filters. */
1594 for (idx
= 0; idx
< IPV6_MIN_MEMBERSHIPS
; idx
++)
1595 im6f_init(&imfp
[idx
], MCAST_UNDEFINED
, MCAST_EXCLUDE
);
1597 imo
->im6o_mfilters
= imfp
;
1598 inp
->in6p_moptions
= imo
; /* keep reference from ip6_allocmoptions() */
1599 IM6O_ADDREF(imo
); /* for caller */
1605 * Atomically get source filters on a socket for an IPv6 multicast group.
1606 * Called with INP lock held; returns with lock released.
1609 in6p_get_source_filters(struct inpcb
*inp
, struct sockopt
*sopt
)
1611 struct __msfilterreq64 msfr
, msfr64
;
1612 struct __msfilterreq32 msfr32
;
1615 struct ip6_moptions
*imo
;
1616 struct in6_mfilter
*imf
;
1617 struct ip6_msource
*ims
;
1618 struct in6_msource
*lims
;
1619 struct sockaddr_in6
*psin
;
1620 struct sockaddr_storage
*ptss
;
1621 struct sockaddr_storage
*tss
;
1623 size_t idx
, nsrcs
, ncsrcs
;
1624 user_addr_t tmp_ptr
;
1626 imo
= inp
->in6p_moptions
;
1627 VERIFY(imo
!= NULL
);
1629 if (IS_64BIT_PROCESS(current_proc())) {
1630 error
= sooptcopyin(sopt
, &msfr64
,
1631 sizeof(struct __msfilterreq64
),
1632 sizeof(struct __msfilterreq64
));
1635 /* we never use msfr.msfr_srcs; */
1636 memcpy(&msfr
, &msfr64
, sizeof(msfr
));
1638 error
= sooptcopyin(sopt
, &msfr32
,
1639 sizeof(struct __msfilterreq32
),
1640 sizeof(struct __msfilterreq32
));
1643 /* we never use msfr.msfr_srcs; */
1644 memcpy(&msfr
, &msfr32
, sizeof(msfr
));
1647 if (msfr
.msfr_group
.ss_family
!= AF_INET6
||
1648 msfr
.msfr_group
.ss_len
!= sizeof(struct sockaddr_in6
))
1651 gsa
= (sockunion_t
*)&msfr
.msfr_group
;
1652 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6
.sin6_addr
))
1655 ifnet_head_lock_shared();
1656 if (msfr
.msfr_ifindex
== 0 || (u_int
)if_index
< msfr
.msfr_ifindex
) {
1658 return (EADDRNOTAVAIL
);
1660 ifp
= ifindex2ifnet
[msfr
.msfr_ifindex
];
1664 return (EADDRNOTAVAIL
);
1666 if ((size_t) msfr
.msfr_nsrcs
>
1667 SIZE_MAX
/ sizeof(struct sockaddr_storage
))
1668 msfr
.msfr_nsrcs
= SIZE_MAX
/ sizeof(struct sockaddr_storage
);
1670 if (msfr
.msfr_nsrcs
> in6_mcast_maxsocksrc
)
1671 msfr
.msfr_nsrcs
= in6_mcast_maxsocksrc
;
1673 (void)in6_setscope(&gsa
->sin6
.sin6_addr
, ifp
, NULL
);
1677 * Lookup group on the socket.
1679 idx
= im6o_match_group(imo
, ifp
, &gsa
->sa
);
1680 if (idx
== (size_t)-1 || imo
->im6o_mfilters
== NULL
) {
1682 return (EADDRNOTAVAIL
);
1684 imf
= &imo
->im6o_mfilters
[idx
];
1687 * Ignore memberships which are in limbo.
1689 if (imf
->im6f_st
[1] == MCAST_UNDEFINED
) {
1693 msfr
.msfr_fmode
= imf
->im6f_st
[1];
1696 * If the user specified a buffer, copy out the source filter
1697 * entries to userland gracefully.
1698 * We only copy out the number of entries which userland
1699 * has asked for, but we always tell userland how big the
1700 * buffer really needs to be.
1704 if (IS_64BIT_PROCESS(current_proc()))
1705 tmp_ptr
= msfr64
.msfr_srcs
;
1707 tmp_ptr
= CAST_USER_ADDR_T(msfr32
.msfr_srcs
);
1709 if (tmp_ptr
!= USER_ADDR_NULL
&& msfr
.msfr_nsrcs
> 0) {
1710 tss
= _MALLOC((size_t) msfr
.msfr_nsrcs
* sizeof(*tss
),
1711 M_TEMP
, M_WAITOK
| M_ZERO
);
1716 bzero(tss
, (size_t) msfr
.msfr_nsrcs
* sizeof(*tss
));
1720 * Count number of sources in-mode at t0.
1721 * If buffer space exists and remains, copy out source entries.
1723 nsrcs
= msfr
.msfr_nsrcs
;
1726 RB_FOREACH(ims
, ip6_msource_tree
, &imf
->im6f_sources
) {
1727 lims
= (struct in6_msource
*)ims
;
1728 if (lims
->im6sl_st
[0] == MCAST_UNDEFINED
||
1729 lims
->im6sl_st
[0] != imf
->im6f_st
[0])
1731 if (tss
!= NULL
&& nsrcs
> 0) {
1732 psin
= (struct sockaddr_in6
*)ptss
;
1733 psin
->sin6_family
= AF_INET6
;
1734 psin
->sin6_len
= sizeof(struct sockaddr_in6
);
1735 psin
->sin6_addr
= lims
->im6s_addr
;
1736 psin
->sin6_port
= 0;
1746 error
= copyout(tss
, tmp_ptr
, ncsrcs
* sizeof(*tss
));
1752 msfr
.msfr_nsrcs
= ncsrcs
;
1753 if (IS_64BIT_PROCESS(current_proc())) {
1754 msfr64
.msfr_ifindex
= msfr
.msfr_ifindex
;
1755 msfr64
.msfr_fmode
= msfr
.msfr_fmode
;
1756 msfr64
.msfr_nsrcs
= msfr
.msfr_nsrcs
;
1757 memcpy(&msfr64
.msfr_group
, &msfr
.msfr_group
,
1758 sizeof(struct sockaddr_storage
));
1759 error
= sooptcopyout(sopt
, &msfr64
,
1760 sizeof(struct __msfilterreq64
));
1762 msfr32
.msfr_ifindex
= msfr
.msfr_ifindex
;
1763 msfr32
.msfr_fmode
= msfr
.msfr_fmode
;
1764 msfr32
.msfr_nsrcs
= msfr
.msfr_nsrcs
;
1765 memcpy(&msfr64
.msfr_group
, &msfr
.msfr_group
,
1766 sizeof(struct sockaddr_storage
));
1767 error
= sooptcopyout(sopt
, &msfr32
,
1768 sizeof(struct __msfilterreq32
));
1775 * Return the IP multicast options in response to user getsockopt().
1778 ip6_getmoptions(struct inpcb
*inp
, struct sockopt
*sopt
)
1780 struct ip6_moptions
*im6o
;
1784 im6o
= inp
->in6p_moptions
;
1786 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
1787 * or is a divert socket, reject it.
1789 if (inp
->inp_socket
->so_proto
->pr_protocol
== IPPROTO_DIVERT
||
1790 (inp
->inp_socket
->so_proto
->pr_type
!= SOCK_RAW
&&
1791 inp
->inp_socket
->so_proto
->pr_type
!= SOCK_DGRAM
)) {
1792 return (EOPNOTSUPP
);
1796 switch (sopt
->sopt_name
) {
1797 case IPV6_MULTICAST_IF
:
1800 if (im6o
== NULL
|| im6o
->im6o_multicast_ifp
== NULL
) {
1803 optval
= im6o
->im6o_multicast_ifp
->if_index
;
1807 error
= sooptcopyout(sopt
, &optval
, sizeof(u_int
));
1810 case IPV6_MULTICAST_HOPS
:
1812 optval
= ip6_defmcasthlim
;
1815 optval
= im6o
->im6o_multicast_hlim
;
1818 error
= sooptcopyout(sopt
, &optval
, sizeof(u_int
));
1821 case IPV6_MULTICAST_LOOP
:
1823 optval
= in6_mcast_loop
; /* XXX VIMAGE */
1826 optval
= im6o
->im6o_multicast_loop
;
1829 error
= sooptcopyout(sopt
, &optval
, sizeof(u_int
));
1834 error
= EADDRNOTAVAIL
;
1836 error
= in6p_get_source_filters(inp
, sopt
);
1841 error
= ENOPROTOOPT
;
1849 * Look up the ifnet to use for a multicast group membership,
1850 * given the address of an IPv6 group.
1852 * This routine exists to support legacy IPv6 multicast applications.
1854 * If inp is non-NULL and is bound to an interface, use this socket's
1855 * inp_boundif for any required routing table lookup.
1857 * If the route lookup fails, return NULL.
1859 * FUTURE: Support multiple forwarding tables for IPv6.
1861 * Returns NULL if no ifp could be found.
1863 static struct ifnet
*
1864 in6p_lookup_mcast_ifp(const struct inpcb
*in6p
,
1865 const struct sockaddr_in6
*gsin6
)
1867 struct route_in6 ro6
;
1869 unsigned int ifscope
= IFSCOPE_NONE
;
1871 VERIFY(in6p
== NULL
|| (in6p
->inp_vflag
& INP_IPV6
));
1872 VERIFY(gsin6
->sin6_family
== AF_INET6
);
1873 if (IN6_IS_ADDR_MULTICAST(&gsin6
->sin6_addr
) == 0)
1876 if (in6p
!= NULL
&& (in6p
->inp_flags
& INP_BOUND_IF
))
1877 ifscope
= in6p
->inp_boundifp
->if_index
;
1880 memset(&ro6
, 0, sizeof(struct route_in6
));
1881 memcpy(&ro6
.ro_dst
, gsin6
, sizeof(struct sockaddr_in6
));
1882 rtalloc_scoped_ign((struct route
*)&ro6
, 0, ifscope
);
1883 if (ro6
.ro_rt
!= NULL
) {
1884 ifp
= ro6
.ro_rt
->rt_ifp
;
1885 VERIFY(ifp
!= NULL
);
1893 * Since ipv6_mreq contains an ifindex and ip_mreq contains an AF_INET
1894 * address, we need to lookup the AF_INET address when translating an
1895 * ipv6_mreq structure into an ipmreq structure.
1896 * This is used when userland performs multicast setsockopt() on AF_INET6
1897 * sockets with AF_INET multicast addresses (IPv6 v4 mapped addresses).
1900 in6p_lookup_v4addr(struct ipv6_mreq
*mreq
, struct ip_mreq
*v4mreq
)
1904 struct sockaddr_in
*sin
;
1906 ifnet_head_lock_shared();
1907 if (mreq
->ipv6mr_interface
> (unsigned int)if_index
) {
1909 return (EADDRNOTAVAIL
);
1911 ifp
= ifindex2ifnet
[mreq
->ipv6mr_interface
];
1914 return (EADDRNOTAVAIL
);
1915 ifa
= ifa_ifpgetprimary(ifp
, AF_INET
);
1917 return (EADDRNOTAVAIL
);
1918 sin
= (struct sockaddr_in
*)(uintptr_t)(size_t)ifa
->ifa_addr
;
1919 v4mreq
->imr_interface
.s_addr
= sin
->sin_addr
.s_addr
;
1926 * Join an IPv6 multicast group, possibly with a source.
1928 * FIXME: The KAME use of the unspecified address (::)
1929 * to join *all* multicast groups is currently unsupported.
1932 in6p_join_group(struct inpcb
*inp
, struct sockopt
*sopt
)
1934 struct group_source_req gsr
;
1935 sockunion_t
*gsa
, *ssa
;
1937 struct in6_mfilter
*imf
;
1938 struct ip6_moptions
*imo
;
1939 struct in6_multi
*inm
= NULL
;
1940 struct in6_msource
*lims
= NULL
;
1943 uint32_t scopeid
= 0;
1950 memset(&gsr
, 0, sizeof(struct group_source_req
));
1951 gsa
= (sockunion_t
*)&gsr
.gsr_group
;
1952 gsa
->ss
.ss_family
= AF_UNSPEC
;
1953 ssa
= (sockunion_t
*)&gsr
.gsr_source
;
1954 ssa
->ss
.ss_family
= AF_UNSPEC
;
1957 * Chew everything into struct group_source_req.
1958 * Overwrite the port field if present, as the sockaddr
1959 * being copied in may be matched with a binary comparison.
1960 * Ignore passed-in scope ID.
1962 switch (sopt
->sopt_name
) {
1963 case IPV6_JOIN_GROUP
: {
1964 struct ipv6_mreq mreq
;
1965 struct sockaddr_in6
*gsin6
;
1967 error
= sooptcopyin(sopt
, &mreq
, sizeof(struct ipv6_mreq
),
1968 sizeof(struct ipv6_mreq
));
1971 if (IN6_IS_ADDR_V4MAPPED(&mreq
.ipv6mr_multiaddr
)) {
1972 struct ip_mreq v4mreq
;
1973 struct sockopt v4sopt
;
1975 v4mreq
.imr_multiaddr
.s_addr
=
1976 mreq
.ipv6mr_multiaddr
.s6_addr32
[3];
1977 if (mreq
.ipv6mr_interface
== 0)
1978 v4mreq
.imr_interface
.s_addr
= INADDR_ANY
;
1980 error
= in6p_lookup_v4addr(&mreq
, &v4mreq
);
1983 v4sopt
.sopt_dir
= SOPT_SET
;
1984 v4sopt
.sopt_level
= sopt
->sopt_level
;
1985 v4sopt
.sopt_name
= IP_ADD_MEMBERSHIP
;
1986 v4sopt
.sopt_val
= CAST_USER_ADDR_T(&v4mreq
);
1987 v4sopt
.sopt_valsize
= sizeof(v4mreq
);
1988 v4sopt
.sopt_p
= kernproc
;
1990 return (inp_join_group(inp
, &v4sopt
));
1992 gsa
->sin6
.sin6_family
= AF_INET6
;
1993 gsa
->sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
1994 gsa
->sin6
.sin6_addr
= mreq
.ipv6mr_multiaddr
;
1998 /* Only allow IPv6 multicast addresses */
1999 if (IN6_IS_ADDR_MULTICAST(&gsin6
->sin6_addr
) == 0) {
2003 if (mreq
.ipv6mr_interface
== 0) {
2004 ifp
= in6p_lookup_mcast_ifp(inp
, gsin6
);
2006 ifnet_head_lock_shared();
2007 if ((u_int
)if_index
< mreq
.ipv6mr_interface
) {
2009 return (EADDRNOTAVAIL
);
2011 ifp
= ifindex2ifnet
[mreq
.ipv6mr_interface
];
2014 MLD_PRINTF(("%s: ipv6mr_interface = %d, ifp = %p\n",
2015 __func__
, mreq
.ipv6mr_interface
, ifp
));
2019 case MCAST_JOIN_GROUP
:
2020 case MCAST_JOIN_SOURCE_GROUP
:
2021 if (sopt
->sopt_name
== MCAST_JOIN_GROUP
) {
2022 error
= sooptcopyin(sopt
, &gsr
,
2023 sizeof(struct group_req
),
2024 sizeof(struct group_req
));
2025 } else if (sopt
->sopt_name
== MCAST_JOIN_SOURCE_GROUP
) {
2026 error
= sooptcopyin(sopt
, &gsr
,
2027 sizeof(struct group_source_req
),
2028 sizeof(struct group_source_req
));
2033 if (gsa
->sin6
.sin6_family
!= AF_INET6
||
2034 gsa
->sin6
.sin6_len
!= sizeof(struct sockaddr_in6
))
2037 if (sopt
->sopt_name
== MCAST_JOIN_SOURCE_GROUP
) {
2038 if (ssa
->sin6
.sin6_family
!= AF_INET6
||
2039 ssa
->sin6
.sin6_len
!= sizeof(struct sockaddr_in6
))
2041 if (IN6_IS_ADDR_MULTICAST(&ssa
->sin6
.sin6_addr
))
2044 * TODO: Validate embedded scope ID in source
2045 * list entry against passed-in ifp, if and only
2046 * if source list filter entry is iface or node local.
2048 in6_clearscope(&ssa
->sin6
.sin6_addr
);
2049 ssa
->sin6
.sin6_port
= 0;
2050 ssa
->sin6
.sin6_scope_id
= 0;
2053 ifnet_head_lock_shared();
2054 if (gsr
.gsr_interface
== 0 ||
2055 (u_int
)if_index
< gsr
.gsr_interface
) {
2057 return (EADDRNOTAVAIL
);
2059 ifp
= ifindex2ifnet
[gsr
.gsr_interface
];
2064 MLD_PRINTF(("%s: unknown sopt_name %d\n",
2065 __func__
, sopt
->sopt_name
));
2066 return (EOPNOTSUPP
);
2070 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6
.sin6_addr
))
2073 if (ifp
== NULL
|| (ifp
->if_flags
& IFF_MULTICAST
) == 0)
2074 return (EADDRNOTAVAIL
);
2076 gsa
->sin6
.sin6_port
= 0;
2077 gsa
->sin6
.sin6_scope_id
= 0;
2080 * Always set the scope zone ID on memberships created from userland.
2081 * Use the passed-in ifp to do this.
2083 (void)in6_setscope(&gsa
->sin6
.sin6_addr
, ifp
, &scopeid
);
2085 * Some addresses are not valid without an embedded scopeid.
2086 * This check must be present because otherwise we will later hit
2087 * a VERIFY() in in6_mc_join().
2089 if ((IN6_IS_ADDR_MC_LINKLOCAL(&gsa
->sin6
.sin6_addr
) ||
2090 IN6_IS_ADDR_MC_INTFACELOCAL(&gsa
->sin6
.sin6_addr
)) &&
2091 (scopeid
== 0 || gsa
->sin6
.sin6_addr
.s6_addr16
[1] == 0))
2094 imo
= in6p_findmoptions(inp
);
2099 idx
= im6o_match_group(imo
, ifp
, &gsa
->sa
);
2100 if (idx
== (size_t)-1) {
2103 inm
= imo
->im6o_membership
[idx
];
2104 imf
= &imo
->im6o_mfilters
[idx
];
2105 if (ssa
->ss
.ss_family
!= AF_UNSPEC
) {
2107 * MCAST_JOIN_SOURCE_GROUP on an exclusive membership
2108 * is an error. On an existing inclusive membership,
2109 * it just adds the source to the filter list.
2111 if (imf
->im6f_st
[1] != MCAST_INCLUDE
) {
2113 goto out_imo_locked
;
2116 * Throw out duplicates.
2118 * XXX FIXME: This makes a naive assumption that
2119 * even if entries exist for *ssa in this imf,
2120 * they will be rejected as dupes, even if they
2121 * are not valid in the current mode (in-mode).
2123 * in6_msource is transactioned just as for anything
2124 * else in SSM -- but note naive use of in6m_graft()
2125 * below for allocating new filter entries.
2127 * This is only an issue if someone mixes the
2128 * full-state SSM API with the delta-based API,
2129 * which is discouraged in the relevant RFCs.
2131 lims
= im6o_match_source(imo
, idx
, &ssa
->sa
);
2132 if (lims
!= NULL
/*&&
2133 lims->im6sl_st[1] == MCAST_INCLUDE*/) {
2134 error
= EADDRNOTAVAIL
;
2135 goto out_imo_locked
;
2139 * MCAST_JOIN_GROUP on an existing exclusive
2140 * membership is an error; return EADDRINUSE
2141 * to preserve 4.4BSD API idempotence, and
2142 * avoid tedious detour to code below.
2143 * NOTE: This is bending RFC 3678 a bit.
2145 * On an existing inclusive membership, this is also
2146 * an error; if you want to change filter mode,
2147 * you must use the userland API setsourcefilter().
2148 * XXX We don't reject this for imf in UNDEFINED
2149 * state at t1, because allocation of a filter
2150 * is atomic with allocation of a membership.
2153 /* See comments above for EADDRINUSE */
2154 if (imf
->im6f_st
[1] == MCAST_EXCLUDE
)
2156 goto out_imo_locked
;
2161 * Begin state merge transaction at socket layer.
2165 if (imo
->im6o_num_memberships
== imo
->im6o_max_memberships
) {
2166 error
= im6o_grow(imo
, 0);
2168 goto out_imo_locked
;
2171 * Allocate the new slot upfront so we can deal with
2172 * grafting the new source filter in same code path
2173 * as for join-source on existing membership.
2175 idx
= imo
->im6o_num_memberships
;
2176 imo
->im6o_membership
[idx
] = NULL
;
2177 imo
->im6o_num_memberships
++;
2178 VERIFY(imo
->im6o_mfilters
!= NULL
);
2179 imf
= &imo
->im6o_mfilters
[idx
];
2180 VERIFY(RB_EMPTY(&imf
->im6f_sources
));
2184 * Graft new source into filter list for this inpcb's
2185 * membership of the group. The in6_multi may not have
2186 * been allocated yet if this is a new membership, however,
2187 * the in_mfilter slot will be allocated and must be initialized.
2189 * Note: Grafting of exclusive mode filters doesn't happen
2191 * XXX: Should check for non-NULL lims (node exists but may
2192 * not be in-mode) for interop with full-state API.
2194 if (ssa
->ss
.ss_family
!= AF_UNSPEC
) {
2195 /* Membership starts in IN mode */
2197 MLD_PRINTF(("%s: new join w/source\n", __func__
);
2198 im6f_init(imf
, MCAST_UNDEFINED
, MCAST_INCLUDE
));
2200 MLD_PRINTF(("%s: %s source\n", __func__
, "allow"));
2202 lims
= im6f_graft(imf
, MCAST_INCLUDE
, &ssa
->sin6
);
2204 MLD_PRINTF(("%s: merge imf state failed\n",
2210 /* No address specified; Membership starts in EX mode */
2212 MLD_PRINTF(("%s: new join w/o source", __func__
));
2213 im6f_init(imf
, MCAST_UNDEFINED
, MCAST_EXCLUDE
);
2218 * Begin state merge transaction at MLD layer.
2222 VERIFY(inm
== NULL
);
2223 error
= in6_mc_join(ifp
, &gsa
->sin6
.sin6_addr
, imf
, &inm
, 0);
2224 VERIFY(inm
!= NULL
|| error
!= 0);
2227 imo
->im6o_membership
[idx
] = inm
; /* from in6_mc_join() */
2229 MLD_PRINTF(("%s: merge inm state\n", __func__
));
2231 error
= in6m_merge(inm
, imf
);
2233 MLD_PRINTF(("%s: failed to merge inm state\n",
2236 goto out_im6f_rollback
;
2238 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
2239 error
= mld_change_state(inm
, 0);
2242 MLD_PRINTF(("%s: failed mld downcall\n",
2244 goto out_im6f_rollback
;
2260 if (error
&& is_new
) {
2261 VERIFY(inm
== NULL
);
2262 imo
->im6o_membership
[idx
] = NULL
;
2263 --imo
->im6o_num_memberships
;
2268 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2273 * Leave an IPv6 multicast group on an inpcb, possibly with a source.
2276 in6p_leave_group(struct inpcb
*inp
, struct sockopt
*sopt
)
2278 struct ipv6_mreq mreq
;
2279 struct group_source_req gsr
;
2280 sockunion_t
*gsa
, *ssa
;
2282 struct in6_mfilter
*imf
;
2283 struct ip6_moptions
*imo
;
2284 struct in6_msource
*ims
;
2285 struct in6_multi
*inm
= NULL
;
2286 uint32_t ifindex
= 0;
2288 int error
, is_final
;
2294 memset(&gsr
, 0, sizeof(struct group_source_req
));
2295 gsa
= (sockunion_t
*)&gsr
.gsr_group
;
2296 gsa
->ss
.ss_family
= AF_UNSPEC
;
2297 ssa
= (sockunion_t
*)&gsr
.gsr_source
;
2298 ssa
->ss
.ss_family
= AF_UNSPEC
;
2301 * Chew everything passed in up into a struct group_source_req
2302 * as that is easier to process.
2303 * Note: Any embedded scope ID in the multicast group passed
2304 * in by userland is ignored, the interface index is the recommended
2305 * mechanism to specify an interface; see below.
2307 switch (sopt
->sopt_name
) {
2308 case IPV6_LEAVE_GROUP
: {
2309 struct sockaddr_in6
*gsin6
;
2311 error
= sooptcopyin(sopt
, &mreq
, sizeof(struct ipv6_mreq
),
2312 sizeof(struct ipv6_mreq
));
2315 if (IN6_IS_ADDR_V4MAPPED(&mreq
.ipv6mr_multiaddr
)) {
2316 struct ip_mreq v4mreq
;
2317 struct sockopt v4sopt
;
2319 v4mreq
.imr_multiaddr
.s_addr
=
2320 mreq
.ipv6mr_multiaddr
.s6_addr32
[3];
2321 if (mreq
.ipv6mr_interface
== 0)
2322 v4mreq
.imr_interface
.s_addr
= INADDR_ANY
;
2324 error
= in6p_lookup_v4addr(&mreq
, &v4mreq
);
2327 v4sopt
.sopt_dir
= SOPT_SET
;
2328 v4sopt
.sopt_level
= sopt
->sopt_level
;
2329 v4sopt
.sopt_name
= IP_DROP_MEMBERSHIP
;
2330 v4sopt
.sopt_val
= CAST_USER_ADDR_T(&v4mreq
);
2331 v4sopt
.sopt_valsize
= sizeof(v4mreq
);
2332 v4sopt
.sopt_p
= kernproc
;
2334 return (inp_leave_group(inp
, &v4sopt
));
2336 gsa
->sin6
.sin6_family
= AF_INET6
;
2337 gsa
->sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
2338 gsa
->sin6
.sin6_addr
= mreq
.ipv6mr_multiaddr
;
2339 gsa
->sin6
.sin6_port
= 0;
2340 gsa
->sin6
.sin6_scope_id
= 0;
2341 ifindex
= mreq
.ipv6mr_interface
;
2343 /* Only allow IPv6 multicast addresses */
2344 if (IN6_IS_ADDR_MULTICAST(&gsin6
->sin6_addr
) == 0) {
2350 case MCAST_LEAVE_GROUP
:
2351 case MCAST_LEAVE_SOURCE_GROUP
:
2352 if (sopt
->sopt_name
== MCAST_LEAVE_GROUP
) {
2353 error
= sooptcopyin(sopt
, &gsr
,
2354 sizeof(struct group_req
),
2355 sizeof(struct group_req
));
2356 } else if (sopt
->sopt_name
== MCAST_LEAVE_SOURCE_GROUP
) {
2357 error
= sooptcopyin(sopt
, &gsr
,
2358 sizeof(struct group_source_req
),
2359 sizeof(struct group_source_req
));
2364 if (gsa
->sin6
.sin6_family
!= AF_INET6
||
2365 gsa
->sin6
.sin6_len
!= sizeof(struct sockaddr_in6
))
2367 if (sopt
->sopt_name
== MCAST_LEAVE_SOURCE_GROUP
) {
2368 if (ssa
->sin6
.sin6_family
!= AF_INET6
||
2369 ssa
->sin6
.sin6_len
!= sizeof(struct sockaddr_in6
))
2371 if (IN6_IS_ADDR_MULTICAST(&ssa
->sin6
.sin6_addr
))
2374 * TODO: Validate embedded scope ID in source
2375 * list entry against passed-in ifp, if and only
2376 * if source list filter entry is iface or node local.
2378 in6_clearscope(&ssa
->sin6
.sin6_addr
);
2380 gsa
->sin6
.sin6_port
= 0;
2381 gsa
->sin6
.sin6_scope_id
= 0;
2382 ifindex
= gsr
.gsr_interface
;
2386 MLD_PRINTF(("%s: unknown sopt_name %d\n",
2387 __func__
, sopt
->sopt_name
));
2388 return (EOPNOTSUPP
);
2392 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6
.sin6_addr
))
2396 * Validate interface index if provided. If no interface index
2397 * was provided separately, attempt to look the membership up
2398 * from the default scope as a last resort to disambiguate
2399 * the membership we are being asked to leave.
2400 * XXX SCOPE6 lock potentially taken here.
2403 ifnet_head_lock_shared();
2404 if ((u_int
)if_index
< ifindex
) {
2406 return (EADDRNOTAVAIL
);
2408 ifp
= ifindex2ifnet
[ifindex
];
2411 return (EADDRNOTAVAIL
);
2412 (void) in6_setscope(&gsa
->sin6
.sin6_addr
, ifp
, NULL
);
2414 error
= sa6_embedscope(&gsa
->sin6
, ip6_use_defzone
);
2416 return (EADDRNOTAVAIL
);
2418 * Some badly behaved applications don't pass an ifindex
2419 * or a scope ID, which is an API violation. In this case,
2420 * perform a lookup as per a v6 join.
2422 * XXX For now, stomp on zone ID for the corner case.
2423 * This is not the 'KAME way', but we need to see the ifp
2424 * directly until such time as this implementation is
2425 * refactored, assuming the scope IDs are the way to go.
2427 ifindex
= ntohs(gsa
->sin6
.sin6_addr
.s6_addr16
[1]);
2429 MLD_PRINTF(("%s: warning: no ifindex, looking up "
2430 "ifp for group %s.\n", __func__
,
2431 ip6_sprintf(&gsa
->sin6
.sin6_addr
)));
2432 ifp
= in6p_lookup_mcast_ifp(inp
, &gsa
->sin6
);
2434 ifnet_head_lock_shared();
2435 ifp
= ifindex2ifnet
[ifindex
];
2439 return (EADDRNOTAVAIL
);
2442 VERIFY(ifp
!= NULL
);
2443 MLD_PRINTF(("%s: ifp = %p\n", __func__
, ifp
));
2446 * Find the membership in the membership array.
2448 imo
= in6p_findmoptions(inp
);
2453 idx
= im6o_match_group(imo
, ifp
, &gsa
->sa
);
2454 if (idx
== (size_t)-1) {
2455 error
= EADDRNOTAVAIL
;
2458 inm
= imo
->im6o_membership
[idx
];
2459 imf
= &imo
->im6o_mfilters
[idx
];
2461 if (ssa
->ss
.ss_family
!= AF_UNSPEC
)
2465 * Begin state merge transaction at socket layer.
2469 * If we were instructed only to leave a given source, do so.
2470 * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
2475 if (imf
->im6f_st
[0] == MCAST_EXCLUDE
) {
2476 error
= EADDRNOTAVAIL
;
2479 ims
= im6o_match_source(imo
, idx
, &ssa
->sa
);
2481 MLD_PRINTF(("%s: source %p %spresent\n", __func__
,
2482 ip6_sprintf(&ssa
->sin6
.sin6_addr
),
2484 error
= EADDRNOTAVAIL
;
2487 MLD_PRINTF(("%s: %s source\n", __func__
, "block"));
2488 error
= im6f_prune(imf
, &ssa
->sin6
);
2490 MLD_PRINTF(("%s: merge imf state failed\n",
2497 * Begin state merge transaction at MLD layer.
2502 * Give up the multicast address record to which
2503 * the membership points. Reference held in im6o
2504 * will be released below.
2506 (void) in6_mc_leave(inm
, imf
);
2508 MLD_PRINTF(("%s: merge inm state\n", __func__
));
2510 error
= in6m_merge(inm
, imf
);
2512 MLD_PRINTF(("%s: failed to merge inm state\n",
2515 goto out_im6f_rollback
;
2518 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
2519 error
= mld_change_state(inm
, 0);
2521 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
2535 /* Remove the gap in the membership array. */
2536 VERIFY(inm
== imo
->im6o_membership
[idx
]);
2537 imo
->im6o_membership
[idx
] = NULL
;
2539 for (++idx
; idx
< imo
->im6o_num_memberships
; ++idx
) {
2540 imo
->im6o_membership
[idx
-1] = imo
->im6o_membership
[idx
];
2541 imo
->im6o_mfilters
[idx
-1] = imo
->im6o_mfilters
[idx
];
2543 imo
->im6o_num_memberships
--;
2548 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2553 * Select the interface for transmitting IPv6 multicast datagrams.
2555 * Either an instance of struct in6_addr or an instance of struct ipv6_mreqn
2556 * may be passed to this socket option. An address of in6addr_any or an
2557 * interface index of 0 is used to remove a previous selection.
2558 * When no interface is selected, one is chosen for every send.
2561 in6p_set_multicast_if(struct inpcb
*inp
, struct sockopt
*sopt
)
2564 struct ip6_moptions
*imo
;
2568 if (sopt
->sopt_valsize
!= sizeof(u_int
))
2571 error
= sooptcopyin(sopt
, &ifindex
, sizeof(u_int
), sizeof(u_int
));
2575 ifnet_head_lock_shared();
2576 if ((u_int
)if_index
< ifindex
) {
2581 ifp
= ifindex2ifnet
[ifindex
];
2583 if (ifp
== NULL
|| (ifp
->if_flags
& IFF_MULTICAST
) == 0)
2584 return (EADDRNOTAVAIL
);
2586 imo
= in6p_findmoptions(inp
);
2591 imo
->im6o_multicast_ifp
= ifp
;
2593 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2599 * Atomically set source filters on a socket for an IPv6 multicast group.
2603 in6p_set_source_filters(struct inpcb
*inp
, struct sockopt
*sopt
)
2605 struct __msfilterreq64 msfr
, msfr64
;
2606 struct __msfilterreq32 msfr32
;
2609 struct in6_mfilter
*imf
;
2610 struct ip6_moptions
*imo
;
2611 struct in6_multi
*inm
;
2614 user_addr_t tmp_ptr
;
2616 if (IS_64BIT_PROCESS(current_proc())) {
2617 error
= sooptcopyin(sopt
, &msfr64
,
2618 sizeof(struct __msfilterreq64
),
2619 sizeof(struct __msfilterreq64
));
2622 /* we never use msfr.msfr_srcs; */
2623 memcpy(&msfr
, &msfr64
, sizeof(msfr
));
2625 error
= sooptcopyin(sopt
, &msfr32
,
2626 sizeof(struct __msfilterreq32
),
2627 sizeof(struct __msfilterreq32
));
2630 /* we never use msfr.msfr_srcs; */
2631 memcpy(&msfr
, &msfr32
, sizeof(msfr
));
2634 if ((size_t) msfr
.msfr_nsrcs
>
2635 SIZE_MAX
/ sizeof(struct sockaddr_storage
))
2636 msfr
.msfr_nsrcs
= SIZE_MAX
/ sizeof(struct sockaddr_storage
);
2638 if (msfr
.msfr_nsrcs
> in6_mcast_maxsocksrc
)
2641 if (msfr
.msfr_fmode
!= MCAST_EXCLUDE
&&
2642 msfr
.msfr_fmode
!= MCAST_INCLUDE
)
2645 if (msfr
.msfr_group
.ss_family
!= AF_INET6
||
2646 msfr
.msfr_group
.ss_len
!= sizeof(struct sockaddr_in6
))
2649 gsa
= (sockunion_t
*)&msfr
.msfr_group
;
2650 if (!IN6_IS_ADDR_MULTICAST(&gsa
->sin6
.sin6_addr
))
2653 gsa
->sin6
.sin6_port
= 0; /* ignore port */
2655 ifnet_head_lock_shared();
2656 if (msfr
.msfr_ifindex
== 0 || (u_int
)if_index
< msfr
.msfr_ifindex
) {
2658 return (EADDRNOTAVAIL
);
2660 ifp
= ifindex2ifnet
[msfr
.msfr_ifindex
];
2663 return (EADDRNOTAVAIL
);
2665 (void)in6_setscope(&gsa
->sin6
.sin6_addr
, ifp
, NULL
);
2668 * Take the INP write lock.
2669 * Check if this socket is a member of this group.
2671 imo
= in6p_findmoptions(inp
);
2676 idx
= im6o_match_group(imo
, ifp
, &gsa
->sa
);
2677 if (idx
== (size_t)-1 || imo
->im6o_mfilters
== NULL
) {
2678 error
= EADDRNOTAVAIL
;
2679 goto out_imo_locked
;
2681 inm
= imo
->im6o_membership
[idx
];
2682 imf
= &imo
->im6o_mfilters
[idx
];
2685 * Begin state merge transaction at socket layer.
2688 imf
->im6f_st
[1] = msfr
.msfr_fmode
;
2691 * Apply any new source filters, if present.
2692 * Make a copy of the user-space source vector so
2693 * that we may copy them with a single copyin. This
2694 * allows us to deal with page faults up-front.
2696 if (msfr
.msfr_nsrcs
> 0) {
2697 struct in6_msource
*lims
;
2698 struct sockaddr_in6
*psin
;
2699 struct sockaddr_storage
*kss
, *pkss
;
2702 if (IS_64BIT_PROCESS(current_proc()))
2703 tmp_ptr
= msfr64
.msfr_srcs
;
2705 tmp_ptr
= CAST_USER_ADDR_T(msfr32
.msfr_srcs
);
2707 MLD_PRINTF(("%s: loading %lu source list entries\n",
2708 __func__
, (unsigned long)msfr
.msfr_nsrcs
));
2709 kss
= _MALLOC((size_t) msfr
.msfr_nsrcs
* sizeof(*kss
),
2713 goto out_imo_locked
;
2716 error
= copyin(tmp_ptr
, kss
,
2717 (size_t) msfr
.msfr_nsrcs
* sizeof(*kss
));
2720 goto out_imo_locked
;
2724 * Mark all source filters as UNDEFINED at t1.
2725 * Restore new group filter mode, as im6f_leave()
2726 * will set it to INCLUDE.
2729 imf
->im6f_st
[1] = msfr
.msfr_fmode
;
2732 * Update socket layer filters at t1, lazy-allocating
2733 * new entries. This saves a bunch of memory at the
2734 * cost of one RB_FIND() per source entry; duplicate
2735 * entries in the msfr_nsrcs vector are ignored.
2736 * If we encounter an error, rollback transaction.
2738 * XXX This too could be replaced with a set-symmetric
2739 * difference like loop to avoid walking from root
2740 * every time, as the key space is common.
2742 for (i
= 0, pkss
= kss
; i
< msfr
.msfr_nsrcs
; i
++, pkss
++) {
2743 psin
= (struct sockaddr_in6
*)pkss
;
2744 if (psin
->sin6_family
!= AF_INET6
) {
2745 error
= EAFNOSUPPORT
;
2748 if (psin
->sin6_len
!= sizeof(struct sockaddr_in6
)) {
2752 if (IN6_IS_ADDR_MULTICAST(&psin
->sin6_addr
)) {
2757 * TODO: Validate embedded scope ID in source
2758 * list entry against passed-in ifp, if and only
2759 * if source list filter entry is iface or node local.
2761 in6_clearscope(&psin
->sin6_addr
);
2762 error
= im6f_get_source(imf
, psin
, &lims
);
2765 lims
->im6sl_st
[1] = imf
->im6f_st
[1];
2771 goto out_im6f_rollback
;
2774 * Begin state merge transaction at MLD layer.
2777 MLD_PRINTF(("%s: merge inm state\n", __func__
));
2778 error
= in6m_merge(inm
, imf
);
2780 MLD_PRINTF(("%s: failed to merge inm state\n", __func__
));
2782 goto out_im6f_rollback
;
2785 MLD_PRINTF(("%s: doing mld downcall\n", __func__
));
2786 error
= mld_change_state(inm
, 0);
2790 MLD_PRINTF(("%s: failed mld downcall\n", __func__
));
2803 IM6O_REMREF(imo
); /* from in6p_findmoptions() */
2809 * Set the IP multicast options in response to user setsockopt().
2811 * Many of the socket options handled in this function duplicate the
2812 * functionality of socket options in the regular unicast API. However,
2813 * it is not possible to merge the duplicate code, because the idempotence
2814 * of the IPv6 multicast part of the BSD Sockets API must be preserved;
2815 * the effects of these options must be treated as separate and distinct.
2819 ip6_setmoptions(struct inpcb
*inp
, struct sockopt
*sopt
)
2821 struct ip6_moptions
*im6o
;
2827 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
2828 * or is a divert socket, reject it.
2830 if (inp
->inp_socket
->so_proto
->pr_protocol
== IPPROTO_DIVERT
||
2831 (inp
->inp_socket
->so_proto
->pr_type
!= SOCK_RAW
&&
2832 inp
->inp_socket
->so_proto
->pr_type
!= SOCK_DGRAM
))
2833 return (EOPNOTSUPP
);
2835 switch (sopt
->sopt_name
) {
2836 case IPV6_MULTICAST_IF
:
2837 error
= in6p_set_multicast_if(inp
, sopt
);
2840 case IPV6_MULTICAST_HOPS
: {
2843 if (sopt
->sopt_valsize
!= sizeof(int)) {
2847 error
= sooptcopyin(sopt
, &hlim
, sizeof(hlim
), sizeof(int));
2850 if (hlim
< -1 || hlim
> 255) {
2853 } else if (hlim
== -1) {
2854 hlim
= ip6_defmcasthlim
;
2856 im6o
= in6p_findmoptions(inp
);
2862 im6o
->im6o_multicast_hlim
= hlim
;
2864 IM6O_REMREF(im6o
); /* from in6p_findmoptions() */
2868 case IPV6_MULTICAST_LOOP
: {
2872 * Set the loopback flag for outgoing multicast packets.
2873 * Must be zero or one.
2875 if (sopt
->sopt_valsize
!= sizeof(u_int
)) {
2879 error
= sooptcopyin(sopt
, &loop
, sizeof(u_int
), sizeof(u_int
));
2886 im6o
= in6p_findmoptions(inp
);
2892 im6o
->im6o_multicast_loop
= loop
;
2894 IM6O_REMREF(im6o
); /* from in6p_findmoptions() */
2898 case IPV6_JOIN_GROUP
:
2899 case MCAST_JOIN_GROUP
:
2900 case MCAST_JOIN_SOURCE_GROUP
:
2901 error
= in6p_join_group(inp
, sopt
);
2904 case IPV6_LEAVE_GROUP
:
2905 case MCAST_LEAVE_GROUP
:
2906 case MCAST_LEAVE_SOURCE_GROUP
:
2907 error
= in6p_leave_group(inp
, sopt
);
2910 case MCAST_BLOCK_SOURCE
:
2911 case MCAST_UNBLOCK_SOURCE
:
2912 error
= in6p_block_unblock_source(inp
, sopt
);
2916 error
= in6p_set_source_filters(inp
, sopt
);
2927 * Expose MLD's multicast filter mode and source list(s) to userland,
2928 * keyed by (ifindex, group).
2929 * The filter mode is written out as a uint32_t, followed by
2930 * 0..n of struct in6_addr.
2931 * For use by ifmcstat(8).
2934 sysctl_ip6_mcast_filters SYSCTL_HANDLER_ARGS
2936 #pragma unused(oidp)
2938 struct in6_addr mcaddr
;
2939 struct in6_addr src
;
2941 struct in6_multi
*inm
;
2942 struct in6_multistep step
;
2943 struct ip6_msource
*ims
;
2947 uint32_t fmode
, ifindex
;
2952 if (req
->newptr
!= USER_ADDR_NULL
)
2955 /* int: ifindex + 4 * 32 bits of IPv6 address */
2960 ifnet_head_lock_shared();
2961 if (ifindex
<= 0 || ifindex
> (u_int
)if_index
) {
2962 MLD_PRINTF(("%s: ifindex %u out of range\n",
2963 __func__
, ifindex
));
2968 memcpy(&mcaddr
, &name
[1], sizeof(struct in6_addr
));
2969 if (!IN6_IS_ADDR_MULTICAST(&mcaddr
)) {
2970 MLD_PRINTF(("%s: group %s is not multicast\n",
2971 __func__
, ip6_sprintf(&mcaddr
)));
2976 ifp
= ifindex2ifnet
[ifindex
];
2979 MLD_PRINTF(("%s: no ifp for ifindex %u\n", __func__
, ifindex
));
2983 * Internal MLD lookups require that scope/zone ID is set.
2985 (void)in6_setscope(&mcaddr
, ifp
, NULL
);
2987 in6_multihead_lock_shared();
2988 IN6_FIRST_MULTI(step
, inm
);
2989 while (inm
!= NULL
) {
2991 if (inm
->in6m_ifp
!= ifp
)
2994 if (!IN6_ARE_ADDR_EQUAL(&inm
->in6m_addr
, &mcaddr
))
2997 fmode
= inm
->in6m_st
[1].iss_fmode
;
2998 retval
= SYSCTL_OUT(req
, &fmode
, sizeof(uint32_t));
3003 RB_FOREACH(ims
, ip6_msource_tree
, &inm
->in6m_srcs
) {
3004 MLD_PRINTF(("%s: visit node %p\n", __func__
, ims
));
3006 * Only copy-out sources which are in-mode.
3008 if (fmode
!= im6s_get_mode(inm
, ims
, 1)) {
3009 MLD_PRINTF(("%s: skip non-in-mode\n",
3011 continue; /* process next source */
3013 src
= ims
->im6s_addr
;
3014 retval
= SYSCTL_OUT(req
, &src
, sizeof(struct in6_addr
));
3016 break; /* process next inm */
3020 IN6_NEXT_MULTI(step
, inm
);
3022 in6_multihead_lock_done();
3028 in6_multi_init(void)
3030 PE_parse_boot_argn("ifa_debug", &in6m_debug
, sizeof (in6m_debug
));
3032 /* Setup lock group and attribute for in6_multihead */
3033 in6_multihead_lock_grp_attr
= lck_grp_attr_alloc_init();
3034 in6_multihead_lock_grp
= lck_grp_alloc_init("in6_multihead",
3035 in6_multihead_lock_grp_attr
);
3036 in6_multihead_lock_attr
= lck_attr_alloc_init();
3037 lck_rw_init(&in6_multihead_lock
, in6_multihead_lock_grp
,
3038 in6_multihead_lock_attr
);
3040 lck_mtx_init(&in6m_trash_lock
, in6_multihead_lock_grp
,
3041 in6_multihead_lock_attr
);
3042 TAILQ_INIT(&in6m_trash_head
);
3044 in6m_size
= (in6m_debug
== 0) ? sizeof (struct in6_multi
) :
3045 sizeof (struct in6_multi_dbg
);
3046 in6m_zone
= zinit(in6m_size
, IN6M_ZONE_MAX
* in6m_size
,
3048 if (in6m_zone
== NULL
) {
3049 panic("%s: failed allocating %s", __func__
, IN6M_ZONE_NAME
);
3052 zone_change(in6m_zone
, Z_EXPAND
, TRUE
);
3054 imm_size
= sizeof (struct in6_multi_mship
);
3055 imm_zone
= zinit(imm_size
, IMM_ZONE_MAX
* imm_size
, 0, IMM_ZONE_NAME
);
3056 if (imm_zone
== NULL
) {
3057 panic("%s: failed allocating %s", __func__
, IMM_ZONE_NAME
);
3060 zone_change(imm_zone
, Z_EXPAND
, TRUE
);
3062 ip6ms_size
= sizeof (struct ip6_msource
);
3063 ip6ms_zone
= zinit(ip6ms_size
, IP6MS_ZONE_MAX
* ip6ms_size
,
3064 0, IP6MS_ZONE_NAME
);
3065 if (ip6ms_zone
== NULL
) {
3066 panic("%s: failed allocating %s", __func__
, IP6MS_ZONE_NAME
);
3069 zone_change(ip6ms_zone
, Z_EXPAND
, TRUE
);
3071 in6ms_size
= sizeof (struct in6_msource
);
3072 in6ms_zone
= zinit(in6ms_size
, IN6MS_ZONE_MAX
* in6ms_size
,
3073 0, IN6MS_ZONE_NAME
);
3074 if (in6ms_zone
== NULL
) {
3075 panic("%s: failed allocating %s", __func__
, IN6MS_ZONE_NAME
);
3078 zone_change(in6ms_zone
, Z_EXPAND
, TRUE
);
3081 static struct in6_multi
*
3082 in6_multi_alloc(int how
)
3084 struct in6_multi
*in6m
;
3086 in6m
= (how
== M_WAITOK
) ? zalloc(in6m_zone
) :
3087 zalloc_noblock(in6m_zone
);
3089 bzero(in6m
, in6m_size
);
3090 lck_mtx_init(&in6m
->in6m_lock
, in6_multihead_lock_grp
,
3091 in6_multihead_lock_attr
);
3092 in6m
->in6m_debug
|= IFD_ALLOC
;
3093 if (in6m_debug
!= 0) {
3094 in6m
->in6m_debug
|= IFD_DEBUG
;
3095 in6m
->in6m_trace
= in6m_trace
;
3102 in6_multi_free(struct in6_multi
*in6m
)
3105 if (in6m
->in6m_debug
& IFD_ATTACHED
) {
3106 panic("%s: attached in6m=%p is being freed", __func__
, in6m
);
3108 } else if (in6m
->in6m_ifma
!= NULL
) {
3109 panic("%s: ifma not NULL for in6m=%p", __func__
, in6m
);
3111 } else if (!(in6m
->in6m_debug
& IFD_ALLOC
)) {
3112 panic("%s: in6m %p cannot be freed", __func__
, in6m
);
3114 } else if (in6m
->in6m_refcount
!= 0) {
3115 panic("%s: non-zero refcount in6m=%p", __func__
, in6m
);
3117 } else if (in6m
->in6m_reqcnt
!= 0) {
3118 panic("%s: non-zero reqcnt in6m=%p", __func__
, in6m
);
3122 /* Free any pending MLDv2 state-change records */
3123 IF_DRAIN(&in6m
->in6m_scq
);
3125 in6m
->in6m_debug
&= ~IFD_ALLOC
;
3126 if ((in6m
->in6m_debug
& (IFD_DEBUG
| IFD_TRASHED
)) ==
3127 (IFD_DEBUG
| IFD_TRASHED
)) {
3128 lck_mtx_lock(&in6m_trash_lock
);
3129 TAILQ_REMOVE(&in6m_trash_head
, (struct in6_multi_dbg
*)in6m
,
3131 lck_mtx_unlock(&in6m_trash_lock
);
3132 in6m
->in6m_debug
&= ~IFD_TRASHED
;
3136 lck_mtx_destroy(&in6m
->in6m_lock
, in6_multihead_lock_grp
);
3137 zfree(in6m_zone
, in6m
);
3141 in6_multi_attach(struct in6_multi
*in6m
)
3143 in6_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE
);
3144 IN6M_LOCK_ASSERT_HELD(in6m
);
3146 if (in6m
->in6m_debug
& IFD_ATTACHED
) {
3147 panic("%s: Attempt to attach an already attached in6m=%p",
3150 } else if (in6m
->in6m_debug
& IFD_TRASHED
) {
3151 panic("%s: Attempt to reattach a detached in6m=%p",
3156 in6m
->in6m_reqcnt
++;
3157 VERIFY(in6m
->in6m_reqcnt
== 1);
3158 IN6M_ADDREF_LOCKED(in6m
);
3159 in6m
->in6m_debug
|= IFD_ATTACHED
;
3161 * Reattach case: If debugging is enabled, take it
3162 * out of the trash list and clear IFD_TRASHED.
3164 if ((in6m
->in6m_debug
& (IFD_DEBUG
| IFD_TRASHED
)) ==
3165 (IFD_DEBUG
| IFD_TRASHED
)) {
3166 /* Become a regular mutex, just in case */
3167 IN6M_CONVERT_LOCK(in6m
);
3168 lck_mtx_lock(&in6m_trash_lock
);
3169 TAILQ_REMOVE(&in6m_trash_head
, (struct in6_multi_dbg
*)in6m
,
3171 lck_mtx_unlock(&in6m_trash_lock
);
3172 in6m
->in6m_debug
&= ~IFD_TRASHED
;
3175 LIST_INSERT_HEAD(&in6_multihead
, in6m
, in6m_entry
);
3179 in6_multi_detach(struct in6_multi
*in6m
)
3181 in6_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE
);
3182 IN6M_LOCK_ASSERT_HELD(in6m
);
3184 if (in6m
->in6m_reqcnt
== 0) {
3185 panic("%s: in6m=%p negative reqcnt", __func__
, in6m
);
3189 --in6m
->in6m_reqcnt
;
3190 if (in6m
->in6m_reqcnt
> 0)
3193 if (!(in6m
->in6m_debug
& IFD_ATTACHED
)) {
3194 panic("%s: Attempt to detach an unattached record in6m=%p",
3197 } else if (in6m
->in6m_debug
& IFD_TRASHED
) {
3198 panic("%s: in6m %p is already in trash list", __func__
, in6m
);
3203 * NOTE: Caller calls IFMA_REMREF
3205 in6m
->in6m_debug
&= ~IFD_ATTACHED
;
3206 LIST_REMOVE(in6m
, in6m_entry
);
3208 if (in6m
->in6m_debug
& IFD_DEBUG
) {
3209 /* Become a regular mutex, just in case */
3210 IN6M_CONVERT_LOCK(in6m
);
3211 lck_mtx_lock(&in6m_trash_lock
);
3212 TAILQ_INSERT_TAIL(&in6m_trash_head
,
3213 (struct in6_multi_dbg
*)in6m
, in6m_trash_link
);
3214 lck_mtx_unlock(&in6m_trash_lock
);
3215 in6m
->in6m_debug
|= IFD_TRASHED
;
3222 in6m_addref(struct in6_multi
*in6m
, int locked
)
3225 IN6M_LOCK_SPIN(in6m
);
3227 IN6M_LOCK_ASSERT_HELD(in6m
);
3229 if (++in6m
->in6m_refcount
== 0) {
3230 panic("%s: in6m=%p wraparound refcnt", __func__
, in6m
);
3232 } else if (in6m
->in6m_trace
!= NULL
) {
3233 (*in6m
->in6m_trace
)(in6m
, TRUE
);
3240 in6m_remref(struct in6_multi
*in6m
, int locked
)
3242 struct ifmultiaddr
*ifma
;
3243 struct mld_ifinfo
*mli
;
3246 IN6M_LOCK_SPIN(in6m
);
3248 IN6M_LOCK_ASSERT_HELD(in6m
);
3250 if (in6m
->in6m_refcount
== 0 || (in6m
->in6m_refcount
== 1 && locked
)) {
3251 panic("%s: in6m=%p negative refcnt", __func__
, in6m
);
3253 } else if (in6m
->in6m_trace
!= NULL
) {
3254 (*in6m
->in6m_trace
)(in6m
, FALSE
);
3257 --in6m
->in6m_refcount
;
3258 if (in6m
->in6m_refcount
> 0) {
3265 * Synchronization with in6_mc_get(). In the event the in6m has been
3266 * detached, the underlying ifma would still be in the if_multiaddrs
3267 * list, and thus can be looked up via if_addmulti(). At that point,
3268 * the only way to find this in6m is via ifma_protospec. To avoid
3269 * race conditions between the last in6m_remref() of that in6m and its
3270 * use via ifma_protospec, in6_multihead lock is used for serialization.
3271 * In order to avoid violating the lock order, we must drop in6m_lock
3272 * before acquiring in6_multihead lock. To prevent the in6m from being
3273 * freed prematurely, we hold an extra reference.
3275 ++in6m
->in6m_refcount
;
3277 in6_multihead_lock_shared();
3278 IN6M_LOCK_SPIN(in6m
);
3279 --in6m
->in6m_refcount
;
3280 if (in6m
->in6m_refcount
> 0) {
3281 /* We've lost the race, so abort since in6m is still in use */
3283 in6_multihead_lock_done();
3284 /* If it was locked, return it as such */
3290 ifma
= in6m
->in6m_ifma
;
3291 in6m
->in6m_ifma
= NULL
;
3292 in6m
->in6m_ifp
= NULL
;
3293 mli
= in6m
->in6m_mli
;
3294 in6m
->in6m_mli
= NULL
;
3296 IFMA_LOCK_SPIN(ifma
);
3297 ifma
->ifma_protospec
= NULL
;
3299 in6_multihead_lock_done();
3301 in6_multi_free(in6m
);
3302 if_delmulti_ifma(ifma
);
3303 /* Release reference held to the underlying ifmultiaddr */
3311 in6m_trace(struct in6_multi
*in6m
, int refhold
)
3313 struct in6_multi_dbg
*in6m_dbg
= (struct in6_multi_dbg
*)in6m
;
3318 if (!(in6m
->in6m_debug
& IFD_DEBUG
)) {
3319 panic("%s: in6m %p has no debug structure", __func__
, in6m
);
3323 cnt
= &in6m_dbg
->in6m_refhold_cnt
;
3324 tr
= in6m_dbg
->in6m_refhold
;
3326 cnt
= &in6m_dbg
->in6m_refrele_cnt
;
3327 tr
= in6m_dbg
->in6m_refrele
;
3330 idx
= atomic_add_16_ov(cnt
, 1) % IN6M_TRACE_HIST_SIZE
;
3331 ctrace_record(&tr
[idx
]);
3334 static struct in6_multi_mship
*
3335 in6_multi_mship_alloc(int how
)
3337 struct in6_multi_mship
*imm
;
3339 imm
= (how
== M_WAITOK
) ? zalloc(imm_zone
) : zalloc_noblock(imm_zone
);
3341 bzero(imm
, imm_size
);
3347 in6_multi_mship_free(struct in6_multi_mship
*imm
)
3349 if (imm
->i6mm_maddr
!= NULL
) {
3350 panic("%s: i6mm_maddr not NULL for imm=%p", __func__
, imm
);
3353 zfree(imm_zone
, imm
);
3357 in6_multihead_lock_exclusive(void)
3359 lck_rw_lock_exclusive(&in6_multihead_lock
);
3363 in6_multihead_lock_shared(void)
3365 lck_rw_lock_shared(&in6_multihead_lock
);
3369 in6_multihead_lock_assert(int what
)
3371 lck_rw_assert(&in6_multihead_lock
, what
);
3375 in6_multihead_lock_done(void)
3377 lck_rw_done(&in6_multihead_lock
);
3380 static struct ip6_msource
*
3381 ip6ms_alloc(int how
)
3383 struct ip6_msource
*i6ms
;
3385 i6ms
= (how
== M_WAITOK
) ? zalloc(ip6ms_zone
) :
3386 zalloc_noblock(ip6ms_zone
);
3388 bzero(i6ms
, ip6ms_size
);
3394 ip6ms_free(struct ip6_msource
*i6ms
)
3396 zfree(ip6ms_zone
, i6ms
);
3399 static struct in6_msource
*
3400 in6ms_alloc(int how
)
3402 struct in6_msource
*in6ms
;
3404 in6ms
= (how
== M_WAITOK
) ? zalloc(in6ms_zone
) :
3405 zalloc_noblock(in6ms_zone
);
3407 bzero(in6ms
, in6ms_size
);
3413 in6ms_free(struct in6_msource
*in6ms
)
3415 zfree(in6ms_zone
, in6ms
);
3420 static const char *in6m_modestrs
[] = { "un\n", "in", "ex" };
3423 in6m_mode_str(const int mode
)
3425 if (mode
>= MCAST_UNDEFINED
&& mode
<= MCAST_EXCLUDE
)
3426 return (in6m_modestrs
[mode
]);
3430 static const char *in6m_statestrs
[] = {
3438 "sg-query-pending\n",
3443 in6m_state_str(const int state
)
3445 if (state
>= MLD_NOT_MEMBER
&& state
<= MLD_LEAVING_MEMBER
)
3446 return (in6m_statestrs
[state
]);
3451 * Dump an in6_multi structure to the console.
3454 in6m_print(const struct in6_multi
*inm
)
3458 IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm
));
3463 printf("%s: --- begin in6m %p ---\n", __func__
, inm
);
3464 printf("addr %s ifp %p(%s%d) ifma %p\n",
3465 ip6_sprintf(&inm
->in6m_addr
),
3467 inm
->in6m_ifp
->if_name
,
3468 inm
->in6m_ifp
->if_unit
,
3470 printf("timer %u state %s refcount %u scq.len %u\n",
3472 in6m_state_str(inm
->in6m_state
),
3474 inm
->in6m_scq
.ifq_len
);
3475 printf("mli %p nsrc %lu sctimer %u scrv %u\n",
3480 for (t
= 0; t
< 2; t
++) {
3481 printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t
,
3482 in6m_mode_str(inm
->in6m_st
[t
].iss_fmode
),
3483 inm
->in6m_st
[t
].iss_asm
,
3484 inm
->in6m_st
[t
].iss_ex
,
3485 inm
->in6m_st
[t
].iss_in
,
3486 inm
->in6m_st
[t
].iss_rec
);
3488 printf("%s: --- end in6m %p ---\n", __func__
, inm
);
3494 in6m_print(__unused
const struct in6_multi
*inm
)