xref: /xnu-11417.140.69/bsd/netinet6/mld6.c (revision 43a90889846e00bfb5cf1d255cdc0a701a1e05a4)
1*43a90889SApple OSS Distributions /*
2*43a90889SApple OSS Distributions  * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3*43a90889SApple OSS Distributions  *
4*43a90889SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*43a90889SApple OSS Distributions  *
6*43a90889SApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*43a90889SApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*43a90889SApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*43a90889SApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*43a90889SApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*43a90889SApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*43a90889SApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*43a90889SApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*43a90889SApple OSS Distributions  *
15*43a90889SApple OSS Distributions  * Please obtain a copy of the License at
16*43a90889SApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*43a90889SApple OSS Distributions  *
18*43a90889SApple OSS Distributions  * The Original Code and all software distributed under the License are
19*43a90889SApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*43a90889SApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*43a90889SApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*43a90889SApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*43a90889SApple OSS Distributions  * Please see the License for the specific language governing rights and
24*43a90889SApple OSS Distributions  * limitations under the License.
25*43a90889SApple OSS Distributions  *
26*43a90889SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*43a90889SApple OSS Distributions  */
28*43a90889SApple OSS Distributions /*-
29*43a90889SApple OSS Distributions  * Copyright (c) 2009 Bruce Simpson.
30*43a90889SApple OSS Distributions  *
31*43a90889SApple OSS Distributions  * Redistribution and use in source and binary forms, with or without
32*43a90889SApple OSS Distributions  * modification, are permitted provided that the following conditions
33*43a90889SApple OSS Distributions  * are met:
34*43a90889SApple OSS Distributions  * 1. Redistributions of source code must retain the above copyright
35*43a90889SApple OSS Distributions  *    notice, this list of conditions and the following disclaimer.
36*43a90889SApple OSS Distributions  * 2. Redistributions in binary form must reproduce the above copyright
37*43a90889SApple OSS Distributions  *    notice, this list of conditions and the following disclaimer in the
38*43a90889SApple OSS Distributions  *    documentation and/or other materials provided with the distribution.
39*43a90889SApple OSS Distributions  * 3. The name of the author may not be used to endorse or promote
40*43a90889SApple OSS Distributions  *    products derived from this software without specific prior written
41*43a90889SApple OSS Distributions  *    permission.
42*43a90889SApple OSS Distributions  *
43*43a90889SApple OSS Distributions  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
44*43a90889SApple OSS Distributions  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45*43a90889SApple OSS Distributions  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46*43a90889SApple OSS Distributions  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
47*43a90889SApple OSS Distributions  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48*43a90889SApple OSS Distributions  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49*43a90889SApple OSS Distributions  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50*43a90889SApple OSS Distributions  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51*43a90889SApple OSS Distributions  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52*43a90889SApple OSS Distributions  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53*43a90889SApple OSS Distributions  * SUCH DAMAGE.
54*43a90889SApple OSS Distributions  */
55*43a90889SApple OSS Distributions 
56*43a90889SApple OSS Distributions /*
57*43a90889SApple OSS Distributions  * Copyright (c) 1988 Stephen Deering.
58*43a90889SApple OSS Distributions  * Copyright (c) 1992, 1993
59*43a90889SApple OSS Distributions  *	The Regents of the University of California.  All rights reserved.
60*43a90889SApple OSS Distributions  *
61*43a90889SApple OSS Distributions  * This code is derived from software contributed to Berkeley by
62*43a90889SApple OSS Distributions  * Stephen Deering of Stanford University.
63*43a90889SApple OSS Distributions  *
64*43a90889SApple OSS Distributions  * Redistribution and use in source and binary forms, with or without
65*43a90889SApple OSS Distributions  * modification, are permitted provided that the following conditions
66*43a90889SApple OSS Distributions  * are met:
67*43a90889SApple OSS Distributions  * 1. Redistributions of source code must retain the above copyright
68*43a90889SApple OSS Distributions  *    notice, this list of conditions and the following disclaimer.
69*43a90889SApple OSS Distributions  * 2. Redistributions in binary form must reproduce the above copyright
70*43a90889SApple OSS Distributions  *    notice, this list of conditions and the following disclaimer in the
71*43a90889SApple OSS Distributions  *    documentation and/or other materials provided with the distribution.
72*43a90889SApple OSS Distributions  * 3. All advertising materials mentioning features or use of this software
73*43a90889SApple OSS Distributions  *    must display the following acknowledgement:
74*43a90889SApple OSS Distributions  *	This product includes software developed by the University of
75*43a90889SApple OSS Distributions  *	California, Berkeley and its contributors.
76*43a90889SApple OSS Distributions  * 4. Neither the name of the University nor the names of its contributors
77*43a90889SApple OSS Distributions  *    may be used to endorse or promote products derived from this software
78*43a90889SApple OSS Distributions  *    without specific prior written permission.
79*43a90889SApple OSS Distributions  *
80*43a90889SApple OSS Distributions  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
81*43a90889SApple OSS Distributions  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
82*43a90889SApple OSS Distributions  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
83*43a90889SApple OSS Distributions  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
84*43a90889SApple OSS Distributions  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
85*43a90889SApple OSS Distributions  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
86*43a90889SApple OSS Distributions  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
87*43a90889SApple OSS Distributions  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
88*43a90889SApple OSS Distributions  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
89*43a90889SApple OSS Distributions  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
90*43a90889SApple OSS Distributions  * SUCH DAMAGE.
91*43a90889SApple OSS Distributions  *
92*43a90889SApple OSS Distributions  *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
93*43a90889SApple OSS Distributions  */
94*43a90889SApple OSS Distributions /*
95*43a90889SApple OSS Distributions  * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
96*43a90889SApple OSS Distributions  * support for mandatory and extensible security protections.  This notice
97*43a90889SApple OSS Distributions  * is included in support of clause 2.2 (b) of the Apple Public License,
98*43a90889SApple OSS Distributions  * Version 2.0.
99*43a90889SApple OSS Distributions  */
100*43a90889SApple OSS Distributions 
101*43a90889SApple OSS Distributions #include <sys/cdefs.h>
102*43a90889SApple OSS Distributions 
103*43a90889SApple OSS Distributions #include <sys/param.h>
104*43a90889SApple OSS Distributions #include <sys/systm.h>
105*43a90889SApple OSS Distributions #include <sys/mbuf.h>
106*43a90889SApple OSS Distributions #include <sys/socket.h>
107*43a90889SApple OSS Distributions #include <sys/protosw.h>
108*43a90889SApple OSS Distributions #include <sys/kernel.h>
109*43a90889SApple OSS Distributions #include <sys/malloc.h>
110*43a90889SApple OSS Distributions #include <sys/mcache.h>
111*43a90889SApple OSS Distributions 
112*43a90889SApple OSS Distributions #include <dev/random/randomdev.h>
113*43a90889SApple OSS Distributions 
114*43a90889SApple OSS Distributions #include <kern/zalloc.h>
115*43a90889SApple OSS Distributions 
116*43a90889SApple OSS Distributions #include <net/if.h>
117*43a90889SApple OSS Distributions #include <net/route.h>
118*43a90889SApple OSS Distributions #include <net/net_sysctl.h>
119*43a90889SApple OSS Distributions 
120*43a90889SApple OSS Distributions #include <netinet/in.h>
121*43a90889SApple OSS Distributions #include <netinet/in_var.h>
122*43a90889SApple OSS Distributions #include <netinet6/in6_var.h>
123*43a90889SApple OSS Distributions #include <netinet/ip6.h>
124*43a90889SApple OSS Distributions #include <netinet6/ip6_var.h>
125*43a90889SApple OSS Distributions #include <netinet6/scope6_var.h>
126*43a90889SApple OSS Distributions #include <netinet/icmp6.h>
127*43a90889SApple OSS Distributions #include <netinet6/mld6.h>
128*43a90889SApple OSS Distributions #include <netinet6/mld6_var.h>
129*43a90889SApple OSS Distributions 
130*43a90889SApple OSS Distributions #include <os/log.h>
131*43a90889SApple OSS Distributions 
132*43a90889SApple OSS Distributions /* Lock group and attribute for mld_mtx */
133*43a90889SApple OSS Distributions static LCK_ATTR_DECLARE(mld_mtx_attr, 0, 0);
134*43a90889SApple OSS Distributions static LCK_GRP_DECLARE(mld_mtx_grp, "mld_mtx");
135*43a90889SApple OSS Distributions 
136*43a90889SApple OSS Distributions /*
137*43a90889SApple OSS Distributions  * Locking and reference counting:
138*43a90889SApple OSS Distributions  *
139*43a90889SApple OSS Distributions  * mld_mtx mainly protects mli_head.  In cases where both mld_mtx and
140*43a90889SApple OSS Distributions  * in6_multihead_lock must be held, the former must be acquired first in order
141*43a90889SApple OSS Distributions  * to maintain lock ordering.  It is not a requirement that mld_mtx be
142*43a90889SApple OSS Distributions  * acquired first before in6_multihead_lock, but in case both must be acquired
143*43a90889SApple OSS Distributions  * in succession, the correct lock ordering must be followed.
144*43a90889SApple OSS Distributions  *
145*43a90889SApple OSS Distributions  * Instead of walking the if_multiaddrs list at the interface and returning
146*43a90889SApple OSS Distributions  * the ifma_protospec value of a matching entry, we search the global list
147*43a90889SApple OSS Distributions  * of in6_multi records and find it that way; this is done with in6_multihead
148*43a90889SApple OSS Distributions  * lock held.  Doing so avoids the race condition issues that many other BSDs
149*43a90889SApple OSS Distributions  * suffer from (therefore in our implementation, ifma_protospec will never be
150*43a90889SApple OSS Distributions  * NULL for as long as the in6_multi is valid.)
151*43a90889SApple OSS Distributions  *
152*43a90889SApple OSS Distributions  * The above creates a requirement for the in6_multi to stay in in6_multihead
153*43a90889SApple OSS Distributions  * list even after the final MLD leave (in MLDv2 mode) until no longer needs
154*43a90889SApple OSS Distributions  * be retransmitted (this is not required for MLDv1.)  In order to handle
155*43a90889SApple OSS Distributions  * this, the request and reference counts of the in6_multi are bumped up when
156*43a90889SApple OSS Distributions  * the state changes to MLD_LEAVING_MEMBER, and later dropped in the timeout
157*43a90889SApple OSS Distributions  * handler.  Each in6_multi holds a reference to the underlying mld_ifinfo.
158*43a90889SApple OSS Distributions  *
159*43a90889SApple OSS Distributions  * Thus, the permitted lock order is:
160*43a90889SApple OSS Distributions  *
161*43a90889SApple OSS Distributions  *	mld_mtx, in6_multihead_lock, inm6_lock, mli_lock
162*43a90889SApple OSS Distributions  *
163*43a90889SApple OSS Distributions  * Any may be taken independently, but if any are held at the same time,
164*43a90889SApple OSS Distributions  * the above lock order must be followed.
165*43a90889SApple OSS Distributions  */
166*43a90889SApple OSS Distributions static LCK_MTX_DECLARE_ATTR(mld_mtx, &mld_mtx_grp, &mld_mtx_attr);
167*43a90889SApple OSS Distributions 
168*43a90889SApple OSS Distributions SLIST_HEAD(mld_in6m_relhead, in6_multi);
169*43a90889SApple OSS Distributions 
170*43a90889SApple OSS Distributions static void     mli_initvar(struct mld_ifinfo *, struct ifnet *, int);
171*43a90889SApple OSS Distributions static struct mld_ifinfo *mli_alloc(zalloc_flags_t);
172*43a90889SApple OSS Distributions static void     mli_free(struct mld_ifinfo *);
173*43a90889SApple OSS Distributions static void     mli_delete(const struct ifnet *, struct mld_in6m_relhead *);
174*43a90889SApple OSS Distributions static void     mld_dispatch_packet(struct mbuf *);
175*43a90889SApple OSS Distributions static void     mld_final_leave(struct in6_multi *, struct mld_ifinfo *,
176*43a90889SApple OSS Distributions     struct mld_tparams *);
177*43a90889SApple OSS Distributions static int      mld_handle_state_change(struct in6_multi *, struct mld_ifinfo *,
178*43a90889SApple OSS Distributions     struct mld_tparams *);
179*43a90889SApple OSS Distributions static int      mld_initial_join(struct in6_multi *, struct mld_ifinfo *,
180*43a90889SApple OSS Distributions     struct mld_tparams *, const int);
181*43a90889SApple OSS Distributions #ifdef MLD_DEBUG
182*43a90889SApple OSS Distributions static const char *     mld_rec_type_to_str(const int);
183*43a90889SApple OSS Distributions #endif
184*43a90889SApple OSS Distributions static uint32_t mld_set_version(struct mld_ifinfo *, const int);
185*43a90889SApple OSS Distributions static void     mld_append_relq(struct mld_ifinfo *, struct in6_multi *);
186*43a90889SApple OSS Distributions static void     mld_flush_relq(struct mld_ifinfo *, struct mld_in6m_relhead *);
187*43a90889SApple OSS Distributions static void     mld_dispatch_queue_locked(struct mld_ifinfo *, struct ifqueue *, int);
188*43a90889SApple OSS Distributions static int      mld_v1_input_query(struct ifnet *, const struct ip6_hdr *,
189*43a90889SApple OSS Distributions     /*const*/ struct mld_hdr *);
190*43a90889SApple OSS Distributions static int      mld_v1_input_report(struct ifnet *, struct mbuf *,
191*43a90889SApple OSS Distributions     const struct ip6_hdr *, /*const*/ struct mld_hdr *);
192*43a90889SApple OSS Distributions static void     mld_v1_process_group_timer(struct in6_multi *, const int);
193*43a90889SApple OSS Distributions static void     mld_v1_process_querier_timers(struct mld_ifinfo *);
194*43a90889SApple OSS Distributions static int      mld_v1_transmit_report(struct in6_multi *, const uint8_t);
195*43a90889SApple OSS Distributions static uint32_t mld_v1_update_group(struct in6_multi *, const int);
196*43a90889SApple OSS Distributions static void     mld_v2_cancel_link_timers(struct mld_ifinfo *);
197*43a90889SApple OSS Distributions static uint32_t mld_v2_dispatch_general_query(struct mld_ifinfo *);
198*43a90889SApple OSS Distributions static struct mbuf *
199*43a90889SApple OSS Distributions mld_v2_encap_report(struct ifnet *, struct mbuf *);
200*43a90889SApple OSS Distributions static int      mld_v2_enqueue_filter_change(struct ifqueue *,
201*43a90889SApple OSS Distributions     struct in6_multi *);
202*43a90889SApple OSS Distributions static int      mld_v2_enqueue_group_record(struct ifqueue *,
203*43a90889SApple OSS Distributions     struct in6_multi *, const int, const int, const int,
204*43a90889SApple OSS Distributions     const int);
205*43a90889SApple OSS Distributions static int      mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
206*43a90889SApple OSS Distributions     struct mbuf *, const int, const int);
207*43a90889SApple OSS Distributions static int      mld_v2_merge_state_changes(struct in6_multi *,
208*43a90889SApple OSS Distributions     struct ifqueue *);
209*43a90889SApple OSS Distributions static void     mld_v2_process_group_timers(struct mld_ifinfo *,
210*43a90889SApple OSS Distributions     struct ifqueue *, struct ifqueue *,
211*43a90889SApple OSS Distributions     struct in6_multi *, const int);
212*43a90889SApple OSS Distributions static int      mld_v2_process_group_query(struct in6_multi *,
213*43a90889SApple OSS Distributions     int, struct mbuf *, const int);
214*43a90889SApple OSS Distributions static int      sysctl_mld_gsr SYSCTL_HANDLER_ARGS;
215*43a90889SApple OSS Distributions static int      sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS;
216*43a90889SApple OSS Distributions static int      sysctl_mld_v2enable SYSCTL_HANDLER_ARGS;
217*43a90889SApple OSS Distributions 
218*43a90889SApple OSS Distributions static const uint32_t mld_timeout_delay = 1000; /* in milliseconds */
219*43a90889SApple OSS Distributions static const uint32_t mld_timeout_leeway = 500; /* in millseconds  */
220*43a90889SApple OSS Distributions static bool mld_timeout_run;             /* MLD timer is scheduled to run */
221*43a90889SApple OSS Distributions static bool mld_fast_timeout_run;        /* MLD fast timer is scheduled to run */
222*43a90889SApple OSS Distributions static void mld_timeout(thread_call_param_t, thread_call_param_t);
223*43a90889SApple OSS Distributions static void mld_sched_timeout(void);
224*43a90889SApple OSS Distributions static void mld_sched_fast_timeout(void);
225*43a90889SApple OSS Distributions 
226*43a90889SApple OSS Distributions /*
227*43a90889SApple OSS Distributions  * Normative references: RFC 2710, RFC 3590, RFC 3810.
228*43a90889SApple OSS Distributions  */
229*43a90889SApple OSS Distributions static struct timeval mld_gsrdelay = {.tv_sec = 10, .tv_usec = 0};
230*43a90889SApple OSS Distributions static LIST_HEAD(, mld_ifinfo) mli_head;
231*43a90889SApple OSS Distributions 
232*43a90889SApple OSS Distributions static int querier_present_timers_running6;
233*43a90889SApple OSS Distributions static int interface_timers_running6;
234*43a90889SApple OSS Distributions static int state_change_timers_running6;
235*43a90889SApple OSS Distributions static int current_state_timers_running6;
236*43a90889SApple OSS Distributions 
237*43a90889SApple OSS Distributions static unsigned int mld_mli_list_genid;
238*43a90889SApple OSS Distributions /*
239*43a90889SApple OSS Distributions  * Subsystem lock macros.
240*43a90889SApple OSS Distributions  */
241*43a90889SApple OSS Distributions #define MLD_LOCK()                      \
242*43a90889SApple OSS Distributions 	lck_mtx_lock(&mld_mtx)
243*43a90889SApple OSS Distributions #define MLD_LOCK_ASSERT_HELD()          \
244*43a90889SApple OSS Distributions 	LCK_MTX_ASSERT(&mld_mtx, LCK_MTX_ASSERT_OWNED)
245*43a90889SApple OSS Distributions #define MLD_LOCK_ASSERT_NOTHELD()       \
246*43a90889SApple OSS Distributions 	LCK_MTX_ASSERT(&mld_mtx, LCK_MTX_ASSERT_NOTOWNED)
247*43a90889SApple OSS Distributions #define MLD_UNLOCK()                    \
248*43a90889SApple OSS Distributions 	lck_mtx_unlock(&mld_mtx)
249*43a90889SApple OSS Distributions 
250*43a90889SApple OSS Distributions #define MLD_ADD_DETACHED_IN6M(_head, _in6m) {                           \
251*43a90889SApple OSS Distributions 	SLIST_INSERT_HEAD(_head, _in6m, in6m_dtle);                     \
252*43a90889SApple OSS Distributions }
253*43a90889SApple OSS Distributions 
254*43a90889SApple OSS Distributions #define MLD_REMOVE_DETACHED_IN6M(_head) {                               \
255*43a90889SApple OSS Distributions 	struct in6_multi *_in6m, *_inm_tmp;                             \
256*43a90889SApple OSS Distributions 	SLIST_FOREACH_SAFE(_in6m, _head, in6m_dtle, _inm_tmp) {         \
257*43a90889SApple OSS Distributions 	        SLIST_REMOVE(_head, _in6m, in6_multi, in6m_dtle);       \
258*43a90889SApple OSS Distributions 	        IN6M_REMREF(_in6m);                                     \
259*43a90889SApple OSS Distributions 	}                                                               \
260*43a90889SApple OSS Distributions 	VERIFY(SLIST_EMPTY(_head));                                     \
261*43a90889SApple OSS Distributions }
262*43a90889SApple OSS Distributions 
263*43a90889SApple OSS Distributions static KALLOC_TYPE_DEFINE(mli_zone, struct mld_ifinfo, NET_KT_DEFAULT);
264*43a90889SApple OSS Distributions 
265*43a90889SApple OSS Distributions SYSCTL_DECL(_net_inet6);        /* Note: Not in any common header. */
266*43a90889SApple OSS Distributions 
267*43a90889SApple OSS Distributions SYSCTL_NODE(_net_inet6, OID_AUTO, mld, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
268*43a90889SApple OSS Distributions     "IPv6 Multicast Listener Discovery");
269*43a90889SApple OSS Distributions SYSCTL_PROC(_net_inet6_mld, OID_AUTO, gsrdelay,
270*43a90889SApple OSS Distributions     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
271*43a90889SApple OSS Distributions     &mld_gsrdelay.tv_sec, 0, sysctl_mld_gsr, "I",
272*43a90889SApple OSS Distributions     "Rate limit for MLDv2 Group-and-Source queries in seconds");
273*43a90889SApple OSS Distributions 
274*43a90889SApple OSS Distributions SYSCTL_NODE(_net_inet6_mld, OID_AUTO, ifinfo, CTLFLAG_RD | CTLFLAG_LOCKED,
275*43a90889SApple OSS Distributions     sysctl_mld_ifinfo, "Per-interface MLDv2 state");
276*43a90889SApple OSS Distributions 
277*43a90889SApple OSS Distributions static int      mld_v1enable = 1;
278*43a90889SApple OSS Distributions SYSCTL_INT(_net_inet6_mld, OID_AUTO, v1enable, CTLFLAG_RW | CTLFLAG_LOCKED,
279*43a90889SApple OSS Distributions     &mld_v1enable, 0, "Enable fallback to MLDv1");
280*43a90889SApple OSS Distributions 
281*43a90889SApple OSS Distributions static int      mld_v2enable = 1;
282*43a90889SApple OSS Distributions SYSCTL_PROC(_net_inet6_mld, OID_AUTO, v2enable,
283*43a90889SApple OSS Distributions     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
284*43a90889SApple OSS Distributions     &mld_v2enable, 0, sysctl_mld_v2enable, "I",
285*43a90889SApple OSS Distributions     "Enable MLDv2 (debug purposes only)");
286*43a90889SApple OSS Distributions 
287*43a90889SApple OSS Distributions static int      mld_use_allow = 1;
288*43a90889SApple OSS Distributions SYSCTL_INT(_net_inet6_mld, OID_AUTO, use_allow, CTLFLAG_RW | CTLFLAG_LOCKED,
289*43a90889SApple OSS Distributions     &mld_use_allow, 0, "Use ALLOW/BLOCK for RFC 4604 SSM joins/leaves");
290*43a90889SApple OSS Distributions 
291*43a90889SApple OSS Distributions #ifdef MLD_DEBUG
292*43a90889SApple OSS Distributions int mld_debug = 0;
293*43a90889SApple OSS Distributions SYSCTL_INT(_net_inet6_mld, OID_AUTO,
294*43a90889SApple OSS Distributions     debug, CTLFLAG_RW | CTLFLAG_LOCKED, &mld_debug, 0, "");
295*43a90889SApple OSS Distributions #endif
296*43a90889SApple OSS Distributions /*
297*43a90889SApple OSS Distributions  * Packed Router Alert option structure declaration.
298*43a90889SApple OSS Distributions  */
299*43a90889SApple OSS Distributions struct mld_raopt {
300*43a90889SApple OSS Distributions 	struct ip6_hbh          hbh;
301*43a90889SApple OSS Distributions 	struct ip6_opt          pad;
302*43a90889SApple OSS Distributions 	struct ip6_opt_router   ra;
303*43a90889SApple OSS Distributions } __packed;
304*43a90889SApple OSS Distributions 
305*43a90889SApple OSS Distributions /*
306*43a90889SApple OSS Distributions  * Router Alert hop-by-hop option header.
307*43a90889SApple OSS Distributions  */
308*43a90889SApple OSS Distributions static struct mld_raopt mld_ra = {
309*43a90889SApple OSS Distributions 	.hbh = { .ip6h_nxt = 0, .ip6h_len = 0 },
310*43a90889SApple OSS Distributions 	.pad = { .ip6o_type = IP6OPT_PADN, .ip6o_len = 0 },
311*43a90889SApple OSS Distributions 	.ra = {
312*43a90889SApple OSS Distributions 		.ip6or_type = (u_int8_t)IP6OPT_ROUTER_ALERT,
313*43a90889SApple OSS Distributions 		.ip6or_len = (u_int8_t)(IP6OPT_RTALERT_LEN - 2),
314*43a90889SApple OSS Distributions 		.ip6or_value =  {((IP6OPT_RTALERT_MLD >> 8) & 0xFF),
315*43a90889SApple OSS Distributions 			         (IP6OPT_RTALERT_MLD & 0xFF) }
316*43a90889SApple OSS Distributions 	}
317*43a90889SApple OSS Distributions };
318*43a90889SApple OSS Distributions static struct ip6_pktopts mld_po;
319*43a90889SApple OSS Distributions 
320*43a90889SApple OSS Distributions /* Store MLDv2 record count in the module private scratch space */
321*43a90889SApple OSS Distributions #define vt_nrecs        pkt_mpriv.__mpriv_u.__mpriv32[0].__mpriv32_u.__val16[0]
322*43a90889SApple OSS Distributions 
323*43a90889SApple OSS Distributions static __inline void
mld_save_context(struct mbuf * m,struct ifnet * ifp)324*43a90889SApple OSS Distributions mld_save_context(struct mbuf *m, struct ifnet *ifp)
325*43a90889SApple OSS Distributions {
326*43a90889SApple OSS Distributions 	m->m_pkthdr.rcvif = ifp;
327*43a90889SApple OSS Distributions }
328*43a90889SApple OSS Distributions 
329*43a90889SApple OSS Distributions static __inline void
mld_scrub_context(struct mbuf * m)330*43a90889SApple OSS Distributions mld_scrub_context(struct mbuf *m)
331*43a90889SApple OSS Distributions {
332*43a90889SApple OSS Distributions 	m->m_pkthdr.rcvif = NULL;
333*43a90889SApple OSS Distributions }
334*43a90889SApple OSS Distributions 
335*43a90889SApple OSS Distributions /*
336*43a90889SApple OSS Distributions  * Restore context from a queued output chain.
337*43a90889SApple OSS Distributions  * Return saved ifp.
338*43a90889SApple OSS Distributions  */
339*43a90889SApple OSS Distributions static __inline struct ifnet *
mld_restore_context(struct mbuf * m)340*43a90889SApple OSS Distributions mld_restore_context(struct mbuf *m)
341*43a90889SApple OSS Distributions {
342*43a90889SApple OSS Distributions 	return m->m_pkthdr.rcvif;
343*43a90889SApple OSS Distributions }
344*43a90889SApple OSS Distributions 
345*43a90889SApple OSS Distributions /*
346*43a90889SApple OSS Distributions  * Retrieve or set threshold between group-source queries in seconds.
347*43a90889SApple OSS Distributions  */
348*43a90889SApple OSS Distributions static int
349*43a90889SApple OSS Distributions sysctl_mld_gsr SYSCTL_HANDLER_ARGS
350*43a90889SApple OSS Distributions {
351*43a90889SApple OSS Distributions #pragma unused(arg1, arg2)
352*43a90889SApple OSS Distributions 	int error;
353*43a90889SApple OSS Distributions 	int i;
354*43a90889SApple OSS Distributions 
355*43a90889SApple OSS Distributions 	MLD_LOCK();
356*43a90889SApple OSS Distributions 
357*43a90889SApple OSS Distributions 	i = (int)mld_gsrdelay.tv_sec;
358*43a90889SApple OSS Distributions 
359*43a90889SApple OSS Distributions 	error = sysctl_handle_int(oidp, &i, 0, req);
360*43a90889SApple OSS Distributions 	if (error || !req->newptr) {
361*43a90889SApple OSS Distributions 		goto out_locked;
362*43a90889SApple OSS Distributions 	}
363*43a90889SApple OSS Distributions 
364*43a90889SApple OSS Distributions 	if (i < -1 || i >= 60) {
365*43a90889SApple OSS Distributions 		error = EINVAL;
366*43a90889SApple OSS Distributions 		goto out_locked;
367*43a90889SApple OSS Distributions 	}
368*43a90889SApple OSS Distributions 
369*43a90889SApple OSS Distributions 	mld_gsrdelay.tv_sec = i;
370*43a90889SApple OSS Distributions 
371*43a90889SApple OSS Distributions out_locked:
372*43a90889SApple OSS Distributions 	MLD_UNLOCK();
373*43a90889SApple OSS Distributions 	return error;
374*43a90889SApple OSS Distributions }
375*43a90889SApple OSS Distributions /*
376*43a90889SApple OSS Distributions  * Expose struct mld_ifinfo to userland, keyed by ifindex.
377*43a90889SApple OSS Distributions  * For use by ifmcstat(8).
378*43a90889SApple OSS Distributions  *
379*43a90889SApple OSS Distributions  */
380*43a90889SApple OSS Distributions static int
381*43a90889SApple OSS Distributions sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS
382*43a90889SApple OSS Distributions {
383*43a90889SApple OSS Distributions #pragma unused(oidp)
384*43a90889SApple OSS Distributions 	DECLARE_SYSCTL_HANDLER_ARG_ARRAY(int, 1, name, namelen);
385*43a90889SApple OSS Distributions 	int                      error;
386*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
387*43a90889SApple OSS Distributions 	struct mld_ifinfo       *mli;
388*43a90889SApple OSS Distributions 	struct mld_ifinfo_u     mli_u;
389*43a90889SApple OSS Distributions 
390*43a90889SApple OSS Distributions 	if (req->newptr != USER_ADDR_NULL) {
391*43a90889SApple OSS Distributions 		return EPERM;
392*43a90889SApple OSS Distributions 	}
393*43a90889SApple OSS Distributions 
394*43a90889SApple OSS Distributions 	MLD_LOCK();
395*43a90889SApple OSS Distributions 
396*43a90889SApple OSS Distributions 	if (name[0] <= 0 || name[0] > (u_int)if_index) {
397*43a90889SApple OSS Distributions 		error = ENOENT;
398*43a90889SApple OSS Distributions 		goto out_locked;
399*43a90889SApple OSS Distributions 	}
400*43a90889SApple OSS Distributions 
401*43a90889SApple OSS Distributions 	error = ENOENT;
402*43a90889SApple OSS Distributions 
403*43a90889SApple OSS Distributions 	ifnet_head_lock_shared();
404*43a90889SApple OSS Distributions 	ifp = ifindex2ifnet[name[0]];
405*43a90889SApple OSS Distributions 	ifnet_head_done();
406*43a90889SApple OSS Distributions 	if (ifp == NULL) {
407*43a90889SApple OSS Distributions 		goto out_locked;
408*43a90889SApple OSS Distributions 	}
409*43a90889SApple OSS Distributions 
410*43a90889SApple OSS Distributions 	bzero(&mli_u, sizeof(mli_u));
411*43a90889SApple OSS Distributions 
412*43a90889SApple OSS Distributions 	LIST_FOREACH(mli, &mli_head, mli_link) {
413*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
414*43a90889SApple OSS Distributions 		if (ifp != mli->mli_ifp) {
415*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
416*43a90889SApple OSS Distributions 			continue;
417*43a90889SApple OSS Distributions 		}
418*43a90889SApple OSS Distributions 
419*43a90889SApple OSS Distributions 		mli_u.mli_ifindex = mli->mli_ifp->if_index;
420*43a90889SApple OSS Distributions 		mli_u.mli_version = mli->mli_version;
421*43a90889SApple OSS Distributions 		mli_u.mli_v1_timer = mli->mli_v1_timer;
422*43a90889SApple OSS Distributions 		mli_u.mli_v2_timer = mli->mli_v2_timer;
423*43a90889SApple OSS Distributions 		mli_u.mli_flags = mli->mli_flags;
424*43a90889SApple OSS Distributions 		mli_u.mli_rv = mli->mli_rv;
425*43a90889SApple OSS Distributions 		mli_u.mli_qi = mli->mli_qi;
426*43a90889SApple OSS Distributions 		mli_u.mli_qri = mli->mli_qri;
427*43a90889SApple OSS Distributions 		mli_u.mli_uri = mli->mli_uri;
428*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
429*43a90889SApple OSS Distributions 
430*43a90889SApple OSS Distributions 		error = SYSCTL_OUT(req, &mli_u, sizeof(mli_u));
431*43a90889SApple OSS Distributions 		break;
432*43a90889SApple OSS Distributions 	}
433*43a90889SApple OSS Distributions 
434*43a90889SApple OSS Distributions out_locked:
435*43a90889SApple OSS Distributions 	MLD_UNLOCK();
436*43a90889SApple OSS Distributions 	return error;
437*43a90889SApple OSS Distributions }
438*43a90889SApple OSS Distributions 
439*43a90889SApple OSS Distributions static int
440*43a90889SApple OSS Distributions sysctl_mld_v2enable SYSCTL_HANDLER_ARGS
441*43a90889SApple OSS Distributions {
442*43a90889SApple OSS Distributions #pragma unused(arg1, arg2)
443*43a90889SApple OSS Distributions 	int error;
444*43a90889SApple OSS Distributions 	int i;
445*43a90889SApple OSS Distributions 	struct mld_ifinfo *mli;
446*43a90889SApple OSS Distributions 	struct mld_tparams mtp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
447*43a90889SApple OSS Distributions 
448*43a90889SApple OSS Distributions 	MLD_LOCK();
449*43a90889SApple OSS Distributions 
450*43a90889SApple OSS Distributions 	i = mld_v2enable;
451*43a90889SApple OSS Distributions 
452*43a90889SApple OSS Distributions 	error = sysctl_handle_int(oidp, &i, 0, req);
453*43a90889SApple OSS Distributions 	if (error || !req->newptr) {
454*43a90889SApple OSS Distributions 		goto out_locked;
455*43a90889SApple OSS Distributions 	}
456*43a90889SApple OSS Distributions 
457*43a90889SApple OSS Distributions 	if (i < 0 || i > 1) {
458*43a90889SApple OSS Distributions 		error = EINVAL;
459*43a90889SApple OSS Distributions 		goto out_locked;
460*43a90889SApple OSS Distributions 	}
461*43a90889SApple OSS Distributions 
462*43a90889SApple OSS Distributions 	mld_v2enable = i;
463*43a90889SApple OSS Distributions 	/*
464*43a90889SApple OSS Distributions 	 * If we enabled v2, the state transition will take care of upgrading
465*43a90889SApple OSS Distributions 	 * the MLD version back to v2. Otherwise, we have to explicitly
466*43a90889SApple OSS Distributions 	 * downgrade. Note that this functionality is to be used for debugging.
467*43a90889SApple OSS Distributions 	 */
468*43a90889SApple OSS Distributions 	if (mld_v2enable == 1) {
469*43a90889SApple OSS Distributions 		goto out_locked;
470*43a90889SApple OSS Distributions 	}
471*43a90889SApple OSS Distributions 
472*43a90889SApple OSS Distributions 	LIST_FOREACH(mli, &mli_head, mli_link) {
473*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
474*43a90889SApple OSS Distributions 		if (mld_set_version(mli, MLD_VERSION_1) > 0) {
475*43a90889SApple OSS Distributions 			mtp.qpt = 1;
476*43a90889SApple OSS Distributions 		}
477*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
478*43a90889SApple OSS Distributions 	}
479*43a90889SApple OSS Distributions 
480*43a90889SApple OSS Distributions out_locked:
481*43a90889SApple OSS Distributions 	MLD_UNLOCK();
482*43a90889SApple OSS Distributions 
483*43a90889SApple OSS Distributions 	mld_set_timeout(&mtp);
484*43a90889SApple OSS Distributions 
485*43a90889SApple OSS Distributions 	return error;
486*43a90889SApple OSS Distributions }
487*43a90889SApple OSS Distributions 
488*43a90889SApple OSS Distributions /*
489*43a90889SApple OSS Distributions  * Dispatch an entire queue of pending packet chains.
490*43a90889SApple OSS Distributions  *
491*43a90889SApple OSS Distributions  * Must not be called with in6m_lock held.
492*43a90889SApple OSS Distributions  * XXX This routine unlocks MLD global lock and also mli locks.
493*43a90889SApple OSS Distributions  * Make sure that the calling routine takes reference on the mli
494*43a90889SApple OSS Distributions  * before calling this routine.
495*43a90889SApple OSS Distributions  * Also if we are traversing mli_head, remember to check for
496*43a90889SApple OSS Distributions  * mli list generation count and restart the loop if generation count
497*43a90889SApple OSS Distributions  * has changed.
498*43a90889SApple OSS Distributions  */
499*43a90889SApple OSS Distributions static void
mld_dispatch_queue_locked(struct mld_ifinfo * mli,struct ifqueue * ifq,int limit)500*43a90889SApple OSS Distributions mld_dispatch_queue_locked(struct mld_ifinfo *mli, struct ifqueue *ifq, int limit)
501*43a90889SApple OSS Distributions {
502*43a90889SApple OSS Distributions 	struct mbuf *m;
503*43a90889SApple OSS Distributions 
504*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_HELD();
505*43a90889SApple OSS Distributions 
506*43a90889SApple OSS Distributions 	if (mli != NULL) {
507*43a90889SApple OSS Distributions 		MLI_LOCK_ASSERT_HELD(mli);
508*43a90889SApple OSS Distributions 	}
509*43a90889SApple OSS Distributions 
510*43a90889SApple OSS Distributions 	for (;;) {
511*43a90889SApple OSS Distributions 		IF_DEQUEUE(ifq, m);
512*43a90889SApple OSS Distributions 		if (m == NULL) {
513*43a90889SApple OSS Distributions 			break;
514*43a90889SApple OSS Distributions 		}
515*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: dispatch 0x%llx from 0x%llx\n", __func__,
516*43a90889SApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifq),
517*43a90889SApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(m)));
518*43a90889SApple OSS Distributions 
519*43a90889SApple OSS Distributions 		if (mli != NULL) {
520*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
521*43a90889SApple OSS Distributions 		}
522*43a90889SApple OSS Distributions 		MLD_UNLOCK();
523*43a90889SApple OSS Distributions 
524*43a90889SApple OSS Distributions 		mld_dispatch_packet(m);
525*43a90889SApple OSS Distributions 
526*43a90889SApple OSS Distributions 		MLD_LOCK();
527*43a90889SApple OSS Distributions 		if (mli != NULL) {
528*43a90889SApple OSS Distributions 			MLI_LOCK(mli);
529*43a90889SApple OSS Distributions 		}
530*43a90889SApple OSS Distributions 
531*43a90889SApple OSS Distributions 		if (--limit == 0) {
532*43a90889SApple OSS Distributions 			break;
533*43a90889SApple OSS Distributions 		}
534*43a90889SApple OSS Distributions 	}
535*43a90889SApple OSS Distributions 
536*43a90889SApple OSS Distributions 	if (mli != NULL) {
537*43a90889SApple OSS Distributions 		MLI_LOCK_ASSERT_HELD(mli);
538*43a90889SApple OSS Distributions 	}
539*43a90889SApple OSS Distributions }
540*43a90889SApple OSS Distributions 
541*43a90889SApple OSS Distributions /*
542*43a90889SApple OSS Distributions  * Filter outgoing MLD report state by group.
543*43a90889SApple OSS Distributions  *
544*43a90889SApple OSS Distributions  * Reports are ALWAYS suppressed for ALL-HOSTS (ff02::1)
545*43a90889SApple OSS Distributions  * and node-local addresses. However, kernel and socket consumers
546*43a90889SApple OSS Distributions  * always embed the KAME scope ID in the address provided, so strip it
547*43a90889SApple OSS Distributions  * when performing comparison.
548*43a90889SApple OSS Distributions  * Note: This is not the same as the *multicast* scope.
549*43a90889SApple OSS Distributions  *
550*43a90889SApple OSS Distributions  * Return zero if the given group is one for which MLD reports
551*43a90889SApple OSS Distributions  * should be suppressed, or non-zero if reports should be issued.
552*43a90889SApple OSS Distributions  */
553*43a90889SApple OSS Distributions static __inline__ int
mld_is_addr_reported(const struct in6_addr * addr)554*43a90889SApple OSS Distributions mld_is_addr_reported(const struct in6_addr *addr)
555*43a90889SApple OSS Distributions {
556*43a90889SApple OSS Distributions 	VERIFY(IN6_IS_ADDR_MULTICAST(addr));
557*43a90889SApple OSS Distributions 
558*43a90889SApple OSS Distributions 	if (IPV6_ADDR_MC_SCOPE(addr) == IPV6_ADDR_SCOPE_NODELOCAL) {
559*43a90889SApple OSS Distributions 		return 0;
560*43a90889SApple OSS Distributions 	}
561*43a90889SApple OSS Distributions 
562*43a90889SApple OSS Distributions 	if (IPV6_ADDR_MC_SCOPE(addr) == IPV6_ADDR_SCOPE_LINKLOCAL && !IN6_IS_ADDR_UNICAST_BASED_MULTICAST(addr)) {
563*43a90889SApple OSS Distributions 		struct in6_addr tmp = *addr;
564*43a90889SApple OSS Distributions 		in6_clearscope(&tmp);
565*43a90889SApple OSS Distributions 		if (IN6_ARE_ADDR_EQUAL(&tmp, &in6addr_linklocal_allnodes)) {
566*43a90889SApple OSS Distributions 			return 0;
567*43a90889SApple OSS Distributions 		}
568*43a90889SApple OSS Distributions 	}
569*43a90889SApple OSS Distributions 
570*43a90889SApple OSS Distributions 	return 1;
571*43a90889SApple OSS Distributions }
572*43a90889SApple OSS Distributions 
573*43a90889SApple OSS Distributions /*
574*43a90889SApple OSS Distributions  * Attach MLD when PF_INET6 is attached to an interface.
575*43a90889SApple OSS Distributions  */
576*43a90889SApple OSS Distributions struct mld_ifinfo *
mld_domifattach(struct ifnet * ifp,zalloc_flags_t how)577*43a90889SApple OSS Distributions mld_domifattach(struct ifnet *ifp, zalloc_flags_t how)
578*43a90889SApple OSS Distributions {
579*43a90889SApple OSS Distributions 	struct mld_ifinfo *mli;
580*43a90889SApple OSS Distributions 
581*43a90889SApple OSS Distributions 	os_log_debug(OS_LOG_DEFAULT, "%s: called for ifp %s\n", __func__,
582*43a90889SApple OSS Distributions 	    if_name(ifp));
583*43a90889SApple OSS Distributions 
584*43a90889SApple OSS Distributions 	mli = mli_alloc(how);
585*43a90889SApple OSS Distributions 	if (mli == NULL) {
586*43a90889SApple OSS Distributions 		return NULL;
587*43a90889SApple OSS Distributions 	}
588*43a90889SApple OSS Distributions 
589*43a90889SApple OSS Distributions 	MLD_LOCK();
590*43a90889SApple OSS Distributions 
591*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
592*43a90889SApple OSS Distributions 	mli_initvar(mli, ifp, 0);
593*43a90889SApple OSS Distributions 	mli->mli_debug |= IFD_ATTACHED;
594*43a90889SApple OSS Distributions 	MLI_ADDREF_LOCKED(mli); /* hold a reference for mli_head */
595*43a90889SApple OSS Distributions 	MLI_ADDREF_LOCKED(mli); /* hold a reference for caller */
596*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
597*43a90889SApple OSS Distributions 	ifnet_lock_shared(ifp);
598*43a90889SApple OSS Distributions 	mld6_initsilent(ifp, mli);
599*43a90889SApple OSS Distributions 	ifnet_lock_done(ifp);
600*43a90889SApple OSS Distributions 
601*43a90889SApple OSS Distributions 	LIST_INSERT_HEAD(&mli_head, mli, mli_link);
602*43a90889SApple OSS Distributions 	mld_mli_list_genid++;
603*43a90889SApple OSS Distributions 
604*43a90889SApple OSS Distributions 	MLD_UNLOCK();
605*43a90889SApple OSS Distributions 
606*43a90889SApple OSS Distributions 	os_log_info(OS_LOG_DEFAULT, "%s: allocated mld_ifinfo for ifp %s\n",
607*43a90889SApple OSS Distributions 	    __func__, if_name(ifp));
608*43a90889SApple OSS Distributions 
609*43a90889SApple OSS Distributions 	return mli;
610*43a90889SApple OSS Distributions }
611*43a90889SApple OSS Distributions 
612*43a90889SApple OSS Distributions /*
613*43a90889SApple OSS Distributions  * Attach MLD when PF_INET6 is reattached to an interface.  Caller is
614*43a90889SApple OSS Distributions  * expected to have an outstanding reference to the mli.
615*43a90889SApple OSS Distributions  */
616*43a90889SApple OSS Distributions void
mld_domifreattach(struct mld_ifinfo * mli)617*43a90889SApple OSS Distributions mld_domifreattach(struct mld_ifinfo *mli)
618*43a90889SApple OSS Distributions {
619*43a90889SApple OSS Distributions 	struct ifnet *ifp;
620*43a90889SApple OSS Distributions 
621*43a90889SApple OSS Distributions 	MLD_LOCK();
622*43a90889SApple OSS Distributions 
623*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
624*43a90889SApple OSS Distributions 	VERIFY(!(mli->mli_debug & IFD_ATTACHED));
625*43a90889SApple OSS Distributions 	ifp = mli->mli_ifp;
626*43a90889SApple OSS Distributions 	VERIFY(ifp != NULL);
627*43a90889SApple OSS Distributions 	mli_initvar(mli, ifp, 1);
628*43a90889SApple OSS Distributions 	mli->mli_debug |= IFD_ATTACHED;
629*43a90889SApple OSS Distributions 	MLI_ADDREF_LOCKED(mli); /* hold a reference for mli_head */
630*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
631*43a90889SApple OSS Distributions 	ifnet_lock_shared(ifp);
632*43a90889SApple OSS Distributions 	mld6_initsilent(ifp, mli);
633*43a90889SApple OSS Distributions 	ifnet_lock_done(ifp);
634*43a90889SApple OSS Distributions 
635*43a90889SApple OSS Distributions 	LIST_INSERT_HEAD(&mli_head, mli, mli_link);
636*43a90889SApple OSS Distributions 	mld_mli_list_genid++;
637*43a90889SApple OSS Distributions 
638*43a90889SApple OSS Distributions 	MLD_UNLOCK();
639*43a90889SApple OSS Distributions 
640*43a90889SApple OSS Distributions 	os_log_info(OS_LOG_DEFAULT, "%s: reattached mld_ifinfo for ifp %s\n",
641*43a90889SApple OSS Distributions 	    __func__, if_name(ifp));
642*43a90889SApple OSS Distributions }
643*43a90889SApple OSS Distributions 
644*43a90889SApple OSS Distributions /*
645*43a90889SApple OSS Distributions  * Hook for domifdetach.
646*43a90889SApple OSS Distributions  */
647*43a90889SApple OSS Distributions void
mld_domifdetach(struct ifnet * ifp)648*43a90889SApple OSS Distributions mld_domifdetach(struct ifnet *ifp)
649*43a90889SApple OSS Distributions {
650*43a90889SApple OSS Distributions 	SLIST_HEAD(, in6_multi) in6m_dthead;
651*43a90889SApple OSS Distributions 
652*43a90889SApple OSS Distributions 	SLIST_INIT(&in6m_dthead);
653*43a90889SApple OSS Distributions 
654*43a90889SApple OSS Distributions 	os_log_info(OS_LOG_DEFAULT, "%s: called for ifp %s\n", __func__,
655*43a90889SApple OSS Distributions 	    if_name(ifp));
656*43a90889SApple OSS Distributions 
657*43a90889SApple OSS Distributions 	MLD_LOCK();
658*43a90889SApple OSS Distributions 	mli_delete(ifp, (struct mld_in6m_relhead *)&in6m_dthead);
659*43a90889SApple OSS Distributions 	MLD_UNLOCK();
660*43a90889SApple OSS Distributions 
661*43a90889SApple OSS Distributions 	/* Now that we're dropped all locks, release detached records */
662*43a90889SApple OSS Distributions 	MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
663*43a90889SApple OSS Distributions }
664*43a90889SApple OSS Distributions 
665*43a90889SApple OSS Distributions /*
666*43a90889SApple OSS Distributions  * Called at interface detach time.  Note that we only flush all deferred
667*43a90889SApple OSS Distributions  * responses and record releases; all remaining inm records and their source
668*43a90889SApple OSS Distributions  * entries related to this interface are left intact, in order to handle
669*43a90889SApple OSS Distributions  * the reattach case.
670*43a90889SApple OSS Distributions  */
671*43a90889SApple OSS Distributions static void
mli_delete(const struct ifnet * ifp,struct mld_in6m_relhead * in6m_dthead)672*43a90889SApple OSS Distributions mli_delete(const struct ifnet *ifp, struct mld_in6m_relhead *in6m_dthead)
673*43a90889SApple OSS Distributions {
674*43a90889SApple OSS Distributions 	struct mld_ifinfo *mli, *tmli;
675*43a90889SApple OSS Distributions 
676*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_HELD();
677*43a90889SApple OSS Distributions 
678*43a90889SApple OSS Distributions 	LIST_FOREACH_SAFE(mli, &mli_head, mli_link, tmli) {
679*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
680*43a90889SApple OSS Distributions 		if (mli->mli_ifp == ifp) {
681*43a90889SApple OSS Distributions 			/*
682*43a90889SApple OSS Distributions 			 * Free deferred General Query responses.
683*43a90889SApple OSS Distributions 			 */
684*43a90889SApple OSS Distributions 			IF_DRAIN(&mli->mli_gq);
685*43a90889SApple OSS Distributions 			IF_DRAIN(&mli->mli_v1q);
686*43a90889SApple OSS Distributions 			mld_flush_relq(mli, in6m_dthead);
687*43a90889SApple OSS Distributions 			mli->mli_debug &= ~IFD_ATTACHED;
688*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
689*43a90889SApple OSS Distributions 
690*43a90889SApple OSS Distributions 			LIST_REMOVE(mli, mli_link);
691*43a90889SApple OSS Distributions 			MLI_REMREF(mli); /* release mli_head reference */
692*43a90889SApple OSS Distributions 			mld_mli_list_genid++;
693*43a90889SApple OSS Distributions 			return;
694*43a90889SApple OSS Distributions 		}
695*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
696*43a90889SApple OSS Distributions 	}
697*43a90889SApple OSS Distributions 	panic("%s: mld_ifinfo not found for ifp %p(%s)", __func__,
698*43a90889SApple OSS Distributions 	    ifp, ifp->if_xname);
699*43a90889SApple OSS Distributions }
700*43a90889SApple OSS Distributions 
701*43a90889SApple OSS Distributions __private_extern__ void
mld6_initsilent(struct ifnet * ifp,struct mld_ifinfo * mli)702*43a90889SApple OSS Distributions mld6_initsilent(struct ifnet *ifp, struct mld_ifinfo *mli)
703*43a90889SApple OSS Distributions {
704*43a90889SApple OSS Distributions 	ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_OWNED);
705*43a90889SApple OSS Distributions 
706*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_NOTHELD(mli);
707*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
708*43a90889SApple OSS Distributions 	if (!(ifp->if_flags & IFF_MULTICAST) &&
709*43a90889SApple OSS Distributions 	    (ifp->if_eflags & (IFEF_IPV6_ND6ALT | IFEF_LOCALNET_PRIVATE))) {
710*43a90889SApple OSS Distributions 		mli->mli_flags |= MLIF_SILENT;
711*43a90889SApple OSS Distributions 	} else {
712*43a90889SApple OSS Distributions 		mli->mli_flags &= ~MLIF_SILENT;
713*43a90889SApple OSS Distributions 	}
714*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
715*43a90889SApple OSS Distributions }
716*43a90889SApple OSS Distributions 
717*43a90889SApple OSS Distributions static void
mli_initvar(struct mld_ifinfo * mli,struct ifnet * ifp,int reattach)718*43a90889SApple OSS Distributions mli_initvar(struct mld_ifinfo *mli, struct ifnet *ifp, int reattach)
719*43a90889SApple OSS Distributions {
720*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
721*43a90889SApple OSS Distributions 
722*43a90889SApple OSS Distributions 	mli->mli_ifp = ifp;
723*43a90889SApple OSS Distributions 	if (mld_v2enable) {
724*43a90889SApple OSS Distributions 		mli->mli_version = MLD_VERSION_2;
725*43a90889SApple OSS Distributions 	} else {
726*43a90889SApple OSS Distributions 		mli->mli_version = MLD_VERSION_1;
727*43a90889SApple OSS Distributions 	}
728*43a90889SApple OSS Distributions 	mli->mli_flags = 0;
729*43a90889SApple OSS Distributions 	mli->mli_rv = MLD_RV_INIT;
730*43a90889SApple OSS Distributions 	mli->mli_qi = MLD_QI_INIT;
731*43a90889SApple OSS Distributions 	mli->mli_qri = MLD_QRI_INIT;
732*43a90889SApple OSS Distributions 	mli->mli_uri = MLD_URI_INIT;
733*43a90889SApple OSS Distributions 
734*43a90889SApple OSS Distributions 	if (mld_use_allow) {
735*43a90889SApple OSS Distributions 		mli->mli_flags |= MLIF_USEALLOW;
736*43a90889SApple OSS Distributions 	}
737*43a90889SApple OSS Distributions 	if (!reattach) {
738*43a90889SApple OSS Distributions 		SLIST_INIT(&mli->mli_relinmhead);
739*43a90889SApple OSS Distributions 	}
740*43a90889SApple OSS Distributions 
741*43a90889SApple OSS Distributions 	/*
742*43a90889SApple OSS Distributions 	 * Responses to general queries are subject to bounds.
743*43a90889SApple OSS Distributions 	 */
744*43a90889SApple OSS Distributions 	mli->mli_gq.ifq_maxlen = MLD_MAX_RESPONSE_PACKETS;
745*43a90889SApple OSS Distributions 	mli->mli_v1q.ifq_maxlen = MLD_MAX_RESPONSE_PACKETS;
746*43a90889SApple OSS Distributions }
747*43a90889SApple OSS Distributions 
748*43a90889SApple OSS Distributions static struct mld_ifinfo *
mli_alloc(zalloc_flags_t how)749*43a90889SApple OSS Distributions mli_alloc(zalloc_flags_t how)
750*43a90889SApple OSS Distributions {
751*43a90889SApple OSS Distributions 	struct mld_ifinfo *mli = zalloc_flags(mli_zone, how | Z_ZERO);
752*43a90889SApple OSS Distributions 	if (mli != NULL) {
753*43a90889SApple OSS Distributions 		lck_mtx_init(&mli->mli_lock, &mld_mtx_grp, &mld_mtx_attr);
754*43a90889SApple OSS Distributions 		mli->mli_debug |= IFD_ALLOC;
755*43a90889SApple OSS Distributions 	}
756*43a90889SApple OSS Distributions 	return mli;
757*43a90889SApple OSS Distributions }
758*43a90889SApple OSS Distributions 
759*43a90889SApple OSS Distributions static void
mli_free(struct mld_ifinfo * mli)760*43a90889SApple OSS Distributions mli_free(struct mld_ifinfo *mli)
761*43a90889SApple OSS Distributions {
762*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
763*43a90889SApple OSS Distributions 	if (mli->mli_debug & IFD_ATTACHED) {
764*43a90889SApple OSS Distributions 		panic("%s: attached mli=%p is being freed", __func__, mli);
765*43a90889SApple OSS Distributions 		/* NOTREACHED */
766*43a90889SApple OSS Distributions 	} else if (mli->mli_ifp != NULL) {
767*43a90889SApple OSS Distributions 		panic("%s: ifp not NULL for mli=%p", __func__, mli);
768*43a90889SApple OSS Distributions 		/* NOTREACHED */
769*43a90889SApple OSS Distributions 	} else if (!(mli->mli_debug & IFD_ALLOC)) {
770*43a90889SApple OSS Distributions 		panic("%s: mli %p cannot be freed", __func__, mli);
771*43a90889SApple OSS Distributions 		/* NOTREACHED */
772*43a90889SApple OSS Distributions 	} else if (mli->mli_refcnt != 0) {
773*43a90889SApple OSS Distributions 		panic("%s: non-zero refcnt mli=%p", __func__, mli);
774*43a90889SApple OSS Distributions 		/* NOTREACHED */
775*43a90889SApple OSS Distributions 	}
776*43a90889SApple OSS Distributions 	mli->mli_debug &= ~IFD_ALLOC;
777*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
778*43a90889SApple OSS Distributions 
779*43a90889SApple OSS Distributions 	lck_mtx_destroy(&mli->mli_lock, &mld_mtx_grp);
780*43a90889SApple OSS Distributions 	zfree(mli_zone, mli);
781*43a90889SApple OSS Distributions }
782*43a90889SApple OSS Distributions 
783*43a90889SApple OSS Distributions void
mli_addref(struct mld_ifinfo * mli,int locked)784*43a90889SApple OSS Distributions mli_addref(struct mld_ifinfo *mli, int locked)
785*43a90889SApple OSS Distributions {
786*43a90889SApple OSS Distributions 	if (!locked) {
787*43a90889SApple OSS Distributions 		MLI_LOCK_SPIN(mli);
788*43a90889SApple OSS Distributions 	} else {
789*43a90889SApple OSS Distributions 		MLI_LOCK_ASSERT_HELD(mli);
790*43a90889SApple OSS Distributions 	}
791*43a90889SApple OSS Distributions 
792*43a90889SApple OSS Distributions 	if (++mli->mli_refcnt == 0) {
793*43a90889SApple OSS Distributions 		panic("%s: mli=%p wraparound refcnt", __func__, mli);
794*43a90889SApple OSS Distributions 		/* NOTREACHED */
795*43a90889SApple OSS Distributions 	}
796*43a90889SApple OSS Distributions 	if (!locked) {
797*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
798*43a90889SApple OSS Distributions 	}
799*43a90889SApple OSS Distributions }
800*43a90889SApple OSS Distributions 
801*43a90889SApple OSS Distributions void
mli_remref(struct mld_ifinfo * mli)802*43a90889SApple OSS Distributions mli_remref(struct mld_ifinfo *mli)
803*43a90889SApple OSS Distributions {
804*43a90889SApple OSS Distributions 	SLIST_HEAD(, in6_multi) in6m_dthead;
805*43a90889SApple OSS Distributions 	struct ifnet *ifp;
806*43a90889SApple OSS Distributions 
807*43a90889SApple OSS Distributions 	MLI_LOCK_SPIN(mli);
808*43a90889SApple OSS Distributions 
809*43a90889SApple OSS Distributions 	if (mli->mli_refcnt == 0) {
810*43a90889SApple OSS Distributions 		panic("%s: mli=%p negative refcnt", __func__, mli);
811*43a90889SApple OSS Distributions 		/* NOTREACHED */
812*43a90889SApple OSS Distributions 	}
813*43a90889SApple OSS Distributions 
814*43a90889SApple OSS Distributions 	--mli->mli_refcnt;
815*43a90889SApple OSS Distributions 	if (mli->mli_refcnt > 0) {
816*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
817*43a90889SApple OSS Distributions 		return;
818*43a90889SApple OSS Distributions 	}
819*43a90889SApple OSS Distributions 
820*43a90889SApple OSS Distributions 	ifp = mli->mli_ifp;
821*43a90889SApple OSS Distributions 	mli->mli_ifp = NULL;
822*43a90889SApple OSS Distributions 	IF_DRAIN(&mli->mli_gq);
823*43a90889SApple OSS Distributions 	IF_DRAIN(&mli->mli_v1q);
824*43a90889SApple OSS Distributions 	SLIST_INIT(&in6m_dthead);
825*43a90889SApple OSS Distributions 	mld_flush_relq(mli, (struct mld_in6m_relhead *)&in6m_dthead);
826*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
827*43a90889SApple OSS Distributions 
828*43a90889SApple OSS Distributions 	/* Now that we're dropped all locks, release detached records */
829*43a90889SApple OSS Distributions 	MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
830*43a90889SApple OSS Distributions 
831*43a90889SApple OSS Distributions 	os_log(OS_LOG_DEFAULT, "%s: freeing mld_ifinfo for ifp %s\n",
832*43a90889SApple OSS Distributions 	    __func__, if_name(ifp));
833*43a90889SApple OSS Distributions 
834*43a90889SApple OSS Distributions 	mli_free(mli);
835*43a90889SApple OSS Distributions }
836*43a90889SApple OSS Distributions 
837*43a90889SApple OSS Distributions /*
838*43a90889SApple OSS Distributions  * Process a received MLDv1 general or address-specific query.
839*43a90889SApple OSS Distributions  * Assumes that the query header has been pulled up to sizeof(mld_hdr).
840*43a90889SApple OSS Distributions  *
841*43a90889SApple OSS Distributions  * NOTE: Can't be fully const correct as we temporarily embed scope ID in
842*43a90889SApple OSS Distributions  * mld_addr. This is OK as we own the mbuf chain.
843*43a90889SApple OSS Distributions  */
844*43a90889SApple OSS Distributions static int
mld_v1_input_query(struct ifnet * ifp,const struct ip6_hdr * ip6,struct mld_hdr * mld)845*43a90889SApple OSS Distributions mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
846*43a90889SApple OSS Distributions     /*const*/ struct mld_hdr *mld)
847*43a90889SApple OSS Distributions {
848*43a90889SApple OSS Distributions 	struct mld_ifinfo       *mli;
849*43a90889SApple OSS Distributions 	struct in6_multi        *inm;
850*43a90889SApple OSS Distributions 	int                      err = 0, is_general_query;
851*43a90889SApple OSS Distributions 	uint16_t                 timer;
852*43a90889SApple OSS Distributions 	struct mld_tparams       mtp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
853*43a90889SApple OSS Distributions 
854*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_NOTHELD();
855*43a90889SApple OSS Distributions 
856*43a90889SApple OSS Distributions 	is_general_query = 0;
857*43a90889SApple OSS Distributions 
858*43a90889SApple OSS Distributions 	if (!mld_v1enable) {
859*43a90889SApple OSS Distributions 		os_log_info(OS_LOG_DEFAULT, "%s: ignore v1 query on ifp %s\n",
860*43a90889SApple OSS Distributions 		    __func__, if_name(ifp));
861*43a90889SApple OSS Distributions 		goto done;
862*43a90889SApple OSS Distributions 	}
863*43a90889SApple OSS Distributions 
864*43a90889SApple OSS Distributions 	/*
865*43a90889SApple OSS Distributions 	 * RFC3810 Section 6.2: MLD queries must originate from
866*43a90889SApple OSS Distributions 	 * a router's link-local address.
867*43a90889SApple OSS Distributions 	 */
868*43a90889SApple OSS Distributions 	if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
869*43a90889SApple OSS Distributions 		os_log_info(OS_LOG_DEFAULT, "%s: ignore v1 query src %s on ifp %s\n",
870*43a90889SApple OSS Distributions 		    __func__, ip6_sprintf(&ip6->ip6_src),
871*43a90889SApple OSS Distributions 		    if_name(ifp));
872*43a90889SApple OSS Distributions 		goto done;
873*43a90889SApple OSS Distributions 	}
874*43a90889SApple OSS Distributions 
875*43a90889SApple OSS Distributions 	/*
876*43a90889SApple OSS Distributions 	 * Do address field validation upfront before we accept
877*43a90889SApple OSS Distributions 	 * the query.
878*43a90889SApple OSS Distributions 	 */
879*43a90889SApple OSS Distributions 	if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
880*43a90889SApple OSS Distributions 		/*
881*43a90889SApple OSS Distributions 		 * MLDv1 General Query.
882*43a90889SApple OSS Distributions 		 * If this was not sent to the all-nodes group, ignore it.
883*43a90889SApple OSS Distributions 		 */
884*43a90889SApple OSS Distributions 		struct in6_addr          dst;
885*43a90889SApple OSS Distributions 
886*43a90889SApple OSS Distributions 		dst = ip6->ip6_dst;
887*43a90889SApple OSS Distributions 		in6_clearscope(&dst);
888*43a90889SApple OSS Distributions 		if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes)) {
889*43a90889SApple OSS Distributions 			err = EINVAL;
890*43a90889SApple OSS Distributions 			goto done;
891*43a90889SApple OSS Distributions 		}
892*43a90889SApple OSS Distributions 		is_general_query = 1;
893*43a90889SApple OSS Distributions 	} else {
894*43a90889SApple OSS Distributions 		/*
895*43a90889SApple OSS Distributions 		 * Embed scope ID of receiving interface in MLD query for
896*43a90889SApple OSS Distributions 		 * lookup whilst we don't hold other locks.
897*43a90889SApple OSS Distributions 		 */
898*43a90889SApple OSS Distributions 		(void)in6_setscope(&mld->mld_addr, ifp, NULL);
899*43a90889SApple OSS Distributions 	}
900*43a90889SApple OSS Distributions 
901*43a90889SApple OSS Distributions 	/*
902*43a90889SApple OSS Distributions 	 * Switch to MLDv1 host compatibility mode.
903*43a90889SApple OSS Distributions 	 */
904*43a90889SApple OSS Distributions 	mli = MLD_IFINFO(ifp);
905*43a90889SApple OSS Distributions 	VERIFY(mli != NULL);
906*43a90889SApple OSS Distributions 
907*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
908*43a90889SApple OSS Distributions 	mtp.qpt = mld_set_version(mli, MLD_VERSION_1);
909*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
910*43a90889SApple OSS Distributions 
911*43a90889SApple OSS Distributions 	timer = ntohs(mld->mld_maxdelay) / MLD_TIMER_SCALE;
912*43a90889SApple OSS Distributions 	if (timer == 0) {
913*43a90889SApple OSS Distributions 		timer = 1;
914*43a90889SApple OSS Distributions 	}
915*43a90889SApple OSS Distributions 
916*43a90889SApple OSS Distributions 	if (is_general_query) {
917*43a90889SApple OSS Distributions 		struct in6_multistep step;
918*43a90889SApple OSS Distributions 
919*43a90889SApple OSS Distributions 		os_log_debug(OS_LOG_DEFAULT, "%s: process v1 general query on ifp %s\n",
920*43a90889SApple OSS Distributions 		    __func__, if_name(ifp));
921*43a90889SApple OSS Distributions 		/*
922*43a90889SApple OSS Distributions 		 * For each reporting group joined on this
923*43a90889SApple OSS Distributions 		 * interface, kick the report timer.
924*43a90889SApple OSS Distributions 		 */
925*43a90889SApple OSS Distributions 		in6_multihead_lock_shared();
926*43a90889SApple OSS Distributions 		IN6_FIRST_MULTI(step, inm);
927*43a90889SApple OSS Distributions 		while (inm != NULL) {
928*43a90889SApple OSS Distributions 			IN6M_LOCK(inm);
929*43a90889SApple OSS Distributions 			if (inm->in6m_ifp == ifp) {
930*43a90889SApple OSS Distributions 				mtp.cst += mld_v1_update_group(inm, timer);
931*43a90889SApple OSS Distributions 			}
932*43a90889SApple OSS Distributions 			IN6M_UNLOCK(inm);
933*43a90889SApple OSS Distributions 			IN6_NEXT_MULTI(step, inm);
934*43a90889SApple OSS Distributions 		}
935*43a90889SApple OSS Distributions 		in6_multihead_lock_done();
936*43a90889SApple OSS Distributions 	} else {
937*43a90889SApple OSS Distributions 		/*
938*43a90889SApple OSS Distributions 		 * MLDv1 Group-Specific Query.
939*43a90889SApple OSS Distributions 		 * If this is a group-specific MLDv1 query, we need only
940*43a90889SApple OSS Distributions 		 * look up the single group to process it.
941*43a90889SApple OSS Distributions 		 */
942*43a90889SApple OSS Distributions 		in6_multihead_lock_shared();
943*43a90889SApple OSS Distributions 		IN6_LOOKUP_MULTI(&mld->mld_addr, ifp, inm);
944*43a90889SApple OSS Distributions 		in6_multihead_lock_done();
945*43a90889SApple OSS Distributions 
946*43a90889SApple OSS Distributions 		if (inm != NULL) {
947*43a90889SApple OSS Distributions 			IN6M_LOCK(inm);
948*43a90889SApple OSS Distributions 			os_log_debug(OS_LOG_DEFAULT, "%s: process v1 query %s on "
949*43a90889SApple OSS Distributions 			    "ifp %s\n", __func__,
950*43a90889SApple OSS Distributions 			    ip6_sprintf(&mld->mld_addr),
951*43a90889SApple OSS Distributions 			    if_name(ifp));
952*43a90889SApple OSS Distributions 			mtp.cst = mld_v1_update_group(inm, timer);
953*43a90889SApple OSS Distributions 			IN6M_UNLOCK(inm);
954*43a90889SApple OSS Distributions 			IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
955*43a90889SApple OSS Distributions 		}
956*43a90889SApple OSS Distributions 		/* XXX Clear embedded scope ID as userland won't expect it. */
957*43a90889SApple OSS Distributions 		in6_clearscope(&mld->mld_addr);
958*43a90889SApple OSS Distributions 	}
959*43a90889SApple OSS Distributions done:
960*43a90889SApple OSS Distributions 	mld_set_timeout(&mtp);
961*43a90889SApple OSS Distributions 
962*43a90889SApple OSS Distributions 	return err;
963*43a90889SApple OSS Distributions }
964*43a90889SApple OSS Distributions 
965*43a90889SApple OSS Distributions /*
966*43a90889SApple OSS Distributions  * Update the report timer on a group in response to an MLDv1 query.
967*43a90889SApple OSS Distributions  *
968*43a90889SApple OSS Distributions  * If we are becoming the reporting member for this group, start the timer.
969*43a90889SApple OSS Distributions  * If we already are the reporting member for this group, and timer is
970*43a90889SApple OSS Distributions  * below the threshold, reset it.
971*43a90889SApple OSS Distributions  *
972*43a90889SApple OSS Distributions  * We may be updating the group for the first time since we switched
973*43a90889SApple OSS Distributions  * to MLDv2. If we are, then we must clear any recorded source lists,
974*43a90889SApple OSS Distributions  * and transition to REPORTING state; the group timer is overloaded
975*43a90889SApple OSS Distributions  * for group and group-source query responses.
976*43a90889SApple OSS Distributions  *
977*43a90889SApple OSS Distributions  * Unlike MLDv2, the delay per group should be jittered
978*43a90889SApple OSS Distributions  * to avoid bursts of MLDv1 reports.
979*43a90889SApple OSS Distributions  */
980*43a90889SApple OSS Distributions static uint32_t
mld_v1_update_group(struct in6_multi * inm,const int timer)981*43a90889SApple OSS Distributions mld_v1_update_group(struct in6_multi *inm, const int timer)
982*43a90889SApple OSS Distributions {
983*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
984*43a90889SApple OSS Distributions 
985*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: %s/%s timer=%d\n", __func__,
986*43a90889SApple OSS Distributions 	    ip6_sprintf(&inm->in6m_addr),
987*43a90889SApple OSS Distributions 	    if_name(inm->in6m_ifp), timer));
988*43a90889SApple OSS Distributions 
989*43a90889SApple OSS Distributions 	switch (inm->in6m_state) {
990*43a90889SApple OSS Distributions 	case MLD_NOT_MEMBER:
991*43a90889SApple OSS Distributions 	case MLD_SILENT_MEMBER:
992*43a90889SApple OSS Distributions 		break;
993*43a90889SApple OSS Distributions 	case MLD_REPORTING_MEMBER:
994*43a90889SApple OSS Distributions 		if (inm->in6m_timer != 0 &&
995*43a90889SApple OSS Distributions 		    inm->in6m_timer <= timer) {
996*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: REPORTING and timer running, "
997*43a90889SApple OSS Distributions 			    "skipping.\n", __func__));
998*43a90889SApple OSS Distributions 			break;
999*43a90889SApple OSS Distributions 		}
1000*43a90889SApple OSS Distributions 		OS_FALLTHROUGH;
1001*43a90889SApple OSS Distributions 	case MLD_SG_QUERY_PENDING_MEMBER:
1002*43a90889SApple OSS Distributions 	case MLD_G_QUERY_PENDING_MEMBER:
1003*43a90889SApple OSS Distributions 	case MLD_IDLE_MEMBER:
1004*43a90889SApple OSS Distributions 	case MLD_LAZY_MEMBER:
1005*43a90889SApple OSS Distributions 	case MLD_AWAKENING_MEMBER:
1006*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: ->REPORTING\n", __func__));
1007*43a90889SApple OSS Distributions 		inm->in6m_state = MLD_REPORTING_MEMBER;
1008*43a90889SApple OSS Distributions 		inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1009*43a90889SApple OSS Distributions 		break;
1010*43a90889SApple OSS Distributions 	case MLD_SLEEPING_MEMBER:
1011*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: ->AWAKENING\n", __func__));
1012*43a90889SApple OSS Distributions 		inm->in6m_state = MLD_AWAKENING_MEMBER;
1013*43a90889SApple OSS Distributions 		break;
1014*43a90889SApple OSS Distributions 	case MLD_LEAVING_MEMBER:
1015*43a90889SApple OSS Distributions 		break;
1016*43a90889SApple OSS Distributions 	}
1017*43a90889SApple OSS Distributions 
1018*43a90889SApple OSS Distributions 	return inm->in6m_timer;
1019*43a90889SApple OSS Distributions }
1020*43a90889SApple OSS Distributions 
1021*43a90889SApple OSS Distributions /*
1022*43a90889SApple OSS Distributions  * Process a received MLDv2 general, group-specific or
1023*43a90889SApple OSS Distributions  * group-and-source-specific query.
1024*43a90889SApple OSS Distributions  *
1025*43a90889SApple OSS Distributions  * Assumes that the query header has been pulled up to sizeof(mldv2_query).
1026*43a90889SApple OSS Distributions  *
1027*43a90889SApple OSS Distributions  * Return 0 if successful, otherwise an appropriate error code is returned.
1028*43a90889SApple OSS Distributions  */
1029*43a90889SApple OSS Distributions static int
mld_v2_input_query(struct ifnet * ifp,const struct ip6_hdr * ip6,struct mbuf * m,const int off,const int icmp6len)1030*43a90889SApple OSS Distributions mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
1031*43a90889SApple OSS Distributions     struct mbuf *m, const int off, const int icmp6len)
1032*43a90889SApple OSS Distributions {
1033*43a90889SApple OSS Distributions 	struct mld_ifinfo       *mli;
1034*43a90889SApple OSS Distributions 	struct mldv2_query      *mld;
1035*43a90889SApple OSS Distributions 	struct in6_multi        *inm;
1036*43a90889SApple OSS Distributions 	uint32_t                 maxdelay, nsrc, qqi, timer;
1037*43a90889SApple OSS Distributions 	int                      err = 0, is_general_query;
1038*43a90889SApple OSS Distributions 	uint8_t                  qrv;
1039*43a90889SApple OSS Distributions 	struct mld_tparams       mtp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
1040*43a90889SApple OSS Distributions 
1041*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_NOTHELD();
1042*43a90889SApple OSS Distributions 
1043*43a90889SApple OSS Distributions 	is_general_query = 0;
1044*43a90889SApple OSS Distributions 
1045*43a90889SApple OSS Distributions 	if (!mld_v2enable) {
1046*43a90889SApple OSS Distributions 		os_log_info(OS_LOG_DEFAULT, "%s: ignore v2 query on ifp %s\n",
1047*43a90889SApple OSS Distributions 		    __func__, if_name(ifp));
1048*43a90889SApple OSS Distributions 		goto done;
1049*43a90889SApple OSS Distributions 	}
1050*43a90889SApple OSS Distributions 
1051*43a90889SApple OSS Distributions 	/*
1052*43a90889SApple OSS Distributions 	 * RFC3810 Section 6.2: MLD queries must originate from
1053*43a90889SApple OSS Distributions 	 * a router's link-local address.
1054*43a90889SApple OSS Distributions 	 */
1055*43a90889SApple OSS Distributions 	if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
1056*43a90889SApple OSS Distributions 		os_log_info(OS_LOG_DEFAULT,
1057*43a90889SApple OSS Distributions 		    "%s: ignore v1 query src %s on ifp %s\n",
1058*43a90889SApple OSS Distributions 		    __func__, ip6_sprintf(&ip6->ip6_src),
1059*43a90889SApple OSS Distributions 		    if_name(ifp));
1060*43a90889SApple OSS Distributions 		goto done;
1061*43a90889SApple OSS Distributions 	}
1062*43a90889SApple OSS Distributions 
1063*43a90889SApple OSS Distributions 	os_log_debug(OS_LOG_DEFAULT,
1064*43a90889SApple OSS Distributions 	    "%s: input v2 query on ifp %s\n", __func__,
1065*43a90889SApple OSS Distributions 	    if_name(ifp));
1066*43a90889SApple OSS Distributions 
1067*43a90889SApple OSS Distributions 	mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
1068*43a90889SApple OSS Distributions 
1069*43a90889SApple OSS Distributions 	maxdelay = ntohs(mld->mld_maxdelay);    /* in 1/10ths of a second */
1070*43a90889SApple OSS Distributions 	if (maxdelay > SHRT_MAX) {
1071*43a90889SApple OSS Distributions 		maxdelay = (MLD_MRC_MANT((uint16_t)maxdelay) | 0x1000) <<
1072*43a90889SApple OSS Distributions 		    (MLD_MRC_EXP((uint16_t)maxdelay) + 3);
1073*43a90889SApple OSS Distributions 	}
1074*43a90889SApple OSS Distributions 	timer = maxdelay / MLD_TIMER_SCALE;
1075*43a90889SApple OSS Distributions 	if (timer == 0) {
1076*43a90889SApple OSS Distributions 		timer = 1;
1077*43a90889SApple OSS Distributions 	}
1078*43a90889SApple OSS Distributions 
1079*43a90889SApple OSS Distributions 	qrv = MLD_QRV(mld->mld_misc);
1080*43a90889SApple OSS Distributions 	if (qrv < 2) {
1081*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: clamping qrv %d to %d\n", __func__,
1082*43a90889SApple OSS Distributions 		    qrv, MLD_RV_INIT));
1083*43a90889SApple OSS Distributions 		qrv = MLD_RV_INIT;
1084*43a90889SApple OSS Distributions 	}
1085*43a90889SApple OSS Distributions 
1086*43a90889SApple OSS Distributions 	qqi = mld->mld_qqi;
1087*43a90889SApple OSS Distributions 	if (qqi >= 128) {
1088*43a90889SApple OSS Distributions 		qqi = MLD_QQIC_MANT(mld->mld_qqi) <<
1089*43a90889SApple OSS Distributions 		    (MLD_QQIC_EXP(mld->mld_qqi) + 3);
1090*43a90889SApple OSS Distributions 	}
1091*43a90889SApple OSS Distributions 
1092*43a90889SApple OSS Distributions 	nsrc = ntohs(mld->mld_numsrc);
1093*43a90889SApple OSS Distributions 	if (nsrc > MLD_MAX_GS_SOURCES) {
1094*43a90889SApple OSS Distributions 		err = EMSGSIZE;
1095*43a90889SApple OSS Distributions 		goto done;
1096*43a90889SApple OSS Distributions 	}
1097*43a90889SApple OSS Distributions 	if (icmp6len < sizeof(struct mldv2_query) +
1098*43a90889SApple OSS Distributions 	    (nsrc * sizeof(struct in6_addr))) {
1099*43a90889SApple OSS Distributions 		err = EMSGSIZE;
1100*43a90889SApple OSS Distributions 		goto done;
1101*43a90889SApple OSS Distributions 	}
1102*43a90889SApple OSS Distributions 
1103*43a90889SApple OSS Distributions 	/*
1104*43a90889SApple OSS Distributions 	 * Do further input validation upfront to avoid resetting timers
1105*43a90889SApple OSS Distributions 	 * should we need to discard this query.
1106*43a90889SApple OSS Distributions 	 */
1107*43a90889SApple OSS Distributions 	if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
1108*43a90889SApple OSS Distributions 		/*
1109*43a90889SApple OSS Distributions 		 * A general query with a source list has undefined
1110*43a90889SApple OSS Distributions 		 * behaviour; discard it.
1111*43a90889SApple OSS Distributions 		 */
1112*43a90889SApple OSS Distributions 		if (nsrc > 0) {
1113*43a90889SApple OSS Distributions 			err = EINVAL;
1114*43a90889SApple OSS Distributions 			goto done;
1115*43a90889SApple OSS Distributions 		}
1116*43a90889SApple OSS Distributions 		is_general_query = 1;
1117*43a90889SApple OSS Distributions 	} else {
1118*43a90889SApple OSS Distributions 		/*
1119*43a90889SApple OSS Distributions 		 * Embed scope ID of receiving interface in MLD query for
1120*43a90889SApple OSS Distributions 		 * lookup whilst we don't hold other locks (due to KAME
1121*43a90889SApple OSS Distributions 		 * locking lameness). We own this mbuf chain just now.
1122*43a90889SApple OSS Distributions 		 */
1123*43a90889SApple OSS Distributions 		(void)in6_setscope(&mld->mld_addr, ifp, NULL);
1124*43a90889SApple OSS Distributions 	}
1125*43a90889SApple OSS Distributions 
1126*43a90889SApple OSS Distributions 	mli = MLD_IFINFO(ifp);
1127*43a90889SApple OSS Distributions 	VERIFY(mli != NULL);
1128*43a90889SApple OSS Distributions 
1129*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
1130*43a90889SApple OSS Distributions 	/*
1131*43a90889SApple OSS Distributions 	 * Discard the v2 query if we're in Compatibility Mode.
1132*43a90889SApple OSS Distributions 	 * The RFC is pretty clear that hosts need to stay in MLDv1 mode
1133*43a90889SApple OSS Distributions 	 * until the Old Version Querier Present timer expires.
1134*43a90889SApple OSS Distributions 	 */
1135*43a90889SApple OSS Distributions 	if (mli->mli_version != MLD_VERSION_2) {
1136*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
1137*43a90889SApple OSS Distributions 		goto done;
1138*43a90889SApple OSS Distributions 	}
1139*43a90889SApple OSS Distributions 
1140*43a90889SApple OSS Distributions 	mtp.qpt = mld_set_version(mli, MLD_VERSION_2);
1141*43a90889SApple OSS Distributions 	mli->mli_rv = qrv;
1142*43a90889SApple OSS Distributions 	mli->mli_qi = qqi;
1143*43a90889SApple OSS Distributions 	mli->mli_qri = MAX(timer, MLD_QRI_MIN);
1144*43a90889SApple OSS Distributions 
1145*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: qrv %d qi %d qri %d\n", __func__, mli->mli_rv,
1146*43a90889SApple OSS Distributions 	    mli->mli_qi, mli->mli_qri));
1147*43a90889SApple OSS Distributions 
1148*43a90889SApple OSS Distributions 	if (is_general_query) {
1149*43a90889SApple OSS Distributions 		/*
1150*43a90889SApple OSS Distributions 		 * MLDv2 General Query.
1151*43a90889SApple OSS Distributions 		 *
1152*43a90889SApple OSS Distributions 		 * Schedule a current-state report on this ifp for
1153*43a90889SApple OSS Distributions 		 * all groups, possibly containing source lists.
1154*43a90889SApple OSS Distributions 		 *
1155*43a90889SApple OSS Distributions 		 * If there is a pending General Query response
1156*43a90889SApple OSS Distributions 		 * scheduled earlier than the selected delay, do
1157*43a90889SApple OSS Distributions 		 * not schedule any other reports.
1158*43a90889SApple OSS Distributions 		 * Otherwise, reset the interface timer.
1159*43a90889SApple OSS Distributions 		 */
1160*43a90889SApple OSS Distributions 		os_log_debug(OS_LOG_DEFAULT, "%s: process v2 general query on ifp %s\n",
1161*43a90889SApple OSS Distributions 		    __func__, if_name(ifp));
1162*43a90889SApple OSS Distributions 		if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) {
1163*43a90889SApple OSS Distributions 			mtp.it = mli->mli_v2_timer = MLD_RANDOM_DELAY(timer);
1164*43a90889SApple OSS Distributions 		}
1165*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
1166*43a90889SApple OSS Distributions 	} else {
1167*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
1168*43a90889SApple OSS Distributions 		/*
1169*43a90889SApple OSS Distributions 		 * MLDv2 Group-specific or Group-and-source-specific Query.
1170*43a90889SApple OSS Distributions 		 *
1171*43a90889SApple OSS Distributions 		 * Group-source-specific queries are throttled on
1172*43a90889SApple OSS Distributions 		 * a per-group basis to defeat denial-of-service attempts.
1173*43a90889SApple OSS Distributions 		 * Queries for groups we are not a member of on this
1174*43a90889SApple OSS Distributions 		 * link are simply ignored.
1175*43a90889SApple OSS Distributions 		 */
1176*43a90889SApple OSS Distributions 		in6_multihead_lock_shared();
1177*43a90889SApple OSS Distributions 		IN6_LOOKUP_MULTI(&mld->mld_addr, ifp, inm);
1178*43a90889SApple OSS Distributions 		in6_multihead_lock_done();
1179*43a90889SApple OSS Distributions 		if (inm == NULL) {
1180*43a90889SApple OSS Distributions 			goto done;
1181*43a90889SApple OSS Distributions 		}
1182*43a90889SApple OSS Distributions 
1183*43a90889SApple OSS Distributions 		IN6M_LOCK(inm);
1184*43a90889SApple OSS Distributions 		if (nsrc > 0) {
1185*43a90889SApple OSS Distributions 			if (!ratecheck(&inm->in6m_lastgsrtv,
1186*43a90889SApple OSS Distributions 			    &mld_gsrdelay)) {
1187*43a90889SApple OSS Distributions 				os_log_info(OS_LOG_DEFAULT, "%s: GS query throttled\n",
1188*43a90889SApple OSS Distributions 				    __func__);
1189*43a90889SApple OSS Distributions 				IN6M_UNLOCK(inm);
1190*43a90889SApple OSS Distributions 				IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1191*43a90889SApple OSS Distributions 				goto done;
1192*43a90889SApple OSS Distributions 			}
1193*43a90889SApple OSS Distributions 		}
1194*43a90889SApple OSS Distributions 		os_log_debug(OS_LOG_DEFAULT, "%s: process v2 group query on ifp %s\n",
1195*43a90889SApple OSS Distributions 		    __func__, if_name(ifp));
1196*43a90889SApple OSS Distributions 		/*
1197*43a90889SApple OSS Distributions 		 * If there is a pending General Query response
1198*43a90889SApple OSS Distributions 		 * scheduled sooner than the selected delay, no
1199*43a90889SApple OSS Distributions 		 * further report need be scheduled.
1200*43a90889SApple OSS Distributions 		 * Otherwise, prepare to respond to the
1201*43a90889SApple OSS Distributions 		 * group-specific or group-and-source query.
1202*43a90889SApple OSS Distributions 		 */
1203*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
1204*43a90889SApple OSS Distributions 		mtp.it = mli->mli_v2_timer;
1205*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
1206*43a90889SApple OSS Distributions 		if (mtp.it == 0 || mtp.it >= timer) {
1207*43a90889SApple OSS Distributions 			(void) mld_v2_process_group_query(inm, timer, m, off);
1208*43a90889SApple OSS Distributions 			mtp.cst = inm->in6m_timer;
1209*43a90889SApple OSS Distributions 		}
1210*43a90889SApple OSS Distributions 		IN6M_UNLOCK(inm);
1211*43a90889SApple OSS Distributions 		IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1212*43a90889SApple OSS Distributions 		/* XXX Clear embedded scope ID as userland won't expect it. */
1213*43a90889SApple OSS Distributions 		in6_clearscope(&mld->mld_addr);
1214*43a90889SApple OSS Distributions 	}
1215*43a90889SApple OSS Distributions done:
1216*43a90889SApple OSS Distributions 	if (mtp.it > 0) {
1217*43a90889SApple OSS Distributions 		os_log_debug(OS_LOG_DEFAULT, "%s: v2 general query response scheduled in "
1218*43a90889SApple OSS Distributions 		    "T+%d seconds on ifp %s\n", __func__, mtp.it,
1219*43a90889SApple OSS Distributions 		    if_name(ifp));
1220*43a90889SApple OSS Distributions 	}
1221*43a90889SApple OSS Distributions 	mld_set_timeout(&mtp);
1222*43a90889SApple OSS Distributions 
1223*43a90889SApple OSS Distributions 	return err;
1224*43a90889SApple OSS Distributions }
1225*43a90889SApple OSS Distributions 
1226*43a90889SApple OSS Distributions /*
1227*43a90889SApple OSS Distributions  * Process a recieved MLDv2 group-specific or group-and-source-specific
1228*43a90889SApple OSS Distributions  * query.
1229*43a90889SApple OSS Distributions  * Return <0 if any error occured. Currently this is ignored.
1230*43a90889SApple OSS Distributions  */
1231*43a90889SApple OSS Distributions static int
mld_v2_process_group_query(struct in6_multi * inm,int timer,struct mbuf * m0,const int off)1232*43a90889SApple OSS Distributions mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
1233*43a90889SApple OSS Distributions     const int off)
1234*43a90889SApple OSS Distributions {
1235*43a90889SApple OSS Distributions 	struct mldv2_query      *mld;
1236*43a90889SApple OSS Distributions 	int                      retval;
1237*43a90889SApple OSS Distributions 	uint16_t                 nsrc;
1238*43a90889SApple OSS Distributions 
1239*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
1240*43a90889SApple OSS Distributions 
1241*43a90889SApple OSS Distributions 	retval = 0;
1242*43a90889SApple OSS Distributions 	mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off);
1243*43a90889SApple OSS Distributions 
1244*43a90889SApple OSS Distributions 	switch (inm->in6m_state) {
1245*43a90889SApple OSS Distributions 	case MLD_NOT_MEMBER:
1246*43a90889SApple OSS Distributions 	case MLD_SILENT_MEMBER:
1247*43a90889SApple OSS Distributions 	case MLD_SLEEPING_MEMBER:
1248*43a90889SApple OSS Distributions 	case MLD_LAZY_MEMBER:
1249*43a90889SApple OSS Distributions 	case MLD_AWAKENING_MEMBER:
1250*43a90889SApple OSS Distributions 	case MLD_IDLE_MEMBER:
1251*43a90889SApple OSS Distributions 	case MLD_LEAVING_MEMBER:
1252*43a90889SApple OSS Distributions 		return retval;
1253*43a90889SApple OSS Distributions 	case MLD_REPORTING_MEMBER:
1254*43a90889SApple OSS Distributions 	case MLD_G_QUERY_PENDING_MEMBER:
1255*43a90889SApple OSS Distributions 	case MLD_SG_QUERY_PENDING_MEMBER:
1256*43a90889SApple OSS Distributions 		break;
1257*43a90889SApple OSS Distributions 	}
1258*43a90889SApple OSS Distributions 
1259*43a90889SApple OSS Distributions 	nsrc = ntohs(mld->mld_numsrc);
1260*43a90889SApple OSS Distributions 
1261*43a90889SApple OSS Distributions 	/*
1262*43a90889SApple OSS Distributions 	 * Deal with group-specific queries upfront.
1263*43a90889SApple OSS Distributions 	 * If any group query is already pending, purge any recorded
1264*43a90889SApple OSS Distributions 	 * source-list state if it exists, and schedule a query response
1265*43a90889SApple OSS Distributions 	 * for this group-specific query.
1266*43a90889SApple OSS Distributions 	 */
1267*43a90889SApple OSS Distributions 	if (nsrc == 0) {
1268*43a90889SApple OSS Distributions 		if (inm->in6m_state == MLD_G_QUERY_PENDING_MEMBER ||
1269*43a90889SApple OSS Distributions 		    inm->in6m_state == MLD_SG_QUERY_PENDING_MEMBER) {
1270*43a90889SApple OSS Distributions 			in6m_clear_recorded(inm);
1271*43a90889SApple OSS Distributions 			timer = min(inm->in6m_timer, timer);
1272*43a90889SApple OSS Distributions 		}
1273*43a90889SApple OSS Distributions 		inm->in6m_state = MLD_G_QUERY_PENDING_MEMBER;
1274*43a90889SApple OSS Distributions 		inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1275*43a90889SApple OSS Distributions 		return retval;
1276*43a90889SApple OSS Distributions 	}
1277*43a90889SApple OSS Distributions 
1278*43a90889SApple OSS Distributions 	/*
1279*43a90889SApple OSS Distributions 	 * Deal with the case where a group-and-source-specific query has
1280*43a90889SApple OSS Distributions 	 * been received but a group-specific query is already pending.
1281*43a90889SApple OSS Distributions 	 */
1282*43a90889SApple OSS Distributions 	if (inm->in6m_state == MLD_G_QUERY_PENDING_MEMBER) {
1283*43a90889SApple OSS Distributions 		timer = min(inm->in6m_timer, timer);
1284*43a90889SApple OSS Distributions 		inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1285*43a90889SApple OSS Distributions 		return retval;
1286*43a90889SApple OSS Distributions 	}
1287*43a90889SApple OSS Distributions 
1288*43a90889SApple OSS Distributions 	/*
1289*43a90889SApple OSS Distributions 	 * Finally, deal with the case where a group-and-source-specific
1290*43a90889SApple OSS Distributions 	 * query has been received, where a response to a previous g-s-r
1291*43a90889SApple OSS Distributions 	 * query exists, or none exists.
1292*43a90889SApple OSS Distributions 	 * In this case, we need to parse the source-list which the Querier
1293*43a90889SApple OSS Distributions 	 * has provided us with and check if we have any source list filter
1294*43a90889SApple OSS Distributions 	 * entries at T1 for these sources. If we do not, there is no need
1295*43a90889SApple OSS Distributions 	 * schedule a report and the query may be dropped.
1296*43a90889SApple OSS Distributions 	 * If we do, we must record them and schedule a current-state
1297*43a90889SApple OSS Distributions 	 * report for those sources.
1298*43a90889SApple OSS Distributions 	 */
1299*43a90889SApple OSS Distributions 	if (inm->in6m_nsrc > 0) {
1300*43a90889SApple OSS Distributions 		struct mbuf             *m;
1301*43a90889SApple OSS Distributions 		struct in6_addr          addr;
1302*43a90889SApple OSS Distributions 		int                      i, nrecorded;
1303*43a90889SApple OSS Distributions 		int                      soff;
1304*43a90889SApple OSS Distributions 
1305*43a90889SApple OSS Distributions 		m = m0;
1306*43a90889SApple OSS Distributions 		soff = off + sizeof(struct mldv2_query);
1307*43a90889SApple OSS Distributions 		nrecorded = 0;
1308*43a90889SApple OSS Distributions 		for (i = 0; i < nsrc; i++) {
1309*43a90889SApple OSS Distributions 			m_copydata(m, soff, sizeof(addr), &addr);
1310*43a90889SApple OSS Distributions 			retval = in6m_record_source(inm, &addr);
1311*43a90889SApple OSS Distributions 			if (retval < 0) {
1312*43a90889SApple OSS Distributions 				break;
1313*43a90889SApple OSS Distributions 			}
1314*43a90889SApple OSS Distributions 			nrecorded += retval;
1315*43a90889SApple OSS Distributions 			soff += sizeof(struct in6_addr);
1316*43a90889SApple OSS Distributions 
1317*43a90889SApple OSS Distributions 			while (m && (soff >= m->m_len)) {
1318*43a90889SApple OSS Distributions 				soff -= m->m_len;
1319*43a90889SApple OSS Distributions 				m = m->m_next;
1320*43a90889SApple OSS Distributions 			}
1321*43a90889SApple OSS Distributions 
1322*43a90889SApple OSS Distributions 			/* should not be possible: */
1323*43a90889SApple OSS Distributions 			if (m == NULL) {
1324*43a90889SApple OSS Distributions 				break;
1325*43a90889SApple OSS Distributions 			}
1326*43a90889SApple OSS Distributions 		}
1327*43a90889SApple OSS Distributions 		if (nrecorded > 0) {
1328*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: schedule response to SG query\n",
1329*43a90889SApple OSS Distributions 			    __func__));
1330*43a90889SApple OSS Distributions 			inm->in6m_state = MLD_SG_QUERY_PENDING_MEMBER;
1331*43a90889SApple OSS Distributions 			inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1332*43a90889SApple OSS Distributions 		}
1333*43a90889SApple OSS Distributions 	}
1334*43a90889SApple OSS Distributions 
1335*43a90889SApple OSS Distributions 	return retval;
1336*43a90889SApple OSS Distributions }
1337*43a90889SApple OSS Distributions 
1338*43a90889SApple OSS Distributions /*
1339*43a90889SApple OSS Distributions  * Process a received MLDv1 host membership report.
1340*43a90889SApple OSS Distributions  * Assumes mld points to mld_hdr in pulled up mbuf chain.
1341*43a90889SApple OSS Distributions  *
1342*43a90889SApple OSS Distributions  * NOTE: Can't be fully const correct as we temporarily embed scope ID in
1343*43a90889SApple OSS Distributions  * mld_addr. This is OK as we own the mbuf chain.
1344*43a90889SApple OSS Distributions  */
1345*43a90889SApple OSS Distributions static int
mld_v1_input_report(struct ifnet * ifp,struct mbuf * m,const struct ip6_hdr * ip6,struct mld_hdr * mld)1346*43a90889SApple OSS Distributions mld_v1_input_report(struct ifnet *ifp, struct mbuf *m,
1347*43a90889SApple OSS Distributions     const struct ip6_hdr *ip6, /*const*/ struct mld_hdr *mld)
1348*43a90889SApple OSS Distributions {
1349*43a90889SApple OSS Distributions 	struct in6_addr          src, dst;
1350*43a90889SApple OSS Distributions 	struct in6_ifaddr       *ia;
1351*43a90889SApple OSS Distributions 	struct in6_multi        *inm;
1352*43a90889SApple OSS Distributions 
1353*43a90889SApple OSS Distributions 	if (!mld_v1enable) {
1354*43a90889SApple OSS Distributions 		os_log_info(OS_LOG_DEFAULT, "%s: ignore v1 report on ifp %s\n",
1355*43a90889SApple OSS Distributions 		    __func__, if_name(ifp));
1356*43a90889SApple OSS Distributions 		return 0;
1357*43a90889SApple OSS Distributions 	}
1358*43a90889SApple OSS Distributions 
1359*43a90889SApple OSS Distributions 	if ((ifp->if_flags & IFF_LOOPBACK) ||
1360*43a90889SApple OSS Distributions 	    (m->m_pkthdr.pkt_flags & PKTF_LOOP)) {
1361*43a90889SApple OSS Distributions 		return 0;
1362*43a90889SApple OSS Distributions 	}
1363*43a90889SApple OSS Distributions 
1364*43a90889SApple OSS Distributions 	/*
1365*43a90889SApple OSS Distributions 	 * MLDv1 reports must originate from a host's link-local address,
1366*43a90889SApple OSS Distributions 	 * or the unspecified address (when booting).
1367*43a90889SApple OSS Distributions 	 */
1368*43a90889SApple OSS Distributions 	src = ip6->ip6_src;
1369*43a90889SApple OSS Distributions 	in6_clearscope(&src);
1370*43a90889SApple OSS Distributions 	if (!IN6_IS_SCOPE_LINKLOCAL(&src) && !IN6_IS_ADDR_UNSPECIFIED(&src)) {
1371*43a90889SApple OSS Distributions 		os_log_info(OS_LOG_DEFAULT, "%s: ignore v1 query src %s on ifp %s\n",
1372*43a90889SApple OSS Distributions 		    __func__, ip6_sprintf(&ip6->ip6_src),
1373*43a90889SApple OSS Distributions 		    if_name(ifp));
1374*43a90889SApple OSS Distributions 		return EINVAL;
1375*43a90889SApple OSS Distributions 	}
1376*43a90889SApple OSS Distributions 
1377*43a90889SApple OSS Distributions 	/*
1378*43a90889SApple OSS Distributions 	 * RFC2710 Section 4: MLDv1 reports must pertain to a multicast
1379*43a90889SApple OSS Distributions 	 * group, and must be directed to the group itself.
1380*43a90889SApple OSS Distributions 	 */
1381*43a90889SApple OSS Distributions 	dst = ip6->ip6_dst;
1382*43a90889SApple OSS Distributions 	in6_clearscope(&dst);
1383*43a90889SApple OSS Distributions 	if (!IN6_IS_ADDR_MULTICAST(&mld->mld_addr) ||
1384*43a90889SApple OSS Distributions 	    !IN6_ARE_ADDR_EQUAL(&mld->mld_addr, &dst)) {
1385*43a90889SApple OSS Distributions 		os_log_info(OS_LOG_DEFAULT, "%s: ignore v1 query dst %s on ifp %s\n",
1386*43a90889SApple OSS Distributions 		    __func__, ip6_sprintf(&ip6->ip6_dst),
1387*43a90889SApple OSS Distributions 		    if_name(ifp));
1388*43a90889SApple OSS Distributions 		return EINVAL;
1389*43a90889SApple OSS Distributions 	}
1390*43a90889SApple OSS Distributions 
1391*43a90889SApple OSS Distributions 	/*
1392*43a90889SApple OSS Distributions 	 * Make sure we don't hear our own membership report, as fast
1393*43a90889SApple OSS Distributions 	 * leave requires knowing that we are the only member of a
1394*43a90889SApple OSS Distributions 	 * group. Assume we used the link-local address if available,
1395*43a90889SApple OSS Distributions 	 * otherwise look for ::.
1396*43a90889SApple OSS Distributions 	 *
1397*43a90889SApple OSS Distributions 	 * XXX Note that scope ID comparison is needed for the address
1398*43a90889SApple OSS Distributions 	 * returned by in6ifa_ifpforlinklocal(), but SHOULD NOT be
1399*43a90889SApple OSS Distributions 	 * performed for the on-wire address.
1400*43a90889SApple OSS Distributions 	 */
1401*43a90889SApple OSS Distributions 	ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
1402*43a90889SApple OSS Distributions 	if (ia != NULL) {
1403*43a90889SApple OSS Distributions 		IFA_LOCK(&ia->ia_ifa);
1404*43a90889SApple OSS Distributions 		if ((IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, IA6_IN6(ia)))) {
1405*43a90889SApple OSS Distributions 			IFA_UNLOCK(&ia->ia_ifa);
1406*43a90889SApple OSS Distributions 			ifa_remref(&ia->ia_ifa);
1407*43a90889SApple OSS Distributions 			return 0;
1408*43a90889SApple OSS Distributions 		}
1409*43a90889SApple OSS Distributions 		IFA_UNLOCK(&ia->ia_ifa);
1410*43a90889SApple OSS Distributions 		ifa_remref(&ia->ia_ifa);
1411*43a90889SApple OSS Distributions 	} else if (IN6_IS_ADDR_UNSPECIFIED(&src)) {
1412*43a90889SApple OSS Distributions 		return 0;
1413*43a90889SApple OSS Distributions 	}
1414*43a90889SApple OSS Distributions 
1415*43a90889SApple OSS Distributions 	os_log_debug(OS_LOG_DEFAULT, "%s: process v1 report %s on ifp %s\n",
1416*43a90889SApple OSS Distributions 	    __func__, ip6_sprintf(&mld->mld_addr),
1417*43a90889SApple OSS Distributions 	    if_name(ifp));
1418*43a90889SApple OSS Distributions 
1419*43a90889SApple OSS Distributions 	/*
1420*43a90889SApple OSS Distributions 	 * Embed scope ID of receiving interface in MLD query for lookup
1421*43a90889SApple OSS Distributions 	 * whilst we don't hold other locks (due to KAME locking lameness).
1422*43a90889SApple OSS Distributions 	 */
1423*43a90889SApple OSS Distributions 	if (!IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
1424*43a90889SApple OSS Distributions 		(void)in6_setscope(&mld->mld_addr, ifp, NULL);
1425*43a90889SApple OSS Distributions 	}
1426*43a90889SApple OSS Distributions 
1427*43a90889SApple OSS Distributions 	/*
1428*43a90889SApple OSS Distributions 	 * MLDv1 report suppression.
1429*43a90889SApple OSS Distributions 	 * If we are a member of this group, and our membership should be
1430*43a90889SApple OSS Distributions 	 * reported, and our group timer is pending or about to be reset,
1431*43a90889SApple OSS Distributions 	 * stop our group timer by transitioning to the 'lazy' state.
1432*43a90889SApple OSS Distributions 	 */
1433*43a90889SApple OSS Distributions 	in6_multihead_lock_shared();
1434*43a90889SApple OSS Distributions 	IN6_LOOKUP_MULTI(&mld->mld_addr, ifp, inm);
1435*43a90889SApple OSS Distributions 	in6_multihead_lock_done();
1436*43a90889SApple OSS Distributions 
1437*43a90889SApple OSS Distributions 	if (inm != NULL) {
1438*43a90889SApple OSS Distributions 		struct mld_ifinfo *mli;
1439*43a90889SApple OSS Distributions 
1440*43a90889SApple OSS Distributions 		IN6M_LOCK(inm);
1441*43a90889SApple OSS Distributions 		mli = inm->in6m_mli;
1442*43a90889SApple OSS Distributions 		VERIFY(mli != NULL);
1443*43a90889SApple OSS Distributions 
1444*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
1445*43a90889SApple OSS Distributions 		/*
1446*43a90889SApple OSS Distributions 		 * If we are in MLDv2 host mode, do not allow the
1447*43a90889SApple OSS Distributions 		 * other host's MLDv1 report to suppress our reports.
1448*43a90889SApple OSS Distributions 		 */
1449*43a90889SApple OSS Distributions 		if (mli->mli_version == MLD_VERSION_2) {
1450*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
1451*43a90889SApple OSS Distributions 			IN6M_UNLOCK(inm);
1452*43a90889SApple OSS Distributions 			IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1453*43a90889SApple OSS Distributions 			goto out;
1454*43a90889SApple OSS Distributions 		}
1455*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
1456*43a90889SApple OSS Distributions 
1457*43a90889SApple OSS Distributions 		inm->in6m_timer = 0;
1458*43a90889SApple OSS Distributions 
1459*43a90889SApple OSS Distributions 		switch (inm->in6m_state) {
1460*43a90889SApple OSS Distributions 		case MLD_NOT_MEMBER:
1461*43a90889SApple OSS Distributions 		case MLD_SILENT_MEMBER:
1462*43a90889SApple OSS Distributions 		case MLD_SLEEPING_MEMBER:
1463*43a90889SApple OSS Distributions 			break;
1464*43a90889SApple OSS Distributions 		case MLD_REPORTING_MEMBER:
1465*43a90889SApple OSS Distributions 		case MLD_IDLE_MEMBER:
1466*43a90889SApple OSS Distributions 		case MLD_AWAKENING_MEMBER:
1467*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: report suppressed for %s on "
1468*43a90889SApple OSS Distributions 			    "ifp 0x%llx(%s)\n", __func__,
1469*43a90889SApple OSS Distributions 			    ip6_sprintf(&mld->mld_addr),
1470*43a90889SApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1471*43a90889SApple OSS Distributions 			OS_FALLTHROUGH;
1472*43a90889SApple OSS Distributions 		case MLD_LAZY_MEMBER:
1473*43a90889SApple OSS Distributions 			inm->in6m_state = MLD_LAZY_MEMBER;
1474*43a90889SApple OSS Distributions 			break;
1475*43a90889SApple OSS Distributions 		case MLD_G_QUERY_PENDING_MEMBER:
1476*43a90889SApple OSS Distributions 		case MLD_SG_QUERY_PENDING_MEMBER:
1477*43a90889SApple OSS Distributions 		case MLD_LEAVING_MEMBER:
1478*43a90889SApple OSS Distributions 			break;
1479*43a90889SApple OSS Distributions 		}
1480*43a90889SApple OSS Distributions 		IN6M_UNLOCK(inm);
1481*43a90889SApple OSS Distributions 		IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1482*43a90889SApple OSS Distributions 	}
1483*43a90889SApple OSS Distributions 
1484*43a90889SApple OSS Distributions out:
1485*43a90889SApple OSS Distributions 	/* XXX Clear embedded scope ID as userland won't expect it. */
1486*43a90889SApple OSS Distributions 	in6_clearscope(&mld->mld_addr);
1487*43a90889SApple OSS Distributions 
1488*43a90889SApple OSS Distributions 	return 0;
1489*43a90889SApple OSS Distributions }
1490*43a90889SApple OSS Distributions 
1491*43a90889SApple OSS Distributions /*
1492*43a90889SApple OSS Distributions  * MLD input path.
1493*43a90889SApple OSS Distributions  *
1494*43a90889SApple OSS Distributions  * Assume query messages which fit in a single ICMPv6 message header
1495*43a90889SApple OSS Distributions  * have been pulled up.
1496*43a90889SApple OSS Distributions  * Assume that userland will want to see the message, even if it
1497*43a90889SApple OSS Distributions  * otherwise fails kernel input validation; do not free it.
1498*43a90889SApple OSS Distributions  * Pullup may however free the mbuf chain m if it fails.
1499*43a90889SApple OSS Distributions  *
1500*43a90889SApple OSS Distributions  * Return IPPROTO_DONE if we freed m. Otherwise, return 0.
1501*43a90889SApple OSS Distributions  */
1502*43a90889SApple OSS Distributions int
mld_input(struct mbuf * m,int off,int icmp6len)1503*43a90889SApple OSS Distributions mld_input(struct mbuf *m, int off, int icmp6len)
1504*43a90889SApple OSS Distributions {
1505*43a90889SApple OSS Distributions 	struct ifnet    *ifp = NULL;
1506*43a90889SApple OSS Distributions 	struct ip6_hdr  *ip6 = NULL;
1507*43a90889SApple OSS Distributions 	struct mld_hdr  *mld = NULL;
1508*43a90889SApple OSS Distributions 	int              mldlen = 0;
1509*43a90889SApple OSS Distributions 
1510*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: called w/mbuf (0x%llx,%d)\n", __func__,
1511*43a90889SApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(m), off));
1512*43a90889SApple OSS Distributions 
1513*43a90889SApple OSS Distributions 	ifp = m->m_pkthdr.rcvif;
1514*43a90889SApple OSS Distributions 
1515*43a90889SApple OSS Distributions 	/* Pullup to appropriate size. */
1516*43a90889SApple OSS Distributions 	mld = (struct mld_hdr *)(mtod(m, uint8_t *) + off);
1517*43a90889SApple OSS Distributions 	if (mld->mld_type == MLD_LISTENER_QUERY &&
1518*43a90889SApple OSS Distributions 	    icmp6len >= sizeof(struct mldv2_query)) {
1519*43a90889SApple OSS Distributions 		mldlen = sizeof(struct mldv2_query);
1520*43a90889SApple OSS Distributions 	} else {
1521*43a90889SApple OSS Distributions 		mldlen = sizeof(struct mld_hdr);
1522*43a90889SApple OSS Distributions 	}
1523*43a90889SApple OSS Distributions 	// check if mldv2_query/mld_hdr fits in the first mbuf
1524*43a90889SApple OSS Distributions 	IP6_EXTHDR_CHECK(m, off, mldlen, return IPPROTO_DONE);
1525*43a90889SApple OSS Distributions 	IP6_EXTHDR_GET(mld, struct mld_hdr *, m, off, mldlen);
1526*43a90889SApple OSS Distributions 	if (mld == NULL) {
1527*43a90889SApple OSS Distributions 		icmp6stat.icp6s_badlen++;
1528*43a90889SApple OSS Distributions 		return IPPROTO_DONE;
1529*43a90889SApple OSS Distributions 	}
1530*43a90889SApple OSS Distributions 	ip6 = mtod(m, struct ip6_hdr *);
1531*43a90889SApple OSS Distributions 
1532*43a90889SApple OSS Distributions 	/*
1533*43a90889SApple OSS Distributions 	 * Userland needs to see all of this traffic for implementing
1534*43a90889SApple OSS Distributions 	 * the endpoint discovery portion of multicast routing.
1535*43a90889SApple OSS Distributions 	 */
1536*43a90889SApple OSS Distributions 	switch (mld->mld_type) {
1537*43a90889SApple OSS Distributions 	case MLD_LISTENER_QUERY:
1538*43a90889SApple OSS Distributions 		icmp6_ifstat_inc(ifp, ifs6_in_mldquery);
1539*43a90889SApple OSS Distributions 		if (icmp6len == sizeof(struct mld_hdr)) {
1540*43a90889SApple OSS Distributions 			if (mld_v1_input_query(ifp, ip6, mld) != 0) {
1541*43a90889SApple OSS Distributions 				return 0;
1542*43a90889SApple OSS Distributions 			}
1543*43a90889SApple OSS Distributions 		} else if (icmp6len >= sizeof(struct mldv2_query)) {
1544*43a90889SApple OSS Distributions 			if (mld_v2_input_query(ifp, ip6, m, off,
1545*43a90889SApple OSS Distributions 			    icmp6len) != 0) {
1546*43a90889SApple OSS Distributions 				return 0;
1547*43a90889SApple OSS Distributions 			}
1548*43a90889SApple OSS Distributions 		}
1549*43a90889SApple OSS Distributions 		break;
1550*43a90889SApple OSS Distributions 	case MLD_LISTENER_REPORT:
1551*43a90889SApple OSS Distributions 		icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
1552*43a90889SApple OSS Distributions 		if (mld_v1_input_report(ifp, m, ip6, mld) != 0) {
1553*43a90889SApple OSS Distributions 			return 0;
1554*43a90889SApple OSS Distributions 		}
1555*43a90889SApple OSS Distributions 		break;
1556*43a90889SApple OSS Distributions 	case MLDV2_LISTENER_REPORT:
1557*43a90889SApple OSS Distributions 		icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
1558*43a90889SApple OSS Distributions 		break;
1559*43a90889SApple OSS Distributions 	case MLD_LISTENER_DONE:
1560*43a90889SApple OSS Distributions 		icmp6_ifstat_inc(ifp, ifs6_in_mlddone);
1561*43a90889SApple OSS Distributions 		break;
1562*43a90889SApple OSS Distributions 	default:
1563*43a90889SApple OSS Distributions 		break;
1564*43a90889SApple OSS Distributions 	}
1565*43a90889SApple OSS Distributions 
1566*43a90889SApple OSS Distributions 	return 0;
1567*43a90889SApple OSS Distributions }
1568*43a90889SApple OSS Distributions 
1569*43a90889SApple OSS Distributions /*
1570*43a90889SApple OSS Distributions  * Schedule MLD timer based on various parameters; caller must ensure that
1571*43a90889SApple OSS Distributions  * lock ordering is maintained as this routine acquires MLD global lock.
1572*43a90889SApple OSS Distributions  */
1573*43a90889SApple OSS Distributions void
mld_set_timeout(struct mld_tparams * mtp)1574*43a90889SApple OSS Distributions mld_set_timeout(struct mld_tparams *mtp)
1575*43a90889SApple OSS Distributions {
1576*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_NOTHELD();
1577*43a90889SApple OSS Distributions 	VERIFY(mtp != NULL);
1578*43a90889SApple OSS Distributions 
1579*43a90889SApple OSS Distributions 	if (mtp->qpt != 0 || mtp->it != 0 || mtp->cst != 0 || mtp->sct != 0) {
1580*43a90889SApple OSS Distributions 		MLD_LOCK();
1581*43a90889SApple OSS Distributions 		if (mtp->qpt != 0) {
1582*43a90889SApple OSS Distributions 			querier_present_timers_running6 = 1;
1583*43a90889SApple OSS Distributions 		}
1584*43a90889SApple OSS Distributions 		if (mtp->it != 0) {
1585*43a90889SApple OSS Distributions 			interface_timers_running6 = 1;
1586*43a90889SApple OSS Distributions 		}
1587*43a90889SApple OSS Distributions 		if (mtp->cst != 0) {
1588*43a90889SApple OSS Distributions 			current_state_timers_running6 = 1;
1589*43a90889SApple OSS Distributions 		}
1590*43a90889SApple OSS Distributions 		if (mtp->sct != 0) {
1591*43a90889SApple OSS Distributions 			state_change_timers_running6 = 1;
1592*43a90889SApple OSS Distributions 		}
1593*43a90889SApple OSS Distributions 		if (mtp->fast) {
1594*43a90889SApple OSS Distributions 			mld_sched_fast_timeout();
1595*43a90889SApple OSS Distributions 		} else {
1596*43a90889SApple OSS Distributions 			mld_sched_timeout();
1597*43a90889SApple OSS Distributions 		}
1598*43a90889SApple OSS Distributions 		MLD_UNLOCK();
1599*43a90889SApple OSS Distributions 	}
1600*43a90889SApple OSS Distributions }
1601*43a90889SApple OSS Distributions 
1602*43a90889SApple OSS Distributions void
mld_set_fast_timeout(struct mld_tparams * mtp)1603*43a90889SApple OSS Distributions mld_set_fast_timeout(struct mld_tparams *mtp)
1604*43a90889SApple OSS Distributions {
1605*43a90889SApple OSS Distributions 	VERIFY(mtp != NULL);
1606*43a90889SApple OSS Distributions 	mtp->fast = true;
1607*43a90889SApple OSS Distributions 	mld_set_timeout(mtp);
1608*43a90889SApple OSS Distributions }
1609*43a90889SApple OSS Distributions 
1610*43a90889SApple OSS Distributions /*
1611*43a90889SApple OSS Distributions  * MLD6 timer handler (per 1 second).
1612*43a90889SApple OSS Distributions  */
1613*43a90889SApple OSS Distributions static void
mld_timeout(thread_call_param_t arg0,thread_call_param_t arg1 __unused)1614*43a90889SApple OSS Distributions mld_timeout(thread_call_param_t arg0, thread_call_param_t arg1 __unused)
1615*43a90889SApple OSS Distributions {
1616*43a90889SApple OSS Distributions 	struct ifqueue           scq;   /* State-change packets */
1617*43a90889SApple OSS Distributions 	struct ifqueue           qrq;   /* Query response packets */
1618*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
1619*43a90889SApple OSS Distributions 	struct mld_ifinfo       *mli;
1620*43a90889SApple OSS Distributions 	struct in6_multi        *inm;
1621*43a90889SApple OSS Distributions 	int                      uri_sec = 0;
1622*43a90889SApple OSS Distributions 	unsigned int genid = mld_mli_list_genid;
1623*43a90889SApple OSS Distributions 	bool                     fast = arg0 != NULL;
1624*43a90889SApple OSS Distributions 
1625*43a90889SApple OSS Distributions 	SLIST_HEAD(, in6_multi) in6m_dthead;
1626*43a90889SApple OSS Distributions 
1627*43a90889SApple OSS Distributions 	SLIST_INIT(&in6m_dthead);
1628*43a90889SApple OSS Distributions 
1629*43a90889SApple OSS Distributions 	/*
1630*43a90889SApple OSS Distributions 	 * Update coarse-grained networking timestamp (in sec.); the idea
1631*43a90889SApple OSS Distributions 	 * is to piggy-back on the timeout callout to update the counter
1632*43a90889SApple OSS Distributions 	 * returnable via net_uptime().
1633*43a90889SApple OSS Distributions 	 */
1634*43a90889SApple OSS Distributions 	net_update_uptime();
1635*43a90889SApple OSS Distributions 
1636*43a90889SApple OSS Distributions 	MLD_LOCK();
1637*43a90889SApple OSS Distributions 
1638*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: qpt %d, it %d, cst %d, sct %d, fast %d\n", __func__,
1639*43a90889SApple OSS Distributions 	    querier_present_timers_running6, interface_timers_running6,
1640*43a90889SApple OSS Distributions 	    current_state_timers_running6, state_change_timers_running6, fast));
1641*43a90889SApple OSS Distributions 
1642*43a90889SApple OSS Distributions 	if (fast) {
1643*43a90889SApple OSS Distributions 		/*
1644*43a90889SApple OSS Distributions 		 * When running the fast timer, skip processing
1645*43a90889SApple OSS Distributions 		 * of "querier present" timers since they are
1646*43a90889SApple OSS Distributions 		 * based on 1-second intervals.
1647*43a90889SApple OSS Distributions 		 */
1648*43a90889SApple OSS Distributions 		goto skip_query_timers;
1649*43a90889SApple OSS Distributions 	}
1650*43a90889SApple OSS Distributions 	/*
1651*43a90889SApple OSS Distributions 	 * MLDv1 querier present timer processing.
1652*43a90889SApple OSS Distributions 	 */
1653*43a90889SApple OSS Distributions 	if (querier_present_timers_running6) {
1654*43a90889SApple OSS Distributions 		querier_present_timers_running6 = 0;
1655*43a90889SApple OSS Distributions 		LIST_FOREACH(mli, &mli_head, mli_link) {
1656*43a90889SApple OSS Distributions 			MLI_LOCK(mli);
1657*43a90889SApple OSS Distributions 			mld_v1_process_querier_timers(mli);
1658*43a90889SApple OSS Distributions 			if (mli->mli_v1_timer > 0) {
1659*43a90889SApple OSS Distributions 				querier_present_timers_running6 = 1;
1660*43a90889SApple OSS Distributions 			}
1661*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
1662*43a90889SApple OSS Distributions 		}
1663*43a90889SApple OSS Distributions 	}
1664*43a90889SApple OSS Distributions 
1665*43a90889SApple OSS Distributions 	/*
1666*43a90889SApple OSS Distributions 	 * MLDv2 General Query response timer processing.
1667*43a90889SApple OSS Distributions 	 */
1668*43a90889SApple OSS Distributions 	if (interface_timers_running6) {
1669*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: interface timers running\n", __func__));
1670*43a90889SApple OSS Distributions 		interface_timers_running6 = 0;
1671*43a90889SApple OSS Distributions 		mli = LIST_FIRST(&mli_head);
1672*43a90889SApple OSS Distributions 
1673*43a90889SApple OSS Distributions 		while (mli != NULL) {
1674*43a90889SApple OSS Distributions 			if (mli->mli_flags & MLIF_PROCESSED) {
1675*43a90889SApple OSS Distributions 				mli = LIST_NEXT(mli, mli_link);
1676*43a90889SApple OSS Distributions 				continue;
1677*43a90889SApple OSS Distributions 			}
1678*43a90889SApple OSS Distributions 
1679*43a90889SApple OSS Distributions 			MLI_LOCK(mli);
1680*43a90889SApple OSS Distributions 			if (mli->mli_version != MLD_VERSION_2) {
1681*43a90889SApple OSS Distributions 				MLI_UNLOCK(mli);
1682*43a90889SApple OSS Distributions 				mli = LIST_NEXT(mli, mli_link);
1683*43a90889SApple OSS Distributions 				continue;
1684*43a90889SApple OSS Distributions 			}
1685*43a90889SApple OSS Distributions 			/*
1686*43a90889SApple OSS Distributions 			 * XXX The logic below ends up calling
1687*43a90889SApple OSS Distributions 			 * mld_dispatch_packet which can unlock mli
1688*43a90889SApple OSS Distributions 			 * and the global MLD lock.
1689*43a90889SApple OSS Distributions 			 * Therefore grab a reference on MLI and also
1690*43a90889SApple OSS Distributions 			 * check for generation count to see if we should
1691*43a90889SApple OSS Distributions 			 * iterate the list again.
1692*43a90889SApple OSS Distributions 			 */
1693*43a90889SApple OSS Distributions 			MLI_ADDREF_LOCKED(mli);
1694*43a90889SApple OSS Distributions 
1695*43a90889SApple OSS Distributions 			if (mli->mli_v2_timer == 0) {
1696*43a90889SApple OSS Distributions 				/* Do nothing. */
1697*43a90889SApple OSS Distributions 			} else if (--mli->mli_v2_timer == 0) {
1698*43a90889SApple OSS Distributions 				if (mld_v2_dispatch_general_query(mli) > 0) {
1699*43a90889SApple OSS Distributions 					interface_timers_running6 = 1;
1700*43a90889SApple OSS Distributions 				}
1701*43a90889SApple OSS Distributions 			} else {
1702*43a90889SApple OSS Distributions 				interface_timers_running6 = 1;
1703*43a90889SApple OSS Distributions 			}
1704*43a90889SApple OSS Distributions 			mli->mli_flags |= MLIF_PROCESSED;
1705*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
1706*43a90889SApple OSS Distributions 			MLI_REMREF(mli);
1707*43a90889SApple OSS Distributions 
1708*43a90889SApple OSS Distributions 			if (genid != mld_mli_list_genid) {
1709*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: MLD information list changed "
1710*43a90889SApple OSS Distributions 				    "in the middle of iteration! Restart iteration.\n",
1711*43a90889SApple OSS Distributions 				    __func__));
1712*43a90889SApple OSS Distributions 				mli = LIST_FIRST(&mli_head);
1713*43a90889SApple OSS Distributions 				genid = mld_mli_list_genid;
1714*43a90889SApple OSS Distributions 			} else {
1715*43a90889SApple OSS Distributions 				mli = LIST_NEXT(mli, mli_link);
1716*43a90889SApple OSS Distributions 			}
1717*43a90889SApple OSS Distributions 		}
1718*43a90889SApple OSS Distributions 
1719*43a90889SApple OSS Distributions 		LIST_FOREACH(mli, &mli_head, mli_link)
1720*43a90889SApple OSS Distributions 		mli->mli_flags &= ~MLIF_PROCESSED;
1721*43a90889SApple OSS Distributions 	}
1722*43a90889SApple OSS Distributions 
1723*43a90889SApple OSS Distributions skip_query_timers:
1724*43a90889SApple OSS Distributions 	if (!current_state_timers_running6 &&
1725*43a90889SApple OSS Distributions 	    !state_change_timers_running6) {
1726*43a90889SApple OSS Distributions 		goto out_locked;
1727*43a90889SApple OSS Distributions 	}
1728*43a90889SApple OSS Distributions 
1729*43a90889SApple OSS Distributions 	current_state_timers_running6 = 0;
1730*43a90889SApple OSS Distributions 	state_change_timers_running6 = 0;
1731*43a90889SApple OSS Distributions 
1732*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: state change timers running\n", __func__));
1733*43a90889SApple OSS Distributions 
1734*43a90889SApple OSS Distributions 	memset(&qrq, 0, sizeof(struct ifqueue));
1735*43a90889SApple OSS Distributions 	qrq.ifq_maxlen = MLD_MAX_G_GS_PACKETS;
1736*43a90889SApple OSS Distributions 
1737*43a90889SApple OSS Distributions 	memset(&scq, 0, sizeof(struct ifqueue));
1738*43a90889SApple OSS Distributions 	scq.ifq_maxlen = MLD_MAX_STATE_CHANGE_PACKETS;
1739*43a90889SApple OSS Distributions 
1740*43a90889SApple OSS Distributions 	/*
1741*43a90889SApple OSS Distributions 	 * MLD host report and state-change timer processing.
1742*43a90889SApple OSS Distributions 	 * Note: Processing a v2 group timer may remove a node.
1743*43a90889SApple OSS Distributions 	 */
1744*43a90889SApple OSS Distributions 	mli = LIST_FIRST(&mli_head);
1745*43a90889SApple OSS Distributions 
1746*43a90889SApple OSS Distributions 	while (mli != NULL) {
1747*43a90889SApple OSS Distributions 		struct in6_multistep step;
1748*43a90889SApple OSS Distributions 
1749*43a90889SApple OSS Distributions 		if (mli->mli_flags & MLIF_PROCESSED) {
1750*43a90889SApple OSS Distributions 			mli = LIST_NEXT(mli, mli_link);
1751*43a90889SApple OSS Distributions 			continue;
1752*43a90889SApple OSS Distributions 		}
1753*43a90889SApple OSS Distributions 
1754*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
1755*43a90889SApple OSS Distributions 		ifp = mli->mli_ifp;
1756*43a90889SApple OSS Distributions 		uri_sec = MLD_RANDOM_DELAY(mli->mli_uri);
1757*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
1758*43a90889SApple OSS Distributions 
1759*43a90889SApple OSS Distributions 		in6_multihead_lock_shared();
1760*43a90889SApple OSS Distributions 		IN6_FIRST_MULTI(step, inm);
1761*43a90889SApple OSS Distributions 		while (inm != NULL) {
1762*43a90889SApple OSS Distributions 			IN6M_LOCK(inm);
1763*43a90889SApple OSS Distributions 			if (inm->in6m_ifp != ifp) {
1764*43a90889SApple OSS Distributions 				goto next;
1765*43a90889SApple OSS Distributions 			}
1766*43a90889SApple OSS Distributions 
1767*43a90889SApple OSS Distributions 			MLI_LOCK(mli);
1768*43a90889SApple OSS Distributions 			switch (mli->mli_version) {
1769*43a90889SApple OSS Distributions 			case MLD_VERSION_1:
1770*43a90889SApple OSS Distributions 				mld_v1_process_group_timer(inm,
1771*43a90889SApple OSS Distributions 				    mli->mli_version);
1772*43a90889SApple OSS Distributions 				break;
1773*43a90889SApple OSS Distributions 			case MLD_VERSION_2:
1774*43a90889SApple OSS Distributions 				mld_v2_process_group_timers(mli, &qrq,
1775*43a90889SApple OSS Distributions 				    &scq, inm, uri_sec);
1776*43a90889SApple OSS Distributions 				break;
1777*43a90889SApple OSS Distributions 			}
1778*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
1779*43a90889SApple OSS Distributions next:
1780*43a90889SApple OSS Distributions 			IN6M_UNLOCK(inm);
1781*43a90889SApple OSS Distributions 			IN6_NEXT_MULTI(step, inm);
1782*43a90889SApple OSS Distributions 		}
1783*43a90889SApple OSS Distributions 		in6_multihead_lock_done();
1784*43a90889SApple OSS Distributions 
1785*43a90889SApple OSS Distributions 		/*
1786*43a90889SApple OSS Distributions 		 * XXX The logic below ends up calling
1787*43a90889SApple OSS Distributions 		 * mld_dispatch_packet which can unlock mli
1788*43a90889SApple OSS Distributions 		 * and the global MLD lock.
1789*43a90889SApple OSS Distributions 		 * Therefore grab a reference on MLI and also
1790*43a90889SApple OSS Distributions 		 * check for generation count to see if we should
1791*43a90889SApple OSS Distributions 		 * iterate the list again.
1792*43a90889SApple OSS Distributions 		 */
1793*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
1794*43a90889SApple OSS Distributions 		MLI_ADDREF_LOCKED(mli);
1795*43a90889SApple OSS Distributions 		if (mli->mli_version == MLD_VERSION_1) {
1796*43a90889SApple OSS Distributions 			mld_dispatch_queue_locked(mli, &mli->mli_v1q, 0);
1797*43a90889SApple OSS Distributions 		} else if (mli->mli_version == MLD_VERSION_2) {
1798*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
1799*43a90889SApple OSS Distributions 			mld_dispatch_queue_locked(NULL, &qrq, 0);
1800*43a90889SApple OSS Distributions 			mld_dispatch_queue_locked(NULL, &scq, 0);
1801*43a90889SApple OSS Distributions 			VERIFY(qrq.ifq_len == 0);
1802*43a90889SApple OSS Distributions 			VERIFY(scq.ifq_len == 0);
1803*43a90889SApple OSS Distributions 			MLI_LOCK(mli);
1804*43a90889SApple OSS Distributions 		}
1805*43a90889SApple OSS Distributions 		/*
1806*43a90889SApple OSS Distributions 		 * In case there are still any pending membership reports
1807*43a90889SApple OSS Distributions 		 * which didn't get drained at version change time.
1808*43a90889SApple OSS Distributions 		 */
1809*43a90889SApple OSS Distributions 		IF_DRAIN(&mli->mli_v1q);
1810*43a90889SApple OSS Distributions 		/*
1811*43a90889SApple OSS Distributions 		 * Release all deferred inm records, and drain any locally
1812*43a90889SApple OSS Distributions 		 * enqueued packets; do it even if the current MLD version
1813*43a90889SApple OSS Distributions 		 * for the link is no longer MLDv2, in order to handle the
1814*43a90889SApple OSS Distributions 		 * version change case.
1815*43a90889SApple OSS Distributions 		 */
1816*43a90889SApple OSS Distributions 		mld_flush_relq(mli, (struct mld_in6m_relhead *)&in6m_dthead);
1817*43a90889SApple OSS Distributions 		mli->mli_flags |= MLIF_PROCESSED;
1818*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
1819*43a90889SApple OSS Distributions 		MLI_REMREF(mli);
1820*43a90889SApple OSS Distributions 
1821*43a90889SApple OSS Distributions 		IF_DRAIN(&qrq);
1822*43a90889SApple OSS Distributions 		IF_DRAIN(&scq);
1823*43a90889SApple OSS Distributions 
1824*43a90889SApple OSS Distributions 		if (genid != mld_mli_list_genid) {
1825*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: MLD information list changed "
1826*43a90889SApple OSS Distributions 			    "in the middle of iteration! Restart iteration.\n",
1827*43a90889SApple OSS Distributions 			    __func__));
1828*43a90889SApple OSS Distributions 			mli = LIST_FIRST(&mli_head);
1829*43a90889SApple OSS Distributions 			genid = mld_mli_list_genid;
1830*43a90889SApple OSS Distributions 		} else {
1831*43a90889SApple OSS Distributions 			mli = LIST_NEXT(mli, mli_link);
1832*43a90889SApple OSS Distributions 		}
1833*43a90889SApple OSS Distributions 	}
1834*43a90889SApple OSS Distributions 
1835*43a90889SApple OSS Distributions 	LIST_FOREACH(mli, &mli_head, mli_link)
1836*43a90889SApple OSS Distributions 	mli->mli_flags &= ~MLIF_PROCESSED;
1837*43a90889SApple OSS Distributions 
1838*43a90889SApple OSS Distributions out_locked:
1839*43a90889SApple OSS Distributions 	/* re-arm the timer if there's work to do */
1840*43a90889SApple OSS Distributions 	if (fast) {
1841*43a90889SApple OSS Distributions 		mld_fast_timeout_run = false;
1842*43a90889SApple OSS Distributions 	} else {
1843*43a90889SApple OSS Distributions 		mld_timeout_run = false;
1844*43a90889SApple OSS Distributions 	}
1845*43a90889SApple OSS Distributions 	mld_sched_timeout();
1846*43a90889SApple OSS Distributions 	MLD_UNLOCK();
1847*43a90889SApple OSS Distributions 
1848*43a90889SApple OSS Distributions 	/* Now that we're dropped all locks, release detached records */
1849*43a90889SApple OSS Distributions 	MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
1850*43a90889SApple OSS Distributions }
1851*43a90889SApple OSS Distributions 
1852*43a90889SApple OSS Distributions static void
mld_sched_timeout(void)1853*43a90889SApple OSS Distributions mld_sched_timeout(void)
1854*43a90889SApple OSS Distributions {
1855*43a90889SApple OSS Distributions 	static thread_call_t mld_timeout_tcall;
1856*43a90889SApple OSS Distributions 	uint64_t deadline = 0, leeway = 0;
1857*43a90889SApple OSS Distributions 
1858*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_HELD();
1859*43a90889SApple OSS Distributions 	if (mld_timeout_tcall == NULL) {
1860*43a90889SApple OSS Distributions 		mld_timeout_tcall =
1861*43a90889SApple OSS Distributions 		    thread_call_allocate_with_options(mld_timeout,
1862*43a90889SApple OSS Distributions 		    NULL,
1863*43a90889SApple OSS Distributions 		    THREAD_CALL_PRIORITY_KERNEL,
1864*43a90889SApple OSS Distributions 		    THREAD_CALL_OPTIONS_ONCE);
1865*43a90889SApple OSS Distributions 	}
1866*43a90889SApple OSS Distributions 
1867*43a90889SApple OSS Distributions 	if (!mld_timeout_run &&
1868*43a90889SApple OSS Distributions 	    (querier_present_timers_running6 || current_state_timers_running6 ||
1869*43a90889SApple OSS Distributions 	    interface_timers_running6 || state_change_timers_running6)) {
1870*43a90889SApple OSS Distributions 		mld_timeout_run = true;
1871*43a90889SApple OSS Distributions 		clock_interval_to_deadline(mld_timeout_delay, NSEC_PER_MSEC,
1872*43a90889SApple OSS Distributions 		    &deadline);
1873*43a90889SApple OSS Distributions 		clock_interval_to_absolutetime_interval(mld_timeout_leeway,
1874*43a90889SApple OSS Distributions 		    NSEC_PER_MSEC, &leeway);
1875*43a90889SApple OSS Distributions 		thread_call_enter_delayed_with_leeway(mld_timeout_tcall, NULL,
1876*43a90889SApple OSS Distributions 		    deadline, leeway,
1877*43a90889SApple OSS Distributions 		    THREAD_CALL_DELAY_LEEWAY);
1878*43a90889SApple OSS Distributions 	}
1879*43a90889SApple OSS Distributions }
1880*43a90889SApple OSS Distributions 
1881*43a90889SApple OSS Distributions static void
mld_sched_fast_timeout(void)1882*43a90889SApple OSS Distributions mld_sched_fast_timeout(void)
1883*43a90889SApple OSS Distributions {
1884*43a90889SApple OSS Distributions 	static thread_call_t mld_fast_timeout_tcall;
1885*43a90889SApple OSS Distributions 
1886*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_HELD();
1887*43a90889SApple OSS Distributions 	if (mld_fast_timeout_tcall == NULL) {
1888*43a90889SApple OSS Distributions 		mld_fast_timeout_tcall =
1889*43a90889SApple OSS Distributions 		    thread_call_allocate_with_options(mld_timeout,
1890*43a90889SApple OSS Distributions 		    mld_sched_fast_timeout,
1891*43a90889SApple OSS Distributions 		    THREAD_CALL_PRIORITY_KERNEL,
1892*43a90889SApple OSS Distributions 		    THREAD_CALL_OPTIONS_ONCE);
1893*43a90889SApple OSS Distributions 	}
1894*43a90889SApple OSS Distributions 	if (!mld_fast_timeout_run &&
1895*43a90889SApple OSS Distributions 	    (current_state_timers_running6 || state_change_timers_running6)) {
1896*43a90889SApple OSS Distributions 		mld_fast_timeout_run = true;
1897*43a90889SApple OSS Distributions 		thread_call_enter(mld_fast_timeout_tcall);
1898*43a90889SApple OSS Distributions 	}
1899*43a90889SApple OSS Distributions }
1900*43a90889SApple OSS Distributions 
1901*43a90889SApple OSS Distributions /*
1902*43a90889SApple OSS Distributions  * Appends an in6_multi to the list to be released later.
1903*43a90889SApple OSS Distributions  *
1904*43a90889SApple OSS Distributions  * Caller must be holding mli_lock.
1905*43a90889SApple OSS Distributions  */
1906*43a90889SApple OSS Distributions static void
mld_append_relq(struct mld_ifinfo * mli,struct in6_multi * inm)1907*43a90889SApple OSS Distributions mld_append_relq(struct mld_ifinfo *mli, struct in6_multi *inm)
1908*43a90889SApple OSS Distributions {
1909*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
1910*43a90889SApple OSS Distributions 	if (inm->in6m_in_nrele) {
1911*43a90889SApple OSS Distributions 		os_log_debug(OS_LOG_DEFAULT, "%s: inm %llx already on relq ifp %s\n",
1912*43a90889SApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(inm),
1913*43a90889SApple OSS Distributions 		    mli->mli_ifp != NULL ? if_name(mli->mli_ifp) : "<null>");
1914*43a90889SApple OSS Distributions 		return;
1915*43a90889SApple OSS Distributions 	}
1916*43a90889SApple OSS Distributions 	os_log_debug(OS_LOG_DEFAULT, "%s: adding inm %llx on relq ifp %s\n",
1917*43a90889SApple OSS Distributions 	    __func__, (uint64_t)VM_KERNEL_ADDRPERM(inm),
1918*43a90889SApple OSS Distributions 	    mli->mli_ifp != NULL ? if_name(mli->mli_ifp) : "<null>");
1919*43a90889SApple OSS Distributions 	inm->in6m_in_nrele = true;
1920*43a90889SApple OSS Distributions 	SLIST_INSERT_HEAD(&mli->mli_relinmhead, inm, in6m_nrele);
1921*43a90889SApple OSS Distributions }
1922*43a90889SApple OSS Distributions 
1923*43a90889SApple OSS Distributions /*
1924*43a90889SApple OSS Distributions  * Free the in6_multi reference(s) for this MLD lifecycle.
1925*43a90889SApple OSS Distributions  *
1926*43a90889SApple OSS Distributions  * Caller must be holding mli_lock.
1927*43a90889SApple OSS Distributions  */
1928*43a90889SApple OSS Distributions static void
mld_flush_relq(struct mld_ifinfo * mli,struct mld_in6m_relhead * in6m_dthead)1929*43a90889SApple OSS Distributions mld_flush_relq(struct mld_ifinfo *mli, struct mld_in6m_relhead *in6m_dthead)
1930*43a90889SApple OSS Distributions {
1931*43a90889SApple OSS Distributions 	struct in6_multi *inm;
1932*43a90889SApple OSS Distributions 	SLIST_HEAD(, in6_multi) temp_relinmhead;
1933*43a90889SApple OSS Distributions 
1934*43a90889SApple OSS Distributions 	/*
1935*43a90889SApple OSS Distributions 	 * Before dropping the mli_lock, copy all the items in the
1936*43a90889SApple OSS Distributions 	 * release list to a temporary list to prevent other threads
1937*43a90889SApple OSS Distributions 	 * from changing mli_relinmhead while we are traversing it.
1938*43a90889SApple OSS Distributions 	 */
1939*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
1940*43a90889SApple OSS Distributions 	SLIST_INIT(&temp_relinmhead);
1941*43a90889SApple OSS Distributions 	while ((inm = SLIST_FIRST(&mli->mli_relinmhead)) != NULL) {
1942*43a90889SApple OSS Distributions 		SLIST_REMOVE_HEAD(&mli->mli_relinmhead, in6m_nrele);
1943*43a90889SApple OSS Distributions 		SLIST_INSERT_HEAD(&temp_relinmhead, inm, in6m_nrele);
1944*43a90889SApple OSS Distributions 	}
1945*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
1946*43a90889SApple OSS Distributions 	in6_multihead_lock_exclusive();
1947*43a90889SApple OSS Distributions 	while ((inm = SLIST_FIRST(&temp_relinmhead)) != NULL) {
1948*43a90889SApple OSS Distributions 		int lastref;
1949*43a90889SApple OSS Distributions 
1950*43a90889SApple OSS Distributions 		SLIST_REMOVE_HEAD(&temp_relinmhead, in6m_nrele);
1951*43a90889SApple OSS Distributions 		IN6M_LOCK(inm);
1952*43a90889SApple OSS Distributions 		os_log_debug(OS_LOG_DEFAULT, "%s: flushing inm %llx on relq ifp %s\n",
1953*43a90889SApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(inm),
1954*43a90889SApple OSS Distributions 		    inm->in6m_ifp != NULL ? if_name(inm->in6m_ifp) : "<null>");
1955*43a90889SApple OSS Distributions 		VERIFY(inm->in6m_in_nrele == true);
1956*43a90889SApple OSS Distributions 		inm->in6m_in_nrele = false;
1957*43a90889SApple OSS Distributions 		VERIFY(inm->in6m_nrelecnt != 0);
1958*43a90889SApple OSS Distributions 		inm->in6m_nrelecnt--;
1959*43a90889SApple OSS Distributions 		lastref = in6_multi_detach(inm);
1960*43a90889SApple OSS Distributions 		VERIFY(!lastref || (!(inm->in6m_debug & IFD_ATTACHED) &&
1961*43a90889SApple OSS Distributions 		    inm->in6m_reqcnt == 0));
1962*43a90889SApple OSS Distributions 		IN6M_UNLOCK(inm);
1963*43a90889SApple OSS Distributions 		/* from mli_relinmhead */
1964*43a90889SApple OSS Distributions 		IN6M_REMREF(inm);
1965*43a90889SApple OSS Distributions 		/* from in6_multihead_list */
1966*43a90889SApple OSS Distributions 		if (lastref) {
1967*43a90889SApple OSS Distributions 			/*
1968*43a90889SApple OSS Distributions 			 * Defer releasing our final reference, as we
1969*43a90889SApple OSS Distributions 			 * are holding the MLD lock at this point, and
1970*43a90889SApple OSS Distributions 			 * we could end up with locking issues later on
1971*43a90889SApple OSS Distributions 			 * (while issuing SIOCDELMULTI) when this is the
1972*43a90889SApple OSS Distributions 			 * final reference count.  Let the caller do it
1973*43a90889SApple OSS Distributions 			 * when it is safe.
1974*43a90889SApple OSS Distributions 			 */
1975*43a90889SApple OSS Distributions 			MLD_ADD_DETACHED_IN6M(in6m_dthead, inm);
1976*43a90889SApple OSS Distributions 		}
1977*43a90889SApple OSS Distributions 	}
1978*43a90889SApple OSS Distributions 	in6_multihead_lock_done();
1979*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
1980*43a90889SApple OSS Distributions }
1981*43a90889SApple OSS Distributions 
1982*43a90889SApple OSS Distributions /*
1983*43a90889SApple OSS Distributions  * Update host report group timer.
1984*43a90889SApple OSS Distributions  * Will update the global pending timer flags.
1985*43a90889SApple OSS Distributions  */
1986*43a90889SApple OSS Distributions static void
mld_v1_process_group_timer(struct in6_multi * inm,const int mld_version)1987*43a90889SApple OSS Distributions mld_v1_process_group_timer(struct in6_multi *inm, const int mld_version)
1988*43a90889SApple OSS Distributions {
1989*43a90889SApple OSS Distributions #pragma unused(mld_version)
1990*43a90889SApple OSS Distributions 	int report_timer_expired;
1991*43a90889SApple OSS Distributions 
1992*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_HELD();
1993*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
1994*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
1995*43a90889SApple OSS Distributions 
1996*43a90889SApple OSS Distributions 	if (inm->in6m_timer == 0) {
1997*43a90889SApple OSS Distributions 		report_timer_expired = 0;
1998*43a90889SApple OSS Distributions 	} else if (--inm->in6m_timer == 0) {
1999*43a90889SApple OSS Distributions 		report_timer_expired = 1;
2000*43a90889SApple OSS Distributions 	} else {
2001*43a90889SApple OSS Distributions 		current_state_timers_running6 = 1;
2002*43a90889SApple OSS Distributions 		/* caller will schedule timer */
2003*43a90889SApple OSS Distributions 		return;
2004*43a90889SApple OSS Distributions 	}
2005*43a90889SApple OSS Distributions 
2006*43a90889SApple OSS Distributions 	switch (inm->in6m_state) {
2007*43a90889SApple OSS Distributions 	case MLD_NOT_MEMBER:
2008*43a90889SApple OSS Distributions 	case MLD_SILENT_MEMBER:
2009*43a90889SApple OSS Distributions 	case MLD_IDLE_MEMBER:
2010*43a90889SApple OSS Distributions 	case MLD_LAZY_MEMBER:
2011*43a90889SApple OSS Distributions 	case MLD_SLEEPING_MEMBER:
2012*43a90889SApple OSS Distributions 	case MLD_AWAKENING_MEMBER:
2013*43a90889SApple OSS Distributions 		break;
2014*43a90889SApple OSS Distributions 	case MLD_REPORTING_MEMBER:
2015*43a90889SApple OSS Distributions 		if (report_timer_expired) {
2016*43a90889SApple OSS Distributions 			inm->in6m_state = MLD_IDLE_MEMBER;
2017*43a90889SApple OSS Distributions 			(void) mld_v1_transmit_report(inm,
2018*43a90889SApple OSS Distributions 			    MLD_LISTENER_REPORT);
2019*43a90889SApple OSS Distributions 			IN6M_LOCK_ASSERT_HELD(inm);
2020*43a90889SApple OSS Distributions 			MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
2021*43a90889SApple OSS Distributions 		}
2022*43a90889SApple OSS Distributions 		break;
2023*43a90889SApple OSS Distributions 	case MLD_G_QUERY_PENDING_MEMBER:
2024*43a90889SApple OSS Distributions 	case MLD_SG_QUERY_PENDING_MEMBER:
2025*43a90889SApple OSS Distributions 	case MLD_LEAVING_MEMBER:
2026*43a90889SApple OSS Distributions 		break;
2027*43a90889SApple OSS Distributions 	}
2028*43a90889SApple OSS Distributions }
2029*43a90889SApple OSS Distributions 
2030*43a90889SApple OSS Distributions /*
2031*43a90889SApple OSS Distributions  * Update a group's timers for MLDv2.
2032*43a90889SApple OSS Distributions  * Will update the global pending timer flags.
2033*43a90889SApple OSS Distributions  * Note: Unlocked read from mli.
2034*43a90889SApple OSS Distributions  */
2035*43a90889SApple OSS Distributions static void
mld_v2_process_group_timers(struct mld_ifinfo * mli,struct ifqueue * qrq,struct ifqueue * scq,struct in6_multi * inm,const int uri_sec)2036*43a90889SApple OSS Distributions mld_v2_process_group_timers(struct mld_ifinfo *mli,
2037*43a90889SApple OSS Distributions     struct ifqueue *qrq, struct ifqueue *scq,
2038*43a90889SApple OSS Distributions     struct in6_multi *inm, const int uri_sec)
2039*43a90889SApple OSS Distributions {
2040*43a90889SApple OSS Distributions 	int query_response_timer_expired;
2041*43a90889SApple OSS Distributions 	int state_change_retransmit_timer_expired;
2042*43a90889SApple OSS Distributions 
2043*43a90889SApple OSS Distributions 	MLD_LOCK_ASSERT_HELD();
2044*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
2045*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
2046*43a90889SApple OSS Distributions 	VERIFY(mli == inm->in6m_mli);
2047*43a90889SApple OSS Distributions 
2048*43a90889SApple OSS Distributions 	query_response_timer_expired = 0;
2049*43a90889SApple OSS Distributions 	state_change_retransmit_timer_expired = 0;
2050*43a90889SApple OSS Distributions 
2051*43a90889SApple OSS Distributions 	/*
2052*43a90889SApple OSS Distributions 	 * During a transition from compatibility mode back to MLDv2,
2053*43a90889SApple OSS Distributions 	 * a group record in REPORTING state may still have its group
2054*43a90889SApple OSS Distributions 	 * timer active. This is a no-op in this function; it is easier
2055*43a90889SApple OSS Distributions 	 * to deal with it here than to complicate the timeout path.
2056*43a90889SApple OSS Distributions 	 */
2057*43a90889SApple OSS Distributions 	if (inm->in6m_timer == 0) {
2058*43a90889SApple OSS Distributions 		query_response_timer_expired = 0;
2059*43a90889SApple OSS Distributions 	} else if (--inm->in6m_timer == 0) {
2060*43a90889SApple OSS Distributions 		query_response_timer_expired = 1;
2061*43a90889SApple OSS Distributions 	} else {
2062*43a90889SApple OSS Distributions 		current_state_timers_running6 = 1;
2063*43a90889SApple OSS Distributions 		/* caller will schedule timer */
2064*43a90889SApple OSS Distributions 	}
2065*43a90889SApple OSS Distributions 
2066*43a90889SApple OSS Distributions 	if (inm->in6m_sctimer == 0) {
2067*43a90889SApple OSS Distributions 		state_change_retransmit_timer_expired = 0;
2068*43a90889SApple OSS Distributions 	} else if (--inm->in6m_sctimer == 0) {
2069*43a90889SApple OSS Distributions 		state_change_retransmit_timer_expired = 1;
2070*43a90889SApple OSS Distributions 	} else {
2071*43a90889SApple OSS Distributions 		state_change_timers_running6 = 1;
2072*43a90889SApple OSS Distributions 		/* caller will schedule timer */
2073*43a90889SApple OSS Distributions 	}
2074*43a90889SApple OSS Distributions 
2075*43a90889SApple OSS Distributions 	/* We are in timer callback, so be quick about it. */
2076*43a90889SApple OSS Distributions 	if (!state_change_retransmit_timer_expired &&
2077*43a90889SApple OSS Distributions 	    !query_response_timer_expired) {
2078*43a90889SApple OSS Distributions 		return;
2079*43a90889SApple OSS Distributions 	}
2080*43a90889SApple OSS Distributions 
2081*43a90889SApple OSS Distributions 	switch (inm->in6m_state) {
2082*43a90889SApple OSS Distributions 	case MLD_NOT_MEMBER:
2083*43a90889SApple OSS Distributions 	case MLD_SILENT_MEMBER:
2084*43a90889SApple OSS Distributions 	case MLD_SLEEPING_MEMBER:
2085*43a90889SApple OSS Distributions 	case MLD_LAZY_MEMBER:
2086*43a90889SApple OSS Distributions 	case MLD_AWAKENING_MEMBER:
2087*43a90889SApple OSS Distributions 	case MLD_IDLE_MEMBER:
2088*43a90889SApple OSS Distributions 		break;
2089*43a90889SApple OSS Distributions 	case MLD_G_QUERY_PENDING_MEMBER:
2090*43a90889SApple OSS Distributions 	case MLD_SG_QUERY_PENDING_MEMBER:
2091*43a90889SApple OSS Distributions 		/*
2092*43a90889SApple OSS Distributions 		 * Respond to a previously pending Group-Specific
2093*43a90889SApple OSS Distributions 		 * or Group-and-Source-Specific query by enqueueing
2094*43a90889SApple OSS Distributions 		 * the appropriate Current-State report for
2095*43a90889SApple OSS Distributions 		 * immediate transmission.
2096*43a90889SApple OSS Distributions 		 */
2097*43a90889SApple OSS Distributions 		if (query_response_timer_expired) {
2098*43a90889SApple OSS Distributions 			int retval;
2099*43a90889SApple OSS Distributions 
2100*43a90889SApple OSS Distributions 			retval = mld_v2_enqueue_group_record(qrq, inm, 0, 1,
2101*43a90889SApple OSS Distributions 			    (inm->in6m_state == MLD_SG_QUERY_PENDING_MEMBER),
2102*43a90889SApple OSS Distributions 			    0);
2103*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: enqueue record = %d\n",
2104*43a90889SApple OSS Distributions 			    __func__, retval));
2105*43a90889SApple OSS Distributions 			inm->in6m_state = MLD_REPORTING_MEMBER;
2106*43a90889SApple OSS Distributions 			in6m_clear_recorded(inm);
2107*43a90889SApple OSS Distributions 		}
2108*43a90889SApple OSS Distributions 		OS_FALLTHROUGH;
2109*43a90889SApple OSS Distributions 	case MLD_REPORTING_MEMBER:
2110*43a90889SApple OSS Distributions 	case MLD_LEAVING_MEMBER:
2111*43a90889SApple OSS Distributions 		if (state_change_retransmit_timer_expired) {
2112*43a90889SApple OSS Distributions 			/*
2113*43a90889SApple OSS Distributions 			 * State-change retransmission timer fired.
2114*43a90889SApple OSS Distributions 			 * If there are any further pending retransmissions,
2115*43a90889SApple OSS Distributions 			 * set the global pending state-change flag, and
2116*43a90889SApple OSS Distributions 			 * reset the timer.
2117*43a90889SApple OSS Distributions 			 */
2118*43a90889SApple OSS Distributions 			if (--inm->in6m_scrv > 0) {
2119*43a90889SApple OSS Distributions 				inm->in6m_sctimer = (uint16_t)uri_sec;
2120*43a90889SApple OSS Distributions 				state_change_timers_running6 = 1;
2121*43a90889SApple OSS Distributions 				/* caller will schedule timer */
2122*43a90889SApple OSS Distributions 			}
2123*43a90889SApple OSS Distributions 			/*
2124*43a90889SApple OSS Distributions 			 * Retransmit the previously computed state-change
2125*43a90889SApple OSS Distributions 			 * report. If there are no further pending
2126*43a90889SApple OSS Distributions 			 * retransmissions, the mbuf queue will be consumed.
2127*43a90889SApple OSS Distributions 			 * Update T0 state to T1 as we have now sent
2128*43a90889SApple OSS Distributions 			 * a state-change.
2129*43a90889SApple OSS Distributions 			 */
2130*43a90889SApple OSS Distributions 			(void) mld_v2_merge_state_changes(inm, scq);
2131*43a90889SApple OSS Distributions 
2132*43a90889SApple OSS Distributions 			in6m_commit(inm);
2133*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2134*43a90889SApple OSS Distributions 			    ip6_sprintf(&inm->in6m_addr),
2135*43a90889SApple OSS Distributions 			    if_name(inm->in6m_ifp)));
2136*43a90889SApple OSS Distributions 
2137*43a90889SApple OSS Distributions 			/*
2138*43a90889SApple OSS Distributions 			 * If we are leaving the group for good, make sure
2139*43a90889SApple OSS Distributions 			 * we release MLD's reference to it.
2140*43a90889SApple OSS Distributions 			 * This release must be deferred using a SLIST,
2141*43a90889SApple OSS Distributions 			 * as we are called from a loop which traverses
2142*43a90889SApple OSS Distributions 			 * the in_ifmultiaddr TAILQ.
2143*43a90889SApple OSS Distributions 			 */
2144*43a90889SApple OSS Distributions 			if (inm->in6m_state == MLD_LEAVING_MEMBER &&
2145*43a90889SApple OSS Distributions 			    inm->in6m_scrv == 0) {
2146*43a90889SApple OSS Distributions 				inm->in6m_state = MLD_NOT_MEMBER;
2147*43a90889SApple OSS Distributions 				/*
2148*43a90889SApple OSS Distributions 				 * A reference has already been held in
2149*43a90889SApple OSS Distributions 				 * mld_final_leave() for this inm, so
2150*43a90889SApple OSS Distributions 				 * no need to hold another one.  We also
2151*43a90889SApple OSS Distributions 				 * bumped up its request count then, so
2152*43a90889SApple OSS Distributions 				 * that it stays in in6_multihead.  Both
2153*43a90889SApple OSS Distributions 				 * of them will be released when it is
2154*43a90889SApple OSS Distributions 				 * dequeued later on.
2155*43a90889SApple OSS Distributions 				 */
2156*43a90889SApple OSS Distributions 				VERIFY(inm->in6m_nrelecnt != 0);
2157*43a90889SApple OSS Distributions 				mld_append_relq(mli, inm);
2158*43a90889SApple OSS Distributions 			}
2159*43a90889SApple OSS Distributions 		}
2160*43a90889SApple OSS Distributions 		break;
2161*43a90889SApple OSS Distributions 	}
2162*43a90889SApple OSS Distributions }
2163*43a90889SApple OSS Distributions 
2164*43a90889SApple OSS Distributions /*
2165*43a90889SApple OSS Distributions  * Switch to a different version on the given interface,
2166*43a90889SApple OSS Distributions  * as per Section 9.12.
2167*43a90889SApple OSS Distributions  */
2168*43a90889SApple OSS Distributions static uint32_t
mld_set_version(struct mld_ifinfo * mli,const int mld_version)2169*43a90889SApple OSS Distributions mld_set_version(struct mld_ifinfo *mli, const int mld_version)
2170*43a90889SApple OSS Distributions {
2171*43a90889SApple OSS Distributions 	int old_version_timer;
2172*43a90889SApple OSS Distributions 
2173*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
2174*43a90889SApple OSS Distributions 
2175*43a90889SApple OSS Distributions 	os_log(OS_LOG_DEFAULT, "%s: switching to v%d on ifp %s\n", __func__,
2176*43a90889SApple OSS Distributions 	    mld_version, if_name(mli->mli_ifp));
2177*43a90889SApple OSS Distributions 
2178*43a90889SApple OSS Distributions 	if (mld_version == MLD_VERSION_1) {
2179*43a90889SApple OSS Distributions 		/*
2180*43a90889SApple OSS Distributions 		 * Compute the "Older Version Querier Present" timer as per
2181*43a90889SApple OSS Distributions 		 * Section 9.12, in seconds.
2182*43a90889SApple OSS Distributions 		 */
2183*43a90889SApple OSS Distributions 		old_version_timer = (mli->mli_rv * mli->mli_qi) + mli->mli_qri;
2184*43a90889SApple OSS Distributions 		mli->mli_v1_timer = old_version_timer;
2185*43a90889SApple OSS Distributions 	}
2186*43a90889SApple OSS Distributions 
2187*43a90889SApple OSS Distributions 	if (mli->mli_v1_timer > 0 && mli->mli_version != MLD_VERSION_1) {
2188*43a90889SApple OSS Distributions 		mli->mli_version = MLD_VERSION_1;
2189*43a90889SApple OSS Distributions 		mld_v2_cancel_link_timers(mli);
2190*43a90889SApple OSS Distributions 	}
2191*43a90889SApple OSS Distributions 
2192*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
2193*43a90889SApple OSS Distributions 
2194*43a90889SApple OSS Distributions 	return mli->mli_v1_timer;
2195*43a90889SApple OSS Distributions }
2196*43a90889SApple OSS Distributions 
2197*43a90889SApple OSS Distributions /*
2198*43a90889SApple OSS Distributions  * Cancel pending MLDv2 timers for the given link and all groups
2199*43a90889SApple OSS Distributions  * joined on it; state-change, general-query, and group-query timers.
2200*43a90889SApple OSS Distributions  *
2201*43a90889SApple OSS Distributions  * Only ever called on a transition from v2 to Compatibility mode. Kill
2202*43a90889SApple OSS Distributions  * the timers stone dead (this may be expensive for large N groups), they
2203*43a90889SApple OSS Distributions  * will be restarted if Compatibility Mode deems that they must be due to
2204*43a90889SApple OSS Distributions  * query processing.
2205*43a90889SApple OSS Distributions  */
2206*43a90889SApple OSS Distributions static void
mld_v2_cancel_link_timers(struct mld_ifinfo * mli)2207*43a90889SApple OSS Distributions mld_v2_cancel_link_timers(struct mld_ifinfo *mli)
2208*43a90889SApple OSS Distributions {
2209*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
2210*43a90889SApple OSS Distributions 	struct in6_multi        *inm;
2211*43a90889SApple OSS Distributions 	struct in6_multistep    step;
2212*43a90889SApple OSS Distributions 
2213*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
2214*43a90889SApple OSS Distributions 
2215*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: cancel v2 timers on ifp 0x%llx(%s)\n", __func__,
2216*43a90889SApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(mli->mli_ifp), if_name(mli->mli_ifp)));
2217*43a90889SApple OSS Distributions 
2218*43a90889SApple OSS Distributions 	/*
2219*43a90889SApple OSS Distributions 	 * Stop the v2 General Query Response on this link stone dead.
2220*43a90889SApple OSS Distributions 	 * If timer is woken up due to interface_timers_running6,
2221*43a90889SApple OSS Distributions 	 * the flag will be cleared if there are no pending link timers.
2222*43a90889SApple OSS Distributions 	 */
2223*43a90889SApple OSS Distributions 	mli->mli_v2_timer = 0;
2224*43a90889SApple OSS Distributions 
2225*43a90889SApple OSS Distributions 	/*
2226*43a90889SApple OSS Distributions 	 * Now clear the current-state and state-change report timers
2227*43a90889SApple OSS Distributions 	 * for all memberships scoped to this link.
2228*43a90889SApple OSS Distributions 	 */
2229*43a90889SApple OSS Distributions 	ifp = mli->mli_ifp;
2230*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
2231*43a90889SApple OSS Distributions 
2232*43a90889SApple OSS Distributions 	in6_multihead_lock_shared();
2233*43a90889SApple OSS Distributions 	IN6_FIRST_MULTI(step, inm);
2234*43a90889SApple OSS Distributions 	while (inm != NULL) {
2235*43a90889SApple OSS Distributions 		IN6M_LOCK(inm);
2236*43a90889SApple OSS Distributions 		if (inm->in6m_ifp != ifp) {
2237*43a90889SApple OSS Distributions 			goto next;
2238*43a90889SApple OSS Distributions 		}
2239*43a90889SApple OSS Distributions 
2240*43a90889SApple OSS Distributions 		switch (inm->in6m_state) {
2241*43a90889SApple OSS Distributions 		case MLD_NOT_MEMBER:
2242*43a90889SApple OSS Distributions 		case MLD_SILENT_MEMBER:
2243*43a90889SApple OSS Distributions 		case MLD_IDLE_MEMBER:
2244*43a90889SApple OSS Distributions 		case MLD_LAZY_MEMBER:
2245*43a90889SApple OSS Distributions 		case MLD_SLEEPING_MEMBER:
2246*43a90889SApple OSS Distributions 		case MLD_AWAKENING_MEMBER:
2247*43a90889SApple OSS Distributions 			/*
2248*43a90889SApple OSS Distributions 			 * These states are either not relevant in v2 mode,
2249*43a90889SApple OSS Distributions 			 * or are unreported. Do nothing.
2250*43a90889SApple OSS Distributions 			 */
2251*43a90889SApple OSS Distributions 			break;
2252*43a90889SApple OSS Distributions 		case MLD_LEAVING_MEMBER:
2253*43a90889SApple OSS Distributions 			/*
2254*43a90889SApple OSS Distributions 			 * If we are leaving the group and switching
2255*43a90889SApple OSS Distributions 			 * version, we need to release the final
2256*43a90889SApple OSS Distributions 			 * reference held for issuing the INCLUDE {}.
2257*43a90889SApple OSS Distributions 			 * During mld_final_leave(), we bumped up both the
2258*43a90889SApple OSS Distributions 			 * request and reference counts.  Since we cannot
2259*43a90889SApple OSS Distributions 			 * call in6_multi_detach() here, defer this task to
2260*43a90889SApple OSS Distributions 			 * the timer routine.
2261*43a90889SApple OSS Distributions 			 */
2262*43a90889SApple OSS Distributions 			VERIFY(inm->in6m_nrelecnt != 0);
2263*43a90889SApple OSS Distributions 			MLI_LOCK(mli);
2264*43a90889SApple OSS Distributions 			mld_append_relq(mli, inm);
2265*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
2266*43a90889SApple OSS Distributions 			OS_FALLTHROUGH;
2267*43a90889SApple OSS Distributions 		case MLD_G_QUERY_PENDING_MEMBER:
2268*43a90889SApple OSS Distributions 		case MLD_SG_QUERY_PENDING_MEMBER:
2269*43a90889SApple OSS Distributions 			in6m_clear_recorded(inm);
2270*43a90889SApple OSS Distributions 			OS_FALLTHROUGH;
2271*43a90889SApple OSS Distributions 		case MLD_REPORTING_MEMBER:
2272*43a90889SApple OSS Distributions 			inm->in6m_state = MLD_REPORTING_MEMBER;
2273*43a90889SApple OSS Distributions 			break;
2274*43a90889SApple OSS Distributions 		}
2275*43a90889SApple OSS Distributions 		/*
2276*43a90889SApple OSS Distributions 		 * Always clear state-change and group report timers.
2277*43a90889SApple OSS Distributions 		 * Free any pending MLDv2 state-change records.
2278*43a90889SApple OSS Distributions 		 */
2279*43a90889SApple OSS Distributions 		inm->in6m_sctimer = 0;
2280*43a90889SApple OSS Distributions 		inm->in6m_timer = 0;
2281*43a90889SApple OSS Distributions 		IF_DRAIN(&inm->in6m_scq);
2282*43a90889SApple OSS Distributions next:
2283*43a90889SApple OSS Distributions 		IN6M_UNLOCK(inm);
2284*43a90889SApple OSS Distributions 		IN6_NEXT_MULTI(step, inm);
2285*43a90889SApple OSS Distributions 	}
2286*43a90889SApple OSS Distributions 	in6_multihead_lock_done();
2287*43a90889SApple OSS Distributions 
2288*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
2289*43a90889SApple OSS Distributions }
2290*43a90889SApple OSS Distributions 
2291*43a90889SApple OSS Distributions /*
2292*43a90889SApple OSS Distributions  * Update the Older Version Querier Present timers for a link.
2293*43a90889SApple OSS Distributions  * See Section 9.12 of RFC 3810.
2294*43a90889SApple OSS Distributions  */
2295*43a90889SApple OSS Distributions static void
mld_v1_process_querier_timers(struct mld_ifinfo * mli)2296*43a90889SApple OSS Distributions mld_v1_process_querier_timers(struct mld_ifinfo *mli)
2297*43a90889SApple OSS Distributions {
2298*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
2299*43a90889SApple OSS Distributions 
2300*43a90889SApple OSS Distributions 	if (mld_v2enable && mli->mli_version != MLD_VERSION_2 &&
2301*43a90889SApple OSS Distributions 	    --mli->mli_v1_timer == 0) {
2302*43a90889SApple OSS Distributions 		/*
2303*43a90889SApple OSS Distributions 		 * MLDv1 Querier Present timer expired; revert to MLDv2.
2304*43a90889SApple OSS Distributions 		 */
2305*43a90889SApple OSS Distributions 		os_log(OS_LOG_DEFAULT, "%s: transition from v%d -> v%d on %s\n",
2306*43a90889SApple OSS Distributions 		    __func__, mli->mli_version, MLD_VERSION_2,
2307*43a90889SApple OSS Distributions 		    if_name(mli->mli_ifp));
2308*43a90889SApple OSS Distributions 		mli->mli_version = MLD_VERSION_2;
2309*43a90889SApple OSS Distributions 	}
2310*43a90889SApple OSS Distributions }
2311*43a90889SApple OSS Distributions 
2312*43a90889SApple OSS Distributions /*
2313*43a90889SApple OSS Distributions  * Transmit an MLDv1 report immediately.
2314*43a90889SApple OSS Distributions  */
2315*43a90889SApple OSS Distributions static int
mld_v1_transmit_report(struct in6_multi * in6m,const uint8_t type)2316*43a90889SApple OSS Distributions mld_v1_transmit_report(struct in6_multi *in6m, const uint8_t type)
2317*43a90889SApple OSS Distributions {
2318*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
2319*43a90889SApple OSS Distributions 	struct in6_ifaddr       *ia;
2320*43a90889SApple OSS Distributions 	struct ip6_hdr          *ip6;
2321*43a90889SApple OSS Distributions 	struct mbuf             *mh, *md;
2322*43a90889SApple OSS Distributions 	struct mld_hdr          *mld;
2323*43a90889SApple OSS Distributions 	int                     error = 0;
2324*43a90889SApple OSS Distributions 
2325*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(in6m);
2326*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(in6m->in6m_mli);
2327*43a90889SApple OSS Distributions 
2328*43a90889SApple OSS Distributions 	ifp = in6m->in6m_ifp;
2329*43a90889SApple OSS Distributions 	/* ia may be NULL if link-local address is tentative. */
2330*43a90889SApple OSS Distributions 	ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
2331*43a90889SApple OSS Distributions 
2332*43a90889SApple OSS Distributions 	MGETHDR(mh, M_DONTWAIT, MT_HEADER);
2333*43a90889SApple OSS Distributions 	if (mh == NULL) {
2334*43a90889SApple OSS Distributions 		if (ia != NULL) {
2335*43a90889SApple OSS Distributions 			ifa_remref(&ia->ia_ifa);
2336*43a90889SApple OSS Distributions 		}
2337*43a90889SApple OSS Distributions 		return ENOMEM;
2338*43a90889SApple OSS Distributions 	}
2339*43a90889SApple OSS Distributions 	MGET(md, M_DONTWAIT, MT_DATA);
2340*43a90889SApple OSS Distributions 	if (md == NULL) {
2341*43a90889SApple OSS Distributions 		m_free(mh);
2342*43a90889SApple OSS Distributions 		if (ia != NULL) {
2343*43a90889SApple OSS Distributions 			ifa_remref(&ia->ia_ifa);
2344*43a90889SApple OSS Distributions 		}
2345*43a90889SApple OSS Distributions 		return ENOMEM;
2346*43a90889SApple OSS Distributions 	}
2347*43a90889SApple OSS Distributions 	mh->m_next = md;
2348*43a90889SApple OSS Distributions 
2349*43a90889SApple OSS Distributions 	/*
2350*43a90889SApple OSS Distributions 	 * FUTURE: Consider increasing alignment by ETHER_HDR_LEN, so
2351*43a90889SApple OSS Distributions 	 * that ether_output() does not need to allocate another mbuf
2352*43a90889SApple OSS Distributions 	 * for the header in the most common case.
2353*43a90889SApple OSS Distributions 	 */
2354*43a90889SApple OSS Distributions 	MH_ALIGN(mh, sizeof(struct ip6_hdr));
2355*43a90889SApple OSS Distributions 	mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
2356*43a90889SApple OSS Distributions 	mh->m_len = sizeof(struct ip6_hdr);
2357*43a90889SApple OSS Distributions 
2358*43a90889SApple OSS Distributions 	ip6 = mtod(mh, struct ip6_hdr *);
2359*43a90889SApple OSS Distributions 	ip6->ip6_flow = 0;
2360*43a90889SApple OSS Distributions 	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
2361*43a90889SApple OSS Distributions 	ip6->ip6_vfc |= IPV6_VERSION;
2362*43a90889SApple OSS Distributions 	ip6->ip6_nxt = IPPROTO_ICMPV6;
2363*43a90889SApple OSS Distributions 	if (ia != NULL) {
2364*43a90889SApple OSS Distributions 		IFA_LOCK(&ia->ia_ifa);
2365*43a90889SApple OSS Distributions 	}
2366*43a90889SApple OSS Distributions 	ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
2367*43a90889SApple OSS Distributions 	ip6_output_setsrcifscope(mh, IFSCOPE_NONE, ia);
2368*43a90889SApple OSS Distributions 	if (ia != NULL) {
2369*43a90889SApple OSS Distributions 		IFA_UNLOCK(&ia->ia_ifa);
2370*43a90889SApple OSS Distributions 		ifa_remref(&ia->ia_ifa);
2371*43a90889SApple OSS Distributions 		ia = NULL;
2372*43a90889SApple OSS Distributions 	}
2373*43a90889SApple OSS Distributions 	ip6->ip6_dst = in6m->in6m_addr;
2374*43a90889SApple OSS Distributions 	ip6_output_setdstifscope(mh, in6m->ifscope, NULL);
2375*43a90889SApple OSS Distributions 
2376*43a90889SApple OSS Distributions 	md->m_len = sizeof(struct mld_hdr);
2377*43a90889SApple OSS Distributions 	mld = mtod(md, struct mld_hdr *);
2378*43a90889SApple OSS Distributions 	mld->mld_type = type;
2379*43a90889SApple OSS Distributions 	mld->mld_code = 0;
2380*43a90889SApple OSS Distributions 	mld->mld_cksum = 0;
2381*43a90889SApple OSS Distributions 	mld->mld_maxdelay = 0;
2382*43a90889SApple OSS Distributions 	mld->mld_reserved = 0;
2383*43a90889SApple OSS Distributions 	mld->mld_addr = in6m->in6m_addr;
2384*43a90889SApple OSS Distributions 	in6_clearscope(&mld->mld_addr);
2385*43a90889SApple OSS Distributions 	mld->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6,
2386*43a90889SApple OSS Distributions 	    sizeof(struct ip6_hdr), sizeof(struct mld_hdr));
2387*43a90889SApple OSS Distributions 
2388*43a90889SApple OSS Distributions 	mld_save_context(mh, ifp);
2389*43a90889SApple OSS Distributions 	mh->m_flags |= M_MLDV1;
2390*43a90889SApple OSS Distributions 
2391*43a90889SApple OSS Distributions 	/*
2392*43a90889SApple OSS Distributions 	 * Due to the fact that at this point we are possibly holding
2393*43a90889SApple OSS Distributions 	 * in6_multihead_lock in shared or exclusive mode, we can't call
2394*43a90889SApple OSS Distributions 	 * mld_dispatch_packet() here since that will eventually call
2395*43a90889SApple OSS Distributions 	 * ip6_output(), which will try to lock in6_multihead_lock and cause
2396*43a90889SApple OSS Distributions 	 * a deadlock.
2397*43a90889SApple OSS Distributions 	 * Instead we defer the work to the mld_timeout() thread, thus
2398*43a90889SApple OSS Distributions 	 * avoiding unlocking in_multihead_lock here.
2399*43a90889SApple OSS Distributions 	 */
2400*43a90889SApple OSS Distributions 	if (IF_QFULL(&in6m->in6m_mli->mli_v1q)) {
2401*43a90889SApple OSS Distributions 		os_log_error(OS_LOG_DEFAULT, "%s: v1 outbound queue full\n", __func__);
2402*43a90889SApple OSS Distributions 		error = ENOMEM;
2403*43a90889SApple OSS Distributions 		m_freem(mh);
2404*43a90889SApple OSS Distributions 	} else {
2405*43a90889SApple OSS Distributions 		IF_ENQUEUE(&in6m->in6m_mli->mli_v1q, mh);
2406*43a90889SApple OSS Distributions 		VERIFY(error == 0);
2407*43a90889SApple OSS Distributions 	}
2408*43a90889SApple OSS Distributions 
2409*43a90889SApple OSS Distributions 	return error;
2410*43a90889SApple OSS Distributions }
2411*43a90889SApple OSS Distributions 
2412*43a90889SApple OSS Distributions /*
2413*43a90889SApple OSS Distributions  * Process a state change from the upper layer for the given IPv6 group.
2414*43a90889SApple OSS Distributions  *
2415*43a90889SApple OSS Distributions  * Each socket holds a reference on the in6_multi in its own ip_moptions.
2416*43a90889SApple OSS Distributions  * The socket layer will have made the necessary updates to.the group
2417*43a90889SApple OSS Distributions  * state, it is now up to MLD to issue a state change report if there
2418*43a90889SApple OSS Distributions  * has been any change between T0 (when the last state-change was issued)
2419*43a90889SApple OSS Distributions  * and T1 (now).
2420*43a90889SApple OSS Distributions  *
2421*43a90889SApple OSS Distributions  * We use the MLDv2 state machine at group level. The MLd module
2422*43a90889SApple OSS Distributions  * however makes the decision as to which MLD protocol version to speak.
2423*43a90889SApple OSS Distributions  * A state change *from* INCLUDE {} always means an initial join.
2424*43a90889SApple OSS Distributions  * A state change *to* INCLUDE {} always means a final leave.
2425*43a90889SApple OSS Distributions  *
2426*43a90889SApple OSS Distributions  * If delay is non-zero, and the state change is an initial multicast
2427*43a90889SApple OSS Distributions  * join, the state change report will be delayed by 'delay' ticks
2428*43a90889SApple OSS Distributions  * in units of seconds if MLDv1 is active on the link; otherwise
2429*43a90889SApple OSS Distributions  * the initial MLDv2 state change report will be delayed by whichever
2430*43a90889SApple OSS Distributions  * is sooner, a pending state-change timer or delay itself.
2431*43a90889SApple OSS Distributions  */
2432*43a90889SApple OSS Distributions int
mld_change_state(struct in6_multi * inm,struct mld_tparams * mtp,const int delay)2433*43a90889SApple OSS Distributions mld_change_state(struct in6_multi *inm, struct mld_tparams *mtp,
2434*43a90889SApple OSS Distributions     const int delay)
2435*43a90889SApple OSS Distributions {
2436*43a90889SApple OSS Distributions 	struct mld_ifinfo *mli;
2437*43a90889SApple OSS Distributions 	struct ifnet *ifp;
2438*43a90889SApple OSS Distributions 	int error = 0;
2439*43a90889SApple OSS Distributions 
2440*43a90889SApple OSS Distributions 	VERIFY(mtp != NULL);
2441*43a90889SApple OSS Distributions 	bzero(mtp, sizeof(*mtp));
2442*43a90889SApple OSS Distributions 
2443*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
2444*43a90889SApple OSS Distributions 	VERIFY(inm->in6m_mli != NULL);
2445*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_NOTHELD(inm->in6m_mli);
2446*43a90889SApple OSS Distributions 
2447*43a90889SApple OSS Distributions 	/*
2448*43a90889SApple OSS Distributions 	 * Try to detect if the upper layer just asked us to change state
2449*43a90889SApple OSS Distributions 	 * for an interface which has now gone away.
2450*43a90889SApple OSS Distributions 	 */
2451*43a90889SApple OSS Distributions 	VERIFY(inm->in6m_ifma != NULL);
2452*43a90889SApple OSS Distributions 	ifp = inm->in6m_ifma->ifma_ifp;
2453*43a90889SApple OSS Distributions 	/*
2454*43a90889SApple OSS Distributions 	 * Sanity check that netinet6's notion of ifp is the same as net's.
2455*43a90889SApple OSS Distributions 	 */
2456*43a90889SApple OSS Distributions 	VERIFY(inm->in6m_ifp == ifp);
2457*43a90889SApple OSS Distributions 
2458*43a90889SApple OSS Distributions 	mli = MLD_IFINFO(ifp);
2459*43a90889SApple OSS Distributions 	VERIFY(mli != NULL);
2460*43a90889SApple OSS Distributions 
2461*43a90889SApple OSS Distributions 	/*
2462*43a90889SApple OSS Distributions 	 * If we detect a state transition to or from MCAST_UNDEFINED
2463*43a90889SApple OSS Distributions 	 * for this group, then we are starting or finishing an MLD
2464*43a90889SApple OSS Distributions 	 * life cycle for this group.
2465*43a90889SApple OSS Distributions 	 */
2466*43a90889SApple OSS Distributions 	if (inm->in6m_st[1].iss_fmode != inm->in6m_st[0].iss_fmode) {
2467*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: inm transition %d -> %d\n", __func__,
2468*43a90889SApple OSS Distributions 		    inm->in6m_st[0].iss_fmode, inm->in6m_st[1].iss_fmode));
2469*43a90889SApple OSS Distributions 		if (inm->in6m_st[0].iss_fmode == MCAST_UNDEFINED) {
2470*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: initial join\n", __func__));
2471*43a90889SApple OSS Distributions 			error = mld_initial_join(inm, mli, mtp, delay);
2472*43a90889SApple OSS Distributions 			goto out;
2473*43a90889SApple OSS Distributions 		} else if (inm->in6m_st[1].iss_fmode == MCAST_UNDEFINED) {
2474*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: final leave\n", __func__));
2475*43a90889SApple OSS Distributions 			mld_final_leave(inm, mli, mtp);
2476*43a90889SApple OSS Distributions 			goto out;
2477*43a90889SApple OSS Distributions 		}
2478*43a90889SApple OSS Distributions 	} else {
2479*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: filter set change\n", __func__));
2480*43a90889SApple OSS Distributions 	}
2481*43a90889SApple OSS Distributions 
2482*43a90889SApple OSS Distributions 	error = mld_handle_state_change(inm, mli, mtp);
2483*43a90889SApple OSS Distributions out:
2484*43a90889SApple OSS Distributions 	return error;
2485*43a90889SApple OSS Distributions }
2486*43a90889SApple OSS Distributions 
2487*43a90889SApple OSS Distributions /*
2488*43a90889SApple OSS Distributions  * Perform the initial join for an MLD group.
2489*43a90889SApple OSS Distributions  *
2490*43a90889SApple OSS Distributions  * When joining a group:
2491*43a90889SApple OSS Distributions  *  If the group should have its MLD traffic suppressed, do nothing.
2492*43a90889SApple OSS Distributions  *  MLDv1 starts sending MLDv1 host membership reports.
2493*43a90889SApple OSS Distributions  *  MLDv2 will schedule an MLDv2 state-change report containing the
2494*43a90889SApple OSS Distributions  *  initial state of the membership.
2495*43a90889SApple OSS Distributions  *
2496*43a90889SApple OSS Distributions  * If the delay argument is non-zero, then we must delay sending the
2497*43a90889SApple OSS Distributions  * initial state change for delay ticks (in units of seconds).
2498*43a90889SApple OSS Distributions  */
2499*43a90889SApple OSS Distributions static int
mld_initial_join(struct in6_multi * inm,struct mld_ifinfo * mli,struct mld_tparams * mtp,const int delay)2500*43a90889SApple OSS Distributions mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
2501*43a90889SApple OSS Distributions     struct mld_tparams *mtp, const int delay)
2502*43a90889SApple OSS Distributions {
2503*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
2504*43a90889SApple OSS Distributions 	struct ifqueue          *ifq;
2505*43a90889SApple OSS Distributions 	int                      error, retval, syncstates;
2506*43a90889SApple OSS Distributions 	int                      odelay;
2507*43a90889SApple OSS Distributions 
2508*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
2509*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_NOTHELD(mli);
2510*43a90889SApple OSS Distributions 	VERIFY(mtp != NULL);
2511*43a90889SApple OSS Distributions 
2512*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: initial join %s on ifp 0x%llx(%s)\n",
2513*43a90889SApple OSS Distributions 	    __func__, ip6_sprintf(&inm->in6m_addr),
2514*43a90889SApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
2515*43a90889SApple OSS Distributions 	    if_name(inm->in6m_ifp)));
2516*43a90889SApple OSS Distributions 
2517*43a90889SApple OSS Distributions 	error = 0;
2518*43a90889SApple OSS Distributions 	syncstates = 1;
2519*43a90889SApple OSS Distributions 
2520*43a90889SApple OSS Distributions 	ifp = inm->in6m_ifp;
2521*43a90889SApple OSS Distributions 
2522*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
2523*43a90889SApple OSS Distributions 	VERIFY(mli->mli_ifp == ifp);
2524*43a90889SApple OSS Distributions 
2525*43a90889SApple OSS Distributions 	/*
2526*43a90889SApple OSS Distributions 	 * Avoid MLD if group is :
2527*43a90889SApple OSS Distributions 	 * 1. Joined on loopback, OR
2528*43a90889SApple OSS Distributions 	 * 2. On a link that is marked MLIF_SILENT
2529*43a90889SApple OSS Distributions 	 * 3. rdar://problem/19227650 Is link local scoped and
2530*43a90889SApple OSS Distributions 	 *    on cellular interface
2531*43a90889SApple OSS Distributions 	 * 4. Is a type that should not be reported (node local
2532*43a90889SApple OSS Distributions 	 *    or all node link local multicast.
2533*43a90889SApple OSS Distributions 	 * All other groups enter the appropriate state machine
2534*43a90889SApple OSS Distributions 	 * for the version in use on this link.
2535*43a90889SApple OSS Distributions 	 */
2536*43a90889SApple OSS Distributions 	if ((ifp->if_flags & IFF_LOOPBACK) ||
2537*43a90889SApple OSS Distributions 	    (mli->mli_flags & MLIF_SILENT) ||
2538*43a90889SApple OSS Distributions 	    (IFNET_IS_CELLULAR(ifp) &&
2539*43a90889SApple OSS Distributions 	    (IN6_IS_ADDR_MC_LINKLOCAL(&inm->in6m_addr) || IN6_IS_ADDR_MC_UNICAST_BASED_LINKLOCAL(&inm->in6m_addr))) ||
2540*43a90889SApple OSS Distributions 	    !mld_is_addr_reported(&inm->in6m_addr)) {
2541*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: not kicking state machine for silent group\n",
2542*43a90889SApple OSS Distributions 		    __func__));
2543*43a90889SApple OSS Distributions 		inm->in6m_state = MLD_SILENT_MEMBER;
2544*43a90889SApple OSS Distributions 		inm->in6m_timer = 0;
2545*43a90889SApple OSS Distributions 	} else {
2546*43a90889SApple OSS Distributions 		/*
2547*43a90889SApple OSS Distributions 		 * Deal with overlapping in6_multi lifecycle.
2548*43a90889SApple OSS Distributions 		 * If this group was LEAVING, then make sure
2549*43a90889SApple OSS Distributions 		 * we drop the reference we picked up to keep the
2550*43a90889SApple OSS Distributions 		 * group around for the final INCLUDE {} enqueue.
2551*43a90889SApple OSS Distributions 		 * Since we cannot call in6_multi_detach() here,
2552*43a90889SApple OSS Distributions 		 * defer this task to the timer routine.
2553*43a90889SApple OSS Distributions 		 */
2554*43a90889SApple OSS Distributions 		if (mli->mli_version == MLD_VERSION_2 &&
2555*43a90889SApple OSS Distributions 		    inm->in6m_state == MLD_LEAVING_MEMBER) {
2556*43a90889SApple OSS Distributions 			VERIFY(inm->in6m_nrelecnt != 0);
2557*43a90889SApple OSS Distributions 			mld_append_relq(mli, inm);
2558*43a90889SApple OSS Distributions 		}
2559*43a90889SApple OSS Distributions 
2560*43a90889SApple OSS Distributions 		inm->in6m_state = MLD_REPORTING_MEMBER;
2561*43a90889SApple OSS Distributions 
2562*43a90889SApple OSS Distributions 		switch (mli->mli_version) {
2563*43a90889SApple OSS Distributions 		case MLD_VERSION_1:
2564*43a90889SApple OSS Distributions 			/*
2565*43a90889SApple OSS Distributions 			 * If a delay was provided, only use it if
2566*43a90889SApple OSS Distributions 			 * it is greater than the delay normally
2567*43a90889SApple OSS Distributions 			 * used for an MLDv1 state change report,
2568*43a90889SApple OSS Distributions 			 * and delay sending the initial MLDv1 report
2569*43a90889SApple OSS Distributions 			 * by not transitioning to the IDLE state.
2570*43a90889SApple OSS Distributions 			 */
2571*43a90889SApple OSS Distributions 			odelay = MLD_RANDOM_DELAY(MLD_V1_MAX_RI);
2572*43a90889SApple OSS Distributions 			if (delay) {
2573*43a90889SApple OSS Distributions 				inm->in6m_timer = max(delay, odelay);
2574*43a90889SApple OSS Distributions 				mtp->cst = 1;
2575*43a90889SApple OSS Distributions 			} else {
2576*43a90889SApple OSS Distributions 				inm->in6m_state = MLD_IDLE_MEMBER;
2577*43a90889SApple OSS Distributions 				error = mld_v1_transmit_report(inm,
2578*43a90889SApple OSS Distributions 				    MLD_LISTENER_REPORT);
2579*43a90889SApple OSS Distributions 
2580*43a90889SApple OSS Distributions 				IN6M_LOCK_ASSERT_HELD(inm);
2581*43a90889SApple OSS Distributions 				MLI_LOCK_ASSERT_HELD(mli);
2582*43a90889SApple OSS Distributions 
2583*43a90889SApple OSS Distributions 				if (error == 0) {
2584*43a90889SApple OSS Distributions 					inm->in6m_timer = odelay;
2585*43a90889SApple OSS Distributions 					mtp->cst = 1;
2586*43a90889SApple OSS Distributions 				}
2587*43a90889SApple OSS Distributions 			}
2588*43a90889SApple OSS Distributions 			break;
2589*43a90889SApple OSS Distributions 
2590*43a90889SApple OSS Distributions 		case MLD_VERSION_2:
2591*43a90889SApple OSS Distributions 			/*
2592*43a90889SApple OSS Distributions 			 * Defer update of T0 to T1, until the first copy
2593*43a90889SApple OSS Distributions 			 * of the state change has been transmitted.
2594*43a90889SApple OSS Distributions 			 */
2595*43a90889SApple OSS Distributions 			syncstates = 0;
2596*43a90889SApple OSS Distributions 
2597*43a90889SApple OSS Distributions 			/*
2598*43a90889SApple OSS Distributions 			 * Immediately enqueue a State-Change Report for
2599*43a90889SApple OSS Distributions 			 * this interface, freeing any previous reports.
2600*43a90889SApple OSS Distributions 			 * Don't kick the timers if there is nothing to do,
2601*43a90889SApple OSS Distributions 			 * or if an error occurred.
2602*43a90889SApple OSS Distributions 			 */
2603*43a90889SApple OSS Distributions 			ifq = &inm->in6m_scq;
2604*43a90889SApple OSS Distributions 			IF_DRAIN(ifq);
2605*43a90889SApple OSS Distributions 			retval = mld_v2_enqueue_group_record(ifq, inm, 1,
2606*43a90889SApple OSS Distributions 			    0, 0, (mli->mli_flags & MLIF_USEALLOW));
2607*43a90889SApple OSS Distributions 			mtp->cst = (ifq->ifq_len > 0);
2608*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: enqueue record = %d\n",
2609*43a90889SApple OSS Distributions 			    __func__, retval));
2610*43a90889SApple OSS Distributions 			if (retval <= 0) {
2611*43a90889SApple OSS Distributions 				error = retval * -1;
2612*43a90889SApple OSS Distributions 				break;
2613*43a90889SApple OSS Distributions 			}
2614*43a90889SApple OSS Distributions 
2615*43a90889SApple OSS Distributions 			/*
2616*43a90889SApple OSS Distributions 			 * Schedule transmission of pending state-change
2617*43a90889SApple OSS Distributions 			 * report up to RV times for this link. The timer
2618*43a90889SApple OSS Distributions 			 * will fire at the next mld_timeout (1 second)),
2619*43a90889SApple OSS Distributions 			 * giving us an opportunity to merge the reports.
2620*43a90889SApple OSS Distributions 			 *
2621*43a90889SApple OSS Distributions 			 * If a delay was provided to this function, only
2622*43a90889SApple OSS Distributions 			 * use this delay if sooner than the existing one.
2623*43a90889SApple OSS Distributions 			 */
2624*43a90889SApple OSS Distributions 			VERIFY(mli->mli_rv > 1);
2625*43a90889SApple OSS Distributions 			inm->in6m_scrv = (uint16_t)mli->mli_rv;
2626*43a90889SApple OSS Distributions 			if (delay) {
2627*43a90889SApple OSS Distributions 				if (inm->in6m_sctimer > 1) {
2628*43a90889SApple OSS Distributions 					inm->in6m_sctimer =
2629*43a90889SApple OSS Distributions 					    MIN(inm->in6m_sctimer, (uint16_t)delay);
2630*43a90889SApple OSS Distributions 				} else {
2631*43a90889SApple OSS Distributions 					inm->in6m_sctimer = (uint16_t)delay;
2632*43a90889SApple OSS Distributions 				}
2633*43a90889SApple OSS Distributions 			} else {
2634*43a90889SApple OSS Distributions 				inm->in6m_sctimer = 1;
2635*43a90889SApple OSS Distributions 			}
2636*43a90889SApple OSS Distributions 			mtp->sct = 1;
2637*43a90889SApple OSS Distributions 			error = 0;
2638*43a90889SApple OSS Distributions 			break;
2639*43a90889SApple OSS Distributions 		}
2640*43a90889SApple OSS Distributions 	}
2641*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
2642*43a90889SApple OSS Distributions 
2643*43a90889SApple OSS Distributions 	/*
2644*43a90889SApple OSS Distributions 	 * Only update the T0 state if state change is atomic,
2645*43a90889SApple OSS Distributions 	 * i.e. we don't need to wait for a timer to fire before we
2646*43a90889SApple OSS Distributions 	 * can consider the state change to have been communicated.
2647*43a90889SApple OSS Distributions 	 */
2648*43a90889SApple OSS Distributions 	if (syncstates) {
2649*43a90889SApple OSS Distributions 		in6m_commit(inm);
2650*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2651*43a90889SApple OSS Distributions 		    ip6_sprintf(&inm->in6m_addr),
2652*43a90889SApple OSS Distributions 		    if_name(inm->in6m_ifp)));
2653*43a90889SApple OSS Distributions 	}
2654*43a90889SApple OSS Distributions 
2655*43a90889SApple OSS Distributions 	return error;
2656*43a90889SApple OSS Distributions }
2657*43a90889SApple OSS Distributions 
2658*43a90889SApple OSS Distributions /*
2659*43a90889SApple OSS Distributions  * Issue an intermediate state change during the life-cycle.
2660*43a90889SApple OSS Distributions  */
2661*43a90889SApple OSS Distributions static int
mld_handle_state_change(struct in6_multi * inm,struct mld_ifinfo * mli,struct mld_tparams * mtp)2662*43a90889SApple OSS Distributions mld_handle_state_change(struct in6_multi *inm, struct mld_ifinfo *mli,
2663*43a90889SApple OSS Distributions     struct mld_tparams *mtp)
2664*43a90889SApple OSS Distributions {
2665*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
2666*43a90889SApple OSS Distributions 	int                      retval = 0;
2667*43a90889SApple OSS Distributions 
2668*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
2669*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_NOTHELD(mli);
2670*43a90889SApple OSS Distributions 	VERIFY(mtp != NULL);
2671*43a90889SApple OSS Distributions 
2672*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: state change for %s on ifp 0x%llx(%s)\n",
2673*43a90889SApple OSS Distributions 	    __func__, ip6_sprintf(&inm->in6m_addr),
2674*43a90889SApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
2675*43a90889SApple OSS Distributions 	    if_name(inm->in6m_ifp)));
2676*43a90889SApple OSS Distributions 
2677*43a90889SApple OSS Distributions 	ifp = inm->in6m_ifp;
2678*43a90889SApple OSS Distributions 
2679*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
2680*43a90889SApple OSS Distributions 	VERIFY(mli->mli_ifp == ifp);
2681*43a90889SApple OSS Distributions 
2682*43a90889SApple OSS Distributions 	if ((ifp->if_flags & IFF_LOOPBACK) ||
2683*43a90889SApple OSS Distributions 	    (mli->mli_flags & MLIF_SILENT) ||
2684*43a90889SApple OSS Distributions 	    !mld_is_addr_reported(&inm->in6m_addr) ||
2685*43a90889SApple OSS Distributions 	    (mli->mli_version != MLD_VERSION_2)) {
2686*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
2687*43a90889SApple OSS Distributions 		if (!mld_is_addr_reported(&inm->in6m_addr)) {
2688*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: not kicking state machine for silent "
2689*43a90889SApple OSS Distributions 			    "group\n", __func__));
2690*43a90889SApple OSS Distributions 		}
2691*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: nothing to do\n", __func__));
2692*43a90889SApple OSS Distributions 		in6m_commit(inm);
2693*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2694*43a90889SApple OSS Distributions 		    ip6_sprintf(&inm->in6m_addr),
2695*43a90889SApple OSS Distributions 		    if_name(inm->in6m_ifp)));
2696*43a90889SApple OSS Distributions 		goto done;
2697*43a90889SApple OSS Distributions 	}
2698*43a90889SApple OSS Distributions 
2699*43a90889SApple OSS Distributions 	IF_DRAIN(&inm->in6m_scq);
2700*43a90889SApple OSS Distributions 
2701*43a90889SApple OSS Distributions 	retval = mld_v2_enqueue_group_record(&inm->in6m_scq, inm, 1, 0, 0,
2702*43a90889SApple OSS Distributions 	    (mli->mli_flags & MLIF_USEALLOW));
2703*43a90889SApple OSS Distributions 	mtp->cst = (inm->in6m_scq.ifq_len > 0);
2704*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: enqueue record = %d\n", __func__, retval));
2705*43a90889SApple OSS Distributions 	if (retval <= 0) {
2706*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
2707*43a90889SApple OSS Distributions 		retval *= -1;
2708*43a90889SApple OSS Distributions 		goto done;
2709*43a90889SApple OSS Distributions 	} else {
2710*43a90889SApple OSS Distributions 		retval = 0;
2711*43a90889SApple OSS Distributions 	}
2712*43a90889SApple OSS Distributions 
2713*43a90889SApple OSS Distributions 	/*
2714*43a90889SApple OSS Distributions 	 * If record(s) were enqueued, start the state-change
2715*43a90889SApple OSS Distributions 	 * report timer for this group.
2716*43a90889SApple OSS Distributions 	 */
2717*43a90889SApple OSS Distributions 	inm->in6m_scrv = (uint16_t)mli->mli_rv;
2718*43a90889SApple OSS Distributions 	inm->in6m_sctimer = 1;
2719*43a90889SApple OSS Distributions 	mtp->sct = 1;
2720*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
2721*43a90889SApple OSS Distributions 
2722*43a90889SApple OSS Distributions done:
2723*43a90889SApple OSS Distributions 	return retval;
2724*43a90889SApple OSS Distributions }
2725*43a90889SApple OSS Distributions 
2726*43a90889SApple OSS Distributions /*
2727*43a90889SApple OSS Distributions  * Perform the final leave for a multicast address.
2728*43a90889SApple OSS Distributions  *
2729*43a90889SApple OSS Distributions  * When leaving a group:
2730*43a90889SApple OSS Distributions  *  MLDv1 sends a DONE message, if and only if we are the reporter.
2731*43a90889SApple OSS Distributions  *  MLDv2 enqueues a state-change report containing a transition
2732*43a90889SApple OSS Distributions  *  to INCLUDE {} for immediate transmission.
2733*43a90889SApple OSS Distributions  */
2734*43a90889SApple OSS Distributions static void
mld_final_leave(struct in6_multi * inm,struct mld_ifinfo * mli,struct mld_tparams * mtp)2735*43a90889SApple OSS Distributions mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli,
2736*43a90889SApple OSS Distributions     struct mld_tparams *mtp)
2737*43a90889SApple OSS Distributions {
2738*43a90889SApple OSS Distributions 	int syncstates = 1;
2739*43a90889SApple OSS Distributions 
2740*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
2741*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_NOTHELD(mli);
2742*43a90889SApple OSS Distributions 	VERIFY(mtp != NULL);
2743*43a90889SApple OSS Distributions 
2744*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: final leave %s on ifp 0x%llx(%s)\n",
2745*43a90889SApple OSS Distributions 	    __func__, ip6_sprintf(&inm->in6m_addr),
2746*43a90889SApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
2747*43a90889SApple OSS Distributions 	    if_name(inm->in6m_ifp)));
2748*43a90889SApple OSS Distributions 
2749*43a90889SApple OSS Distributions 	switch (inm->in6m_state) {
2750*43a90889SApple OSS Distributions 	case MLD_NOT_MEMBER:
2751*43a90889SApple OSS Distributions 	case MLD_SILENT_MEMBER:
2752*43a90889SApple OSS Distributions 	case MLD_LEAVING_MEMBER:
2753*43a90889SApple OSS Distributions 		/* Already leaving or left; do nothing. */
2754*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: not kicking state machine for silent group\n",
2755*43a90889SApple OSS Distributions 		    __func__));
2756*43a90889SApple OSS Distributions 		break;
2757*43a90889SApple OSS Distributions 	case MLD_REPORTING_MEMBER:
2758*43a90889SApple OSS Distributions 	case MLD_IDLE_MEMBER:
2759*43a90889SApple OSS Distributions 	case MLD_G_QUERY_PENDING_MEMBER:
2760*43a90889SApple OSS Distributions 	case MLD_SG_QUERY_PENDING_MEMBER:
2761*43a90889SApple OSS Distributions 		MLI_LOCK(mli);
2762*43a90889SApple OSS Distributions 		if (mli->mli_version == MLD_VERSION_1) {
2763*43a90889SApple OSS Distributions 			if (inm->in6m_state == MLD_G_QUERY_PENDING_MEMBER ||
2764*43a90889SApple OSS Distributions 			    inm->in6m_state == MLD_SG_QUERY_PENDING_MEMBER) {
2765*43a90889SApple OSS Distributions 				panic("%s: MLDv2 state reached, not MLDv2 "
2766*43a90889SApple OSS Distributions 				    "mode\n", __func__);
2767*43a90889SApple OSS Distributions 				/* NOTREACHED */
2768*43a90889SApple OSS Distributions 			}
2769*43a90889SApple OSS Distributions 			/* scheduler timer if enqueue is successful */
2770*43a90889SApple OSS Distributions 			mtp->cst = (mld_v1_transmit_report(inm,
2771*43a90889SApple OSS Distributions 			    MLD_LISTENER_DONE) == 0);
2772*43a90889SApple OSS Distributions 
2773*43a90889SApple OSS Distributions 			IN6M_LOCK_ASSERT_HELD(inm);
2774*43a90889SApple OSS Distributions 			MLI_LOCK_ASSERT_HELD(mli);
2775*43a90889SApple OSS Distributions 
2776*43a90889SApple OSS Distributions 			inm->in6m_state = MLD_NOT_MEMBER;
2777*43a90889SApple OSS Distributions 		} else if (mli->mli_version == MLD_VERSION_2) {
2778*43a90889SApple OSS Distributions 			/*
2779*43a90889SApple OSS Distributions 			 * Stop group timer and all pending reports.
2780*43a90889SApple OSS Distributions 			 * Immediately enqueue a state-change report
2781*43a90889SApple OSS Distributions 			 * TO_IN {} to be sent on the next timeout,
2782*43a90889SApple OSS Distributions 			 * giving us an opportunity to merge reports.
2783*43a90889SApple OSS Distributions 			 */
2784*43a90889SApple OSS Distributions 			IF_DRAIN(&inm->in6m_scq);
2785*43a90889SApple OSS Distributions 			inm->in6m_timer = 0;
2786*43a90889SApple OSS Distributions 			inm->in6m_scrv = (uint16_t)mli->mli_rv;
2787*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: Leaving %s/%s with %d "
2788*43a90889SApple OSS Distributions 			    "pending retransmissions.\n", __func__,
2789*43a90889SApple OSS Distributions 			    ip6_sprintf(&inm->in6m_addr),
2790*43a90889SApple OSS Distributions 			    if_name(inm->in6m_ifp),
2791*43a90889SApple OSS Distributions 			    inm->in6m_scrv));
2792*43a90889SApple OSS Distributions 			if (inm->in6m_scrv == 0) {
2793*43a90889SApple OSS Distributions 				inm->in6m_state = MLD_NOT_MEMBER;
2794*43a90889SApple OSS Distributions 				inm->in6m_sctimer = 0;
2795*43a90889SApple OSS Distributions 			} else {
2796*43a90889SApple OSS Distributions 				int retval;
2797*43a90889SApple OSS Distributions 				/*
2798*43a90889SApple OSS Distributions 				 * Stick around in the in6_multihead list;
2799*43a90889SApple OSS Distributions 				 * the final detach will be issued by
2800*43a90889SApple OSS Distributions 				 * mld_v2_process_group_timers() when
2801*43a90889SApple OSS Distributions 				 * the retransmit timer expires.
2802*43a90889SApple OSS Distributions 				 */
2803*43a90889SApple OSS Distributions 				IN6M_ADDREF_LOCKED(inm);
2804*43a90889SApple OSS Distributions 				VERIFY(inm->in6m_debug & IFD_ATTACHED);
2805*43a90889SApple OSS Distributions 				inm->in6m_reqcnt++;
2806*43a90889SApple OSS Distributions 				VERIFY(inm->in6m_reqcnt >= 1);
2807*43a90889SApple OSS Distributions 				inm->in6m_nrelecnt++;
2808*43a90889SApple OSS Distributions 				VERIFY(inm->in6m_nrelecnt != 0);
2809*43a90889SApple OSS Distributions 
2810*43a90889SApple OSS Distributions 				retval = mld_v2_enqueue_group_record(
2811*43a90889SApple OSS Distributions 					&inm->in6m_scq, inm, 1, 0, 0,
2812*43a90889SApple OSS Distributions 					(mli->mli_flags & MLIF_USEALLOW));
2813*43a90889SApple OSS Distributions 				mtp->cst = (inm->in6m_scq.ifq_len > 0);
2814*43a90889SApple OSS Distributions 				KASSERT(retval != 0,
2815*43a90889SApple OSS Distributions 				    ("%s: enqueue record = %d\n", __func__,
2816*43a90889SApple OSS Distributions 				    retval));
2817*43a90889SApple OSS Distributions 
2818*43a90889SApple OSS Distributions 				inm->in6m_state = MLD_LEAVING_MEMBER;
2819*43a90889SApple OSS Distributions 				inm->in6m_sctimer = 1;
2820*43a90889SApple OSS Distributions 				mtp->sct = 1;
2821*43a90889SApple OSS Distributions 				syncstates = 0;
2822*43a90889SApple OSS Distributions 			}
2823*43a90889SApple OSS Distributions 		}
2824*43a90889SApple OSS Distributions 		MLI_UNLOCK(mli);
2825*43a90889SApple OSS Distributions 		break;
2826*43a90889SApple OSS Distributions 	case MLD_LAZY_MEMBER:
2827*43a90889SApple OSS Distributions 	case MLD_SLEEPING_MEMBER:
2828*43a90889SApple OSS Distributions 	case MLD_AWAKENING_MEMBER:
2829*43a90889SApple OSS Distributions 		/* Our reports are suppressed; do nothing. */
2830*43a90889SApple OSS Distributions 		break;
2831*43a90889SApple OSS Distributions 	}
2832*43a90889SApple OSS Distributions 
2833*43a90889SApple OSS Distributions 	if (syncstates) {
2834*43a90889SApple OSS Distributions 		in6m_commit(inm);
2835*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2836*43a90889SApple OSS Distributions 		    ip6_sprintf(&inm->in6m_addr),
2837*43a90889SApple OSS Distributions 		    if_name(inm->in6m_ifp)));
2838*43a90889SApple OSS Distributions 		inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED;
2839*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: T1 now MCAST_UNDEFINED for 0x%llx/%s\n",
2840*43a90889SApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(&inm->in6m_addr),
2841*43a90889SApple OSS Distributions 		    if_name(inm->in6m_ifp)));
2842*43a90889SApple OSS Distributions 	}
2843*43a90889SApple OSS Distributions }
2844*43a90889SApple OSS Distributions 
2845*43a90889SApple OSS Distributions /*
2846*43a90889SApple OSS Distributions  * Enqueue an MLDv2 group record to the given output queue.
2847*43a90889SApple OSS Distributions  *
2848*43a90889SApple OSS Distributions  * If is_state_change is zero, a current-state record is appended.
2849*43a90889SApple OSS Distributions  * If is_state_change is non-zero, a state-change report is appended.
2850*43a90889SApple OSS Distributions  *
2851*43a90889SApple OSS Distributions  * If is_group_query is non-zero, an mbuf packet chain is allocated.
2852*43a90889SApple OSS Distributions  * If is_group_query is zero, and if there is a packet with free space
2853*43a90889SApple OSS Distributions  * at the tail of the queue, it will be appended to providing there
2854*43a90889SApple OSS Distributions  * is enough free space.
2855*43a90889SApple OSS Distributions  * Otherwise a new mbuf packet chain is allocated.
2856*43a90889SApple OSS Distributions  *
2857*43a90889SApple OSS Distributions  * If is_source_query is non-zero, each source is checked to see if
2858*43a90889SApple OSS Distributions  * it was recorded for a Group-Source query, and will be omitted if
2859*43a90889SApple OSS Distributions  * it is not both in-mode and recorded.
2860*43a90889SApple OSS Distributions  *
2861*43a90889SApple OSS Distributions  * If use_block_allow is non-zero, state change reports for initial join
2862*43a90889SApple OSS Distributions  * and final leave, on an inclusive mode group with a source list, will be
2863*43a90889SApple OSS Distributions  * rewritten to use the ALLOW_NEW and BLOCK_OLD record types, respectively.
2864*43a90889SApple OSS Distributions  *
2865*43a90889SApple OSS Distributions  * The function will attempt to allocate leading space in the packet
2866*43a90889SApple OSS Distributions  * for the IPv6+ICMP headers to be prepended without fragmenting the chain.
2867*43a90889SApple OSS Distributions  *
2868*43a90889SApple OSS Distributions  * If successful the size of all data appended to the queue is returned,
2869*43a90889SApple OSS Distributions  * otherwise an error code less than zero is returned, or zero if
2870*43a90889SApple OSS Distributions  * no record(s) were appended.
2871*43a90889SApple OSS Distributions  */
2872*43a90889SApple OSS Distributions static int
mld_v2_enqueue_group_record(struct ifqueue * ifq,struct in6_multi * inm,const int is_state_change,const int is_group_query,const int is_source_query,const int use_block_allow)2873*43a90889SApple OSS Distributions mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
2874*43a90889SApple OSS Distributions     const int is_state_change, const int is_group_query,
2875*43a90889SApple OSS Distributions     const int is_source_query, const int use_block_allow)
2876*43a90889SApple OSS Distributions {
2877*43a90889SApple OSS Distributions 	struct mldv2_record      mr;
2878*43a90889SApple OSS Distributions 	struct mldv2_record     *pmr;
2879*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
2880*43a90889SApple OSS Distributions 	struct ip6_msource      *ims, *nims;
2881*43a90889SApple OSS Distributions 	mbuf_ref_t               m0, m, md;
2882*43a90889SApple OSS Distributions 	int                      error, is_filter_list_change;
2883*43a90889SApple OSS Distributions 	int                      minrec0len, m0srcs, msrcs, nbytes, off;
2884*43a90889SApple OSS Distributions 	int                      record_has_sources;
2885*43a90889SApple OSS Distributions 	int                      now;
2886*43a90889SApple OSS Distributions 	uint8_t                  type;
2887*43a90889SApple OSS Distributions 	uint8_t                  mode;
2888*43a90889SApple OSS Distributions 
2889*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
2890*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
2891*43a90889SApple OSS Distributions 
2892*43a90889SApple OSS Distributions 	error = 0;
2893*43a90889SApple OSS Distributions 	ifp = inm->in6m_ifp;
2894*43a90889SApple OSS Distributions 	is_filter_list_change = 0;
2895*43a90889SApple OSS Distributions 	m = NULL;
2896*43a90889SApple OSS Distributions 	m0 = NULL;
2897*43a90889SApple OSS Distributions 	m0srcs = 0;
2898*43a90889SApple OSS Distributions 	msrcs = 0;
2899*43a90889SApple OSS Distributions 	nbytes = 0;
2900*43a90889SApple OSS Distributions 	nims = NULL;
2901*43a90889SApple OSS Distributions 	record_has_sources = 1;
2902*43a90889SApple OSS Distributions 	pmr = NULL;
2903*43a90889SApple OSS Distributions 	type = MLD_DO_NOTHING;
2904*43a90889SApple OSS Distributions 	mode = (uint8_t)inm->in6m_st[1].iss_fmode;
2905*43a90889SApple OSS Distributions 
2906*43a90889SApple OSS Distributions 	/*
2907*43a90889SApple OSS Distributions 	 * If we did not transition out of ASM mode during t0->t1,
2908*43a90889SApple OSS Distributions 	 * and there are no source nodes to process, we can skip
2909*43a90889SApple OSS Distributions 	 * the generation of source records.
2910*43a90889SApple OSS Distributions 	 */
2911*43a90889SApple OSS Distributions 	if (inm->in6m_st[0].iss_asm > 0 && inm->in6m_st[1].iss_asm > 0 &&
2912*43a90889SApple OSS Distributions 	    inm->in6m_nsrc == 0) {
2913*43a90889SApple OSS Distributions 		record_has_sources = 0;
2914*43a90889SApple OSS Distributions 	}
2915*43a90889SApple OSS Distributions 
2916*43a90889SApple OSS Distributions 	if (is_state_change) {
2917*43a90889SApple OSS Distributions 		/*
2918*43a90889SApple OSS Distributions 		 * Queue a state change record.
2919*43a90889SApple OSS Distributions 		 * If the mode did not change, and there are non-ASM
2920*43a90889SApple OSS Distributions 		 * listeners or source filters present,
2921*43a90889SApple OSS Distributions 		 * we potentially need to issue two records for the group.
2922*43a90889SApple OSS Distributions 		 * If there are ASM listeners, and there was no filter
2923*43a90889SApple OSS Distributions 		 * mode transition of any kind, do nothing.
2924*43a90889SApple OSS Distributions 		 *
2925*43a90889SApple OSS Distributions 		 * If we are transitioning to MCAST_UNDEFINED, we need
2926*43a90889SApple OSS Distributions 		 * not send any sources. A transition to/from this state is
2927*43a90889SApple OSS Distributions 		 * considered inclusive with some special treatment.
2928*43a90889SApple OSS Distributions 		 *
2929*43a90889SApple OSS Distributions 		 * If we are rewriting initial joins/leaves to use
2930*43a90889SApple OSS Distributions 		 * ALLOW/BLOCK, and the group's membership is inclusive,
2931*43a90889SApple OSS Distributions 		 * we need to send sources in all cases.
2932*43a90889SApple OSS Distributions 		 */
2933*43a90889SApple OSS Distributions 		if (mode != inm->in6m_st[0].iss_fmode) {
2934*43a90889SApple OSS Distributions 			if (mode == MCAST_EXCLUDE) {
2935*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: change to EXCLUDE\n",
2936*43a90889SApple OSS Distributions 				    __func__));
2937*43a90889SApple OSS Distributions 				type = MLD_CHANGE_TO_EXCLUDE_MODE;
2938*43a90889SApple OSS Distributions 			} else {
2939*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: change to INCLUDE\n",
2940*43a90889SApple OSS Distributions 				    __func__));
2941*43a90889SApple OSS Distributions 				if (use_block_allow) {
2942*43a90889SApple OSS Distributions 					/*
2943*43a90889SApple OSS Distributions 					 * XXX
2944*43a90889SApple OSS Distributions 					 * Here we're interested in state
2945*43a90889SApple OSS Distributions 					 * edges either direction between
2946*43a90889SApple OSS Distributions 					 * MCAST_UNDEFINED and MCAST_INCLUDE.
2947*43a90889SApple OSS Distributions 					 * Perhaps we should just check
2948*43a90889SApple OSS Distributions 					 * the group state, rather than
2949*43a90889SApple OSS Distributions 					 * the filter mode.
2950*43a90889SApple OSS Distributions 					 */
2951*43a90889SApple OSS Distributions 					if (mode == MCAST_UNDEFINED) {
2952*43a90889SApple OSS Distributions 						type = MLD_BLOCK_OLD_SOURCES;
2953*43a90889SApple OSS Distributions 					} else {
2954*43a90889SApple OSS Distributions 						type = MLD_ALLOW_NEW_SOURCES;
2955*43a90889SApple OSS Distributions 					}
2956*43a90889SApple OSS Distributions 				} else {
2957*43a90889SApple OSS Distributions 					type = MLD_CHANGE_TO_INCLUDE_MODE;
2958*43a90889SApple OSS Distributions 					if (mode == MCAST_UNDEFINED) {
2959*43a90889SApple OSS Distributions 						record_has_sources = 0;
2960*43a90889SApple OSS Distributions 					}
2961*43a90889SApple OSS Distributions 				}
2962*43a90889SApple OSS Distributions 			}
2963*43a90889SApple OSS Distributions 		} else {
2964*43a90889SApple OSS Distributions 			if (record_has_sources) {
2965*43a90889SApple OSS Distributions 				is_filter_list_change = 1;
2966*43a90889SApple OSS Distributions 			} else {
2967*43a90889SApple OSS Distributions 				type = MLD_DO_NOTHING;
2968*43a90889SApple OSS Distributions 			}
2969*43a90889SApple OSS Distributions 		}
2970*43a90889SApple OSS Distributions 	} else {
2971*43a90889SApple OSS Distributions 		/*
2972*43a90889SApple OSS Distributions 		 * Queue a current state record.
2973*43a90889SApple OSS Distributions 		 */
2974*43a90889SApple OSS Distributions 		if (mode == MCAST_EXCLUDE) {
2975*43a90889SApple OSS Distributions 			type = MLD_MODE_IS_EXCLUDE;
2976*43a90889SApple OSS Distributions 		} else if (mode == MCAST_INCLUDE) {
2977*43a90889SApple OSS Distributions 			type = MLD_MODE_IS_INCLUDE;
2978*43a90889SApple OSS Distributions 			VERIFY(inm->in6m_st[1].iss_asm == 0);
2979*43a90889SApple OSS Distributions 		}
2980*43a90889SApple OSS Distributions 	}
2981*43a90889SApple OSS Distributions 
2982*43a90889SApple OSS Distributions 	/*
2983*43a90889SApple OSS Distributions 	 * Generate the filter list changes using a separate function.
2984*43a90889SApple OSS Distributions 	 */
2985*43a90889SApple OSS Distributions 	if (is_filter_list_change) {
2986*43a90889SApple OSS Distributions 		return mld_v2_enqueue_filter_change(ifq, inm);
2987*43a90889SApple OSS Distributions 	}
2988*43a90889SApple OSS Distributions 
2989*43a90889SApple OSS Distributions 	if (type == MLD_DO_NOTHING) {
2990*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: nothing to do for %s/%s\n",
2991*43a90889SApple OSS Distributions 		    __func__, ip6_sprintf(&inm->in6m_addr),
2992*43a90889SApple OSS Distributions 		    if_name(inm->in6m_ifp)));
2993*43a90889SApple OSS Distributions 		return 0;
2994*43a90889SApple OSS Distributions 	}
2995*43a90889SApple OSS Distributions 
2996*43a90889SApple OSS Distributions 	/*
2997*43a90889SApple OSS Distributions 	 * If any sources are present, we must be able to fit at least
2998*43a90889SApple OSS Distributions 	 * one in the trailing space of the tail packet's mbuf,
2999*43a90889SApple OSS Distributions 	 * ideally more.
3000*43a90889SApple OSS Distributions 	 */
3001*43a90889SApple OSS Distributions 	minrec0len = sizeof(struct mldv2_record);
3002*43a90889SApple OSS Distributions 	if (record_has_sources) {
3003*43a90889SApple OSS Distributions 		minrec0len += sizeof(struct in6_addr);
3004*43a90889SApple OSS Distributions 	}
3005*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: queueing %s for %s/%s\n", __func__,
3006*43a90889SApple OSS Distributions 	    mld_rec_type_to_str(type),
3007*43a90889SApple OSS Distributions 	    ip6_sprintf(&inm->in6m_addr),
3008*43a90889SApple OSS Distributions 	    if_name(inm->in6m_ifp)));
3009*43a90889SApple OSS Distributions 
3010*43a90889SApple OSS Distributions 	/*
3011*43a90889SApple OSS Distributions 	 * Check if we have a packet in the tail of the queue for this
3012*43a90889SApple OSS Distributions 	 * group into which the first group record for this group will fit.
3013*43a90889SApple OSS Distributions 	 * Otherwise allocate a new packet.
3014*43a90889SApple OSS Distributions 	 * Always allocate leading space for IP6+RA+ICMPV6+REPORT.
3015*43a90889SApple OSS Distributions 	 * Note: Group records for G/GSR query responses MUST be sent
3016*43a90889SApple OSS Distributions 	 * in their own packet.
3017*43a90889SApple OSS Distributions 	 */
3018*43a90889SApple OSS Distributions 	m0 = ifq->ifq_tail;
3019*43a90889SApple OSS Distributions 	if (!is_group_query &&
3020*43a90889SApple OSS Distributions 	    m0 != NULL &&
3021*43a90889SApple OSS Distributions 	    (m0->m_pkthdr.vt_nrecs + 1 <= MLD_V2_REPORT_MAXRECS) &&
3022*43a90889SApple OSS Distributions 	    (m0->m_pkthdr.len + minrec0len) <
3023*43a90889SApple OSS Distributions 	    (ifp->if_mtu - MLD_MTUSPACE)) {
3024*43a90889SApple OSS Distributions 		m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
3025*43a90889SApple OSS Distributions 		    sizeof(struct mldv2_record)) /
3026*43a90889SApple OSS Distributions 		    sizeof(struct in6_addr);
3027*43a90889SApple OSS Distributions 		m = m0;
3028*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: use existing packet\n", __func__));
3029*43a90889SApple OSS Distributions 	} else {
3030*43a90889SApple OSS Distributions 		if (IF_QFULL(ifq)) {
3031*43a90889SApple OSS Distributions 			os_log_error(OS_LOG_DEFAULT,
3032*43a90889SApple OSS Distributions 			    "%s: outbound queue full\n", __func__);
3033*43a90889SApple OSS Distributions 			return -ENOMEM;
3034*43a90889SApple OSS Distributions 		}
3035*43a90889SApple OSS Distributions 		m = NULL;
3036*43a90889SApple OSS Distributions 		m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
3037*43a90889SApple OSS Distributions 		    sizeof(struct mldv2_record)) / sizeof(struct in6_addr);
3038*43a90889SApple OSS Distributions 		if (!is_state_change && !is_group_query) {
3039*43a90889SApple OSS Distributions 			m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3040*43a90889SApple OSS Distributions 		}
3041*43a90889SApple OSS Distributions 		if (m == NULL) {
3042*43a90889SApple OSS Distributions 			m = m_gethdr(M_DONTWAIT, MT_DATA);
3043*43a90889SApple OSS Distributions 		}
3044*43a90889SApple OSS Distributions 		if (m == NULL) {
3045*43a90889SApple OSS Distributions 			return -ENOMEM;
3046*43a90889SApple OSS Distributions 		}
3047*43a90889SApple OSS Distributions 
3048*43a90889SApple OSS Distributions 		mld_save_context(m, ifp);
3049*43a90889SApple OSS Distributions 
3050*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: allocated first packet\n", __func__));
3051*43a90889SApple OSS Distributions 	}
3052*43a90889SApple OSS Distributions 
3053*43a90889SApple OSS Distributions 	/*
3054*43a90889SApple OSS Distributions 	 * Append group record.
3055*43a90889SApple OSS Distributions 	 * If we have sources, we don't know how many yet.
3056*43a90889SApple OSS Distributions 	 */
3057*43a90889SApple OSS Distributions 	mr.mr_type = type;
3058*43a90889SApple OSS Distributions 	mr.mr_datalen = 0;
3059*43a90889SApple OSS Distributions 	mr.mr_numsrc = 0;
3060*43a90889SApple OSS Distributions 	mr.mr_addr = inm->in6m_addr;
3061*43a90889SApple OSS Distributions 	in6_clearscope(&mr.mr_addr);
3062*43a90889SApple OSS Distributions 	if (!m_append(m, sizeof(struct mldv2_record), (void *)&mr)) {
3063*43a90889SApple OSS Distributions 		if (m != m0) {
3064*43a90889SApple OSS Distributions 			m_freem(m);
3065*43a90889SApple OSS Distributions 		}
3066*43a90889SApple OSS Distributions 		os_log_error(OS_LOG_DEFAULT, "%s: m_append() failed.\n", __func__);
3067*43a90889SApple OSS Distributions 		return -ENOMEM;
3068*43a90889SApple OSS Distributions 	}
3069*43a90889SApple OSS Distributions 	nbytes += sizeof(struct mldv2_record);
3070*43a90889SApple OSS Distributions 
3071*43a90889SApple OSS Distributions 	/*
3072*43a90889SApple OSS Distributions 	 * Append as many sources as will fit in the first packet.
3073*43a90889SApple OSS Distributions 	 * If we are appending to a new packet, the chain allocation
3074*43a90889SApple OSS Distributions 	 * may potentially use clusters; use m_getptr() in this case.
3075*43a90889SApple OSS Distributions 	 * If we are appending to an existing packet, we need to obtain
3076*43a90889SApple OSS Distributions 	 * a pointer to the group record after m_append(), in case a new
3077*43a90889SApple OSS Distributions 	 * mbuf was allocated.
3078*43a90889SApple OSS Distributions 	 *
3079*43a90889SApple OSS Distributions 	 * Only append sources which are in-mode at t1. If we are
3080*43a90889SApple OSS Distributions 	 * transitioning to MCAST_UNDEFINED state on the group, and
3081*43a90889SApple OSS Distributions 	 * use_block_allow is zero, do not include source entries.
3082*43a90889SApple OSS Distributions 	 * Otherwise, we need to include this source in the report.
3083*43a90889SApple OSS Distributions 	 *
3084*43a90889SApple OSS Distributions 	 * Only report recorded sources in our filter set when responding
3085*43a90889SApple OSS Distributions 	 * to a group-source query.
3086*43a90889SApple OSS Distributions 	 */
3087*43a90889SApple OSS Distributions 	if (record_has_sources) {
3088*43a90889SApple OSS Distributions 		if (m == m0) {
3089*43a90889SApple OSS Distributions 			md = m_last(m);
3090*43a90889SApple OSS Distributions 			pmr = (struct mldv2_record *)(mtod(md, uint8_t *) +
3091*43a90889SApple OSS Distributions 			    md->m_len - nbytes);
3092*43a90889SApple OSS Distributions 		} else {
3093*43a90889SApple OSS Distributions 			md = m_getptr(m, 0, &off);
3094*43a90889SApple OSS Distributions 			pmr = (struct mldv2_record *)(mtod(md, uint8_t *) +
3095*43a90889SApple OSS Distributions 			    off);
3096*43a90889SApple OSS Distributions 		}
3097*43a90889SApple OSS Distributions 		msrcs = 0;
3098*43a90889SApple OSS Distributions 		RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs,
3099*43a90889SApple OSS Distributions 		    nims) {
3100*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: visit node %s\n", __func__,
3101*43a90889SApple OSS Distributions 			    ip6_sprintf(&ims->im6s_addr)));
3102*43a90889SApple OSS Distributions 			now = im6s_get_mode(inm, ims, 1);
3103*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: node is %d\n", __func__, now));
3104*43a90889SApple OSS Distributions 			if ((now != mode) ||
3105*43a90889SApple OSS Distributions 			    (now == mode &&
3106*43a90889SApple OSS Distributions 			    (!use_block_allow && mode == MCAST_UNDEFINED))) {
3107*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: skip node\n", __func__));
3108*43a90889SApple OSS Distributions 				continue;
3109*43a90889SApple OSS Distributions 			}
3110*43a90889SApple OSS Distributions 			if (is_source_query && ims->im6s_stp == 0) {
3111*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: skip unrecorded node\n",
3112*43a90889SApple OSS Distributions 				    __func__));
3113*43a90889SApple OSS Distributions 				continue;
3114*43a90889SApple OSS Distributions 			}
3115*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: append node\n", __func__));
3116*43a90889SApple OSS Distributions 			if (!m_append(m, sizeof(struct in6_addr),
3117*43a90889SApple OSS Distributions 			    (void *)&ims->im6s_addr)) {
3118*43a90889SApple OSS Distributions 				if (m != m0) {
3119*43a90889SApple OSS Distributions 					m_freem(m);
3120*43a90889SApple OSS Distributions 				}
3121*43a90889SApple OSS Distributions 				os_log_error(OS_LOG_DEFAULT,
3122*43a90889SApple OSS Distributions 				    "%s: m_append() failed\n",
3123*43a90889SApple OSS Distributions 				    __func__);
3124*43a90889SApple OSS Distributions 				return -ENOMEM;
3125*43a90889SApple OSS Distributions 			}
3126*43a90889SApple OSS Distributions 			nbytes += sizeof(struct in6_addr);
3127*43a90889SApple OSS Distributions 			++msrcs;
3128*43a90889SApple OSS Distributions 			if (msrcs == m0srcs) {
3129*43a90889SApple OSS Distributions 				break;
3130*43a90889SApple OSS Distributions 			}
3131*43a90889SApple OSS Distributions 		}
3132*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: msrcs is %d this packet\n", __func__,
3133*43a90889SApple OSS Distributions 		    msrcs));
3134*43a90889SApple OSS Distributions 		pmr->mr_numsrc = htons((uint16_t)msrcs);
3135*43a90889SApple OSS Distributions 		nbytes += (msrcs * sizeof(struct in6_addr));
3136*43a90889SApple OSS Distributions 	}
3137*43a90889SApple OSS Distributions 
3138*43a90889SApple OSS Distributions 	if (is_source_query && msrcs == 0) {
3139*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: no recorded sources to report\n", __func__));
3140*43a90889SApple OSS Distributions 		if (m != m0) {
3141*43a90889SApple OSS Distributions 			m_freem(m);
3142*43a90889SApple OSS Distributions 		}
3143*43a90889SApple OSS Distributions 		return 0;
3144*43a90889SApple OSS Distributions 	}
3145*43a90889SApple OSS Distributions 
3146*43a90889SApple OSS Distributions 	/*
3147*43a90889SApple OSS Distributions 	 * We are good to go with first packet.
3148*43a90889SApple OSS Distributions 	 */
3149*43a90889SApple OSS Distributions 	if (m != m0) {
3150*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: enqueueing first packet\n", __func__));
3151*43a90889SApple OSS Distributions 		m->m_pkthdr.vt_nrecs = 1;
3152*43a90889SApple OSS Distributions 		IF_ENQUEUE(ifq, m);
3153*43a90889SApple OSS Distributions 	} else {
3154*43a90889SApple OSS Distributions 		m->m_pkthdr.vt_nrecs++;
3155*43a90889SApple OSS Distributions 	}
3156*43a90889SApple OSS Distributions 	/*
3157*43a90889SApple OSS Distributions 	 * No further work needed if no source list in packet(s).
3158*43a90889SApple OSS Distributions 	 */
3159*43a90889SApple OSS Distributions 	if (!record_has_sources) {
3160*43a90889SApple OSS Distributions 		return nbytes;
3161*43a90889SApple OSS Distributions 	}
3162*43a90889SApple OSS Distributions 
3163*43a90889SApple OSS Distributions 	/*
3164*43a90889SApple OSS Distributions 	 * Whilst sources remain to be announced, we need to allocate
3165*43a90889SApple OSS Distributions 	 * a new packet and fill out as many sources as will fit.
3166*43a90889SApple OSS Distributions 	 * Always try for a cluster first.
3167*43a90889SApple OSS Distributions 	 */
3168*43a90889SApple OSS Distributions 	while (nims != NULL) {
3169*43a90889SApple OSS Distributions 		if (IF_QFULL(ifq)) {
3170*43a90889SApple OSS Distributions 			os_log_error(OS_LOG_DEFAULT, "%s: outbound queue full\n", __func__);
3171*43a90889SApple OSS Distributions 			return -ENOMEM;
3172*43a90889SApple OSS Distributions 		}
3173*43a90889SApple OSS Distributions 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3174*43a90889SApple OSS Distributions 		if (m == NULL) {
3175*43a90889SApple OSS Distributions 			m = m_gethdr(M_DONTWAIT, MT_DATA);
3176*43a90889SApple OSS Distributions 		}
3177*43a90889SApple OSS Distributions 		if (m == NULL) {
3178*43a90889SApple OSS Distributions 			return -ENOMEM;
3179*43a90889SApple OSS Distributions 		}
3180*43a90889SApple OSS Distributions 		mld_save_context(m, ifp);
3181*43a90889SApple OSS Distributions 		md = m_getptr(m, 0, &off);
3182*43a90889SApple OSS Distributions 		pmr = (struct mldv2_record *)(mtod(md, uint8_t *) + off);
3183*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: allocated next packet\n", __func__));
3184*43a90889SApple OSS Distributions 
3185*43a90889SApple OSS Distributions 		if (!m_append(m, sizeof(struct mldv2_record), (void *)&mr)) {
3186*43a90889SApple OSS Distributions 			if (m != m0) {
3187*43a90889SApple OSS Distributions 				m_freem(m);
3188*43a90889SApple OSS Distributions 			}
3189*43a90889SApple OSS Distributions 			os_log_error(OS_LOG_DEFAULT, "%s: m_append() failed.\n", __func__);
3190*43a90889SApple OSS Distributions 			return -ENOMEM;
3191*43a90889SApple OSS Distributions 		}
3192*43a90889SApple OSS Distributions 		m->m_pkthdr.vt_nrecs = 1;
3193*43a90889SApple OSS Distributions 		nbytes += sizeof(struct mldv2_record);
3194*43a90889SApple OSS Distributions 
3195*43a90889SApple OSS Distributions 		m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
3196*43a90889SApple OSS Distributions 		    sizeof(struct mldv2_record)) / sizeof(struct in6_addr);
3197*43a90889SApple OSS Distributions 
3198*43a90889SApple OSS Distributions 		msrcs = 0;
3199*43a90889SApple OSS Distributions 		RB_FOREACH_FROM(ims, ip6_msource_tree, nims) {
3200*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: visit node %s\n",
3201*43a90889SApple OSS Distributions 			    __func__, ip6_sprintf(&ims->im6s_addr)));
3202*43a90889SApple OSS Distributions 			now = im6s_get_mode(inm, ims, 1);
3203*43a90889SApple OSS Distributions 			if ((now != mode) ||
3204*43a90889SApple OSS Distributions 			    (now == mode &&
3205*43a90889SApple OSS Distributions 			    (!use_block_allow && mode == MCAST_UNDEFINED))) {
3206*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: skip node\n", __func__));
3207*43a90889SApple OSS Distributions 				continue;
3208*43a90889SApple OSS Distributions 			}
3209*43a90889SApple OSS Distributions 			if (is_source_query && ims->im6s_stp == 0) {
3210*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: skip unrecorded node\n",
3211*43a90889SApple OSS Distributions 				    __func__));
3212*43a90889SApple OSS Distributions 				continue;
3213*43a90889SApple OSS Distributions 			}
3214*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: append node\n", __func__));
3215*43a90889SApple OSS Distributions 			if (!m_append(m, sizeof(struct in6_addr),
3216*43a90889SApple OSS Distributions 			    (void *)&ims->im6s_addr)) {
3217*43a90889SApple OSS Distributions 				if (m != m0) {
3218*43a90889SApple OSS Distributions 					m_freem(m);
3219*43a90889SApple OSS Distributions 				}
3220*43a90889SApple OSS Distributions 				os_log_error(OS_LOG_DEFAULT, "%s: m_append() failed\n",
3221*43a90889SApple OSS Distributions 				    __func__);
3222*43a90889SApple OSS Distributions 				return -ENOMEM;
3223*43a90889SApple OSS Distributions 			}
3224*43a90889SApple OSS Distributions 			++msrcs;
3225*43a90889SApple OSS Distributions 			if (msrcs == m0srcs) {
3226*43a90889SApple OSS Distributions 				break;
3227*43a90889SApple OSS Distributions 			}
3228*43a90889SApple OSS Distributions 		}
3229*43a90889SApple OSS Distributions 		pmr->mr_numsrc = htons((uint16_t)msrcs);
3230*43a90889SApple OSS Distributions 		nbytes += (msrcs * sizeof(struct in6_addr));
3231*43a90889SApple OSS Distributions 
3232*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: enqueueing next packet\n", __func__));
3233*43a90889SApple OSS Distributions 		IF_ENQUEUE(ifq, m);
3234*43a90889SApple OSS Distributions 	}
3235*43a90889SApple OSS Distributions 
3236*43a90889SApple OSS Distributions 	return nbytes;
3237*43a90889SApple OSS Distributions }
3238*43a90889SApple OSS Distributions 
3239*43a90889SApple OSS Distributions /*
3240*43a90889SApple OSS Distributions  * Type used to mark record pass completion.
3241*43a90889SApple OSS Distributions  * We exploit the fact we can cast to this easily from the
3242*43a90889SApple OSS Distributions  * current filter modes on each ip_msource node.
3243*43a90889SApple OSS Distributions  */
3244*43a90889SApple OSS Distributions typedef enum {
3245*43a90889SApple OSS Distributions 	REC_NONE = 0x00,        /* MCAST_UNDEFINED */
3246*43a90889SApple OSS Distributions 	REC_ALLOW = 0x01,       /* MCAST_INCLUDE */
3247*43a90889SApple OSS Distributions 	REC_BLOCK = 0x02,       /* MCAST_EXCLUDE */
3248*43a90889SApple OSS Distributions 	REC_FULL = REC_ALLOW | REC_BLOCK
3249*43a90889SApple OSS Distributions } rectype_t;
3250*43a90889SApple OSS Distributions 
3251*43a90889SApple OSS Distributions /*
3252*43a90889SApple OSS Distributions  * Enqueue an MLDv2 filter list change to the given output queue.
3253*43a90889SApple OSS Distributions  *
3254*43a90889SApple OSS Distributions  * Source list filter state is held in an RB-tree. When the filter list
3255*43a90889SApple OSS Distributions  * for a group is changed without changing its mode, we need to compute
3256*43a90889SApple OSS Distributions  * the deltas between T0 and T1 for each source in the filter set,
3257*43a90889SApple OSS Distributions  * and enqueue the appropriate ALLOW_NEW/BLOCK_OLD records.
3258*43a90889SApple OSS Distributions  *
3259*43a90889SApple OSS Distributions  * As we may potentially queue two record types, and the entire R-B tree
3260*43a90889SApple OSS Distributions  * needs to be walked at once, we break this out into its own function
3261*43a90889SApple OSS Distributions  * so we can generate a tightly packed queue of packets.
3262*43a90889SApple OSS Distributions  *
3263*43a90889SApple OSS Distributions  * XXX This could be written to only use one tree walk, although that makes
3264*43a90889SApple OSS Distributions  * serializing into the mbuf chains a bit harder. For now we do two walks
3265*43a90889SApple OSS Distributions  * which makes things easier on us, and it may or may not be harder on
3266*43a90889SApple OSS Distributions  * the L2 cache.
3267*43a90889SApple OSS Distributions  *
3268*43a90889SApple OSS Distributions  * If successful the size of all data appended to the queue is returned,
3269*43a90889SApple OSS Distributions  * otherwise an error code less than zero is returned, or zero if
3270*43a90889SApple OSS Distributions  * no record(s) were appended.
3271*43a90889SApple OSS Distributions  */
3272*43a90889SApple OSS Distributions static int
mld_v2_enqueue_filter_change(struct ifqueue * ifq,struct in6_multi * inm)3273*43a90889SApple OSS Distributions mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
3274*43a90889SApple OSS Distributions {
3275*43a90889SApple OSS Distributions 	static const int MINRECLEN =
3276*43a90889SApple OSS Distributions 	    sizeof(struct mldv2_record) + sizeof(struct in6_addr);
3277*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
3278*43a90889SApple OSS Distributions 	struct mldv2_record      mr;
3279*43a90889SApple OSS Distributions 	struct mldv2_record     *pmr;
3280*43a90889SApple OSS Distributions 	struct ip6_msource      *ims, *nims;
3281*43a90889SApple OSS Distributions 	mbuf_ref_t               m, m0, md;
3282*43a90889SApple OSS Distributions 	int                      m0srcs, nbytes, npbytes, off, rsrcs, schanged;
3283*43a90889SApple OSS Distributions 	int                      nallow, nblock;
3284*43a90889SApple OSS Distributions 	uint8_t                  mode, now, then;
3285*43a90889SApple OSS Distributions 	rectype_t                crt, drt, nrt;
3286*43a90889SApple OSS Distributions 
3287*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
3288*43a90889SApple OSS Distributions 
3289*43a90889SApple OSS Distributions 	if (inm->in6m_nsrc == 0 ||
3290*43a90889SApple OSS Distributions 	    (inm->in6m_st[0].iss_asm > 0 && inm->in6m_st[1].iss_asm > 0)) {
3291*43a90889SApple OSS Distributions 		return 0;
3292*43a90889SApple OSS Distributions 	}
3293*43a90889SApple OSS Distributions 
3294*43a90889SApple OSS Distributions 	ifp = inm->in6m_ifp;                    /* interface */
3295*43a90889SApple OSS Distributions 	mode = (uint8_t)inm->in6m_st[1].iss_fmode;       /* filter mode at t1 */
3296*43a90889SApple OSS Distributions 	crt = REC_NONE; /* current group record type */
3297*43a90889SApple OSS Distributions 	drt = REC_NONE; /* mask of completed group record types */
3298*43a90889SApple OSS Distributions 	nrt = REC_NONE; /* record type for current node */
3299*43a90889SApple OSS Distributions 	m0srcs = 0;     /* # source which will fit in current mbuf chain */
3300*43a90889SApple OSS Distributions 	npbytes = 0;    /* # of bytes appended this packet */
3301*43a90889SApple OSS Distributions 	nbytes = 0;     /* # of bytes appended to group's state-change queue */
3302*43a90889SApple OSS Distributions 	rsrcs = 0;      /* # sources encoded in current record */
3303*43a90889SApple OSS Distributions 	schanged = 0;   /* # nodes encoded in overall filter change */
3304*43a90889SApple OSS Distributions 	nallow = 0;     /* # of source entries in ALLOW_NEW */
3305*43a90889SApple OSS Distributions 	nblock = 0;     /* # of source entries in BLOCK_OLD */
3306*43a90889SApple OSS Distributions 	nims = NULL;    /* next tree node pointer */
3307*43a90889SApple OSS Distributions 
3308*43a90889SApple OSS Distributions 	/*
3309*43a90889SApple OSS Distributions 	 * For each possible filter record mode.
3310*43a90889SApple OSS Distributions 	 * The first kind of source we encounter tells us which
3311*43a90889SApple OSS Distributions 	 * is the first kind of record we start appending.
3312*43a90889SApple OSS Distributions 	 * If a node transitioned to UNDEFINED at t1, its mode is treated
3313*43a90889SApple OSS Distributions 	 * as the inverse of the group's filter mode.
3314*43a90889SApple OSS Distributions 	 */
3315*43a90889SApple OSS Distributions 	while (drt != REC_FULL) {
3316*43a90889SApple OSS Distributions 		do {
3317*43a90889SApple OSS Distributions 			m0 = ifq->ifq_tail;
3318*43a90889SApple OSS Distributions 			if (m0 != NULL &&
3319*43a90889SApple OSS Distributions 			    (m0->m_pkthdr.vt_nrecs + 1 <=
3320*43a90889SApple OSS Distributions 			    MLD_V2_REPORT_MAXRECS) &&
3321*43a90889SApple OSS Distributions 			    (m0->m_pkthdr.len + MINRECLEN) <
3322*43a90889SApple OSS Distributions 			    (ifp->if_mtu - MLD_MTUSPACE)) {
3323*43a90889SApple OSS Distributions 				m = m0;
3324*43a90889SApple OSS Distributions 				m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
3325*43a90889SApple OSS Distributions 				    sizeof(struct mldv2_record)) /
3326*43a90889SApple OSS Distributions 				    sizeof(struct in6_addr);
3327*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: use previous packet\n",
3328*43a90889SApple OSS Distributions 				    __func__));
3329*43a90889SApple OSS Distributions 			} else {
3330*43a90889SApple OSS Distributions 				m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3331*43a90889SApple OSS Distributions 				if (m == NULL) {
3332*43a90889SApple OSS Distributions 					m = m_gethdr(M_DONTWAIT, MT_DATA);
3333*43a90889SApple OSS Distributions 				}
3334*43a90889SApple OSS Distributions 				if (m == NULL) {
3335*43a90889SApple OSS Distributions 					os_log_error(OS_LOG_DEFAULT, "%s: m_get*() failed\n",
3336*43a90889SApple OSS Distributions 					    __func__);
3337*43a90889SApple OSS Distributions 					return -ENOMEM;
3338*43a90889SApple OSS Distributions 				}
3339*43a90889SApple OSS Distributions 				m->m_pkthdr.vt_nrecs = 0;
3340*43a90889SApple OSS Distributions 				mld_save_context(m, ifp);
3341*43a90889SApple OSS Distributions 				m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
3342*43a90889SApple OSS Distributions 				    sizeof(struct mldv2_record)) /
3343*43a90889SApple OSS Distributions 				    sizeof(struct in6_addr);
3344*43a90889SApple OSS Distributions 				npbytes = 0;
3345*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: allocated new packet\n",
3346*43a90889SApple OSS Distributions 				    __func__));
3347*43a90889SApple OSS Distributions 			}
3348*43a90889SApple OSS Distributions 			/*
3349*43a90889SApple OSS Distributions 			 * Append the MLD group record header to the
3350*43a90889SApple OSS Distributions 			 * current packet's data area.
3351*43a90889SApple OSS Distributions 			 * Recalculate pointer to free space for next
3352*43a90889SApple OSS Distributions 			 * group record, in case m_append() allocated
3353*43a90889SApple OSS Distributions 			 * a new mbuf or cluster.
3354*43a90889SApple OSS Distributions 			 */
3355*43a90889SApple OSS Distributions 			memset(&mr, 0, sizeof(mr));
3356*43a90889SApple OSS Distributions 			mr.mr_addr = inm->in6m_addr;
3357*43a90889SApple OSS Distributions 			in6_clearscope(&mr.mr_addr);
3358*43a90889SApple OSS Distributions 			if (!m_append(m, sizeof(mr), (void *)&mr)) {
3359*43a90889SApple OSS Distributions 				if (m != m0) {
3360*43a90889SApple OSS Distributions 					m_freem(m);
3361*43a90889SApple OSS Distributions 				}
3362*43a90889SApple OSS Distributions 				os_log_error(OS_LOG_DEFAULT, "%s: m_append() failed\n",
3363*43a90889SApple OSS Distributions 				    __func__);
3364*43a90889SApple OSS Distributions 				return -ENOMEM;
3365*43a90889SApple OSS Distributions 			}
3366*43a90889SApple OSS Distributions 			npbytes += sizeof(struct mldv2_record);
3367*43a90889SApple OSS Distributions 			if (m != m0) {
3368*43a90889SApple OSS Distributions 				/* new packet; offset in chain */
3369*43a90889SApple OSS Distributions 				md = m_getptr(m, npbytes -
3370*43a90889SApple OSS Distributions 				    sizeof(struct mldv2_record), &off);
3371*43a90889SApple OSS Distributions 				pmr = (struct mldv2_record *)(mtod(md,
3372*43a90889SApple OSS Distributions 				    uint8_t *) + off);
3373*43a90889SApple OSS Distributions 			} else {
3374*43a90889SApple OSS Distributions 				/* current packet; offset from last append */
3375*43a90889SApple OSS Distributions 				md = m_last(m);
3376*43a90889SApple OSS Distributions 				pmr = (struct mldv2_record *)(mtod(md,
3377*43a90889SApple OSS Distributions 				    uint8_t *) + md->m_len -
3378*43a90889SApple OSS Distributions 				    sizeof(struct mldv2_record));
3379*43a90889SApple OSS Distributions 			}
3380*43a90889SApple OSS Distributions 			/*
3381*43a90889SApple OSS Distributions 			 * Begin walking the tree for this record type
3382*43a90889SApple OSS Distributions 			 * pass, or continue from where we left off
3383*43a90889SApple OSS Distributions 			 * previously if we had to allocate a new packet.
3384*43a90889SApple OSS Distributions 			 * Only report deltas in-mode at t1.
3385*43a90889SApple OSS Distributions 			 * We need not report included sources as allowed
3386*43a90889SApple OSS Distributions 			 * if we are in inclusive mode on the group,
3387*43a90889SApple OSS Distributions 			 * however the converse is not true.
3388*43a90889SApple OSS Distributions 			 */
3389*43a90889SApple OSS Distributions 			rsrcs = 0;
3390*43a90889SApple OSS Distributions 			if (nims == NULL) {
3391*43a90889SApple OSS Distributions 				nims = RB_MIN(ip6_msource_tree,
3392*43a90889SApple OSS Distributions 				    &inm->in6m_srcs);
3393*43a90889SApple OSS Distributions 			}
3394*43a90889SApple OSS Distributions 			RB_FOREACH_FROM(ims, ip6_msource_tree, nims) {
3395*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: visit node %s\n", __func__,
3396*43a90889SApple OSS Distributions 				    ip6_sprintf(&ims->im6s_addr)));
3397*43a90889SApple OSS Distributions 				now = im6s_get_mode(inm, ims, 1);
3398*43a90889SApple OSS Distributions 				then = im6s_get_mode(inm, ims, 0);
3399*43a90889SApple OSS Distributions 				MLD_PRINTF(("%s: mode: t0 %d, t1 %d\n",
3400*43a90889SApple OSS Distributions 				    __func__, then, now));
3401*43a90889SApple OSS Distributions 				if (now == then) {
3402*43a90889SApple OSS Distributions 					MLD_PRINTF(("%s: skip unchanged\n",
3403*43a90889SApple OSS Distributions 					    __func__));
3404*43a90889SApple OSS Distributions 					continue;
3405*43a90889SApple OSS Distributions 				}
3406*43a90889SApple OSS Distributions 				if (mode == MCAST_EXCLUDE &&
3407*43a90889SApple OSS Distributions 				    now == MCAST_INCLUDE) {
3408*43a90889SApple OSS Distributions 					MLD_PRINTF(("%s: skip IN src on EX "
3409*43a90889SApple OSS Distributions 					    "group\n", __func__));
3410*43a90889SApple OSS Distributions 					continue;
3411*43a90889SApple OSS Distributions 				}
3412*43a90889SApple OSS Distributions 				nrt = (rectype_t)now;
3413*43a90889SApple OSS Distributions 				if (nrt == REC_NONE) {
3414*43a90889SApple OSS Distributions 					nrt = (rectype_t)(~mode & REC_FULL);
3415*43a90889SApple OSS Distributions 				}
3416*43a90889SApple OSS Distributions 				if (schanged++ == 0) {
3417*43a90889SApple OSS Distributions 					crt = nrt;
3418*43a90889SApple OSS Distributions 				} else if (crt != nrt) {
3419*43a90889SApple OSS Distributions 					continue;
3420*43a90889SApple OSS Distributions 				}
3421*43a90889SApple OSS Distributions 				if (!m_append(m, sizeof(struct in6_addr),
3422*43a90889SApple OSS Distributions 				    (void *)&ims->im6s_addr)) {
3423*43a90889SApple OSS Distributions 					if (m != m0) {
3424*43a90889SApple OSS Distributions 						m_freem(m);
3425*43a90889SApple OSS Distributions 					}
3426*43a90889SApple OSS Distributions 					os_log_error(OS_LOG_DEFAULT, "%s: m_append() failed\n",
3427*43a90889SApple OSS Distributions 					    __func__);
3428*43a90889SApple OSS Distributions 					return -ENOMEM;
3429*43a90889SApple OSS Distributions 				}
3430*43a90889SApple OSS Distributions 				nallow += !!(crt == REC_ALLOW);
3431*43a90889SApple OSS Distributions 				nblock += !!(crt == REC_BLOCK);
3432*43a90889SApple OSS Distributions 				if (++rsrcs == m0srcs) {
3433*43a90889SApple OSS Distributions 					break;
3434*43a90889SApple OSS Distributions 				}
3435*43a90889SApple OSS Distributions 			}
3436*43a90889SApple OSS Distributions 			/*
3437*43a90889SApple OSS Distributions 			 * If we did not append any tree nodes on this
3438*43a90889SApple OSS Distributions 			 * pass, back out of allocations.
3439*43a90889SApple OSS Distributions 			 */
3440*43a90889SApple OSS Distributions 			if (rsrcs == 0) {
3441*43a90889SApple OSS Distributions 				npbytes -= sizeof(struct mldv2_record);
3442*43a90889SApple OSS Distributions 				if (m != m0) {
3443*43a90889SApple OSS Distributions 					MLD_PRINTF(("%s: m_free(m)\n",
3444*43a90889SApple OSS Distributions 					    __func__));
3445*43a90889SApple OSS Distributions 					m_freem(m);
3446*43a90889SApple OSS Distributions 				} else {
3447*43a90889SApple OSS Distributions 					MLD_PRINTF(("%s: m_adj(m, -mr)\n",
3448*43a90889SApple OSS Distributions 					    __func__));
3449*43a90889SApple OSS Distributions 					m_adj(m, -((int)sizeof(
3450*43a90889SApple OSS Distributions 						    struct mldv2_record)));
3451*43a90889SApple OSS Distributions 				}
3452*43a90889SApple OSS Distributions 				continue;
3453*43a90889SApple OSS Distributions 			}
3454*43a90889SApple OSS Distributions 			npbytes += (rsrcs * sizeof(struct in6_addr));
3455*43a90889SApple OSS Distributions 			if (crt == REC_ALLOW) {
3456*43a90889SApple OSS Distributions 				pmr->mr_type = MLD_ALLOW_NEW_SOURCES;
3457*43a90889SApple OSS Distributions 			} else if (crt == REC_BLOCK) {
3458*43a90889SApple OSS Distributions 				pmr->mr_type = MLD_BLOCK_OLD_SOURCES;
3459*43a90889SApple OSS Distributions 			}
3460*43a90889SApple OSS Distributions 			pmr->mr_numsrc = htons((uint16_t)rsrcs);
3461*43a90889SApple OSS Distributions 			/*
3462*43a90889SApple OSS Distributions 			 * Count the new group record, and enqueue this
3463*43a90889SApple OSS Distributions 			 * packet if it wasn't already queued.
3464*43a90889SApple OSS Distributions 			 */
3465*43a90889SApple OSS Distributions 			m->m_pkthdr.vt_nrecs++;
3466*43a90889SApple OSS Distributions 			if (m != m0) {
3467*43a90889SApple OSS Distributions 				IF_ENQUEUE(ifq, m);
3468*43a90889SApple OSS Distributions 			}
3469*43a90889SApple OSS Distributions 			nbytes += npbytes;
3470*43a90889SApple OSS Distributions 		} while (nims != NULL);
3471*43a90889SApple OSS Distributions 		drt |= crt;
3472*43a90889SApple OSS Distributions 		crt = (~crt & REC_FULL);
3473*43a90889SApple OSS Distributions 	}
3474*43a90889SApple OSS Distributions 
3475*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: queued %d ALLOW_NEW, %d BLOCK_OLD\n", __func__,
3476*43a90889SApple OSS Distributions 	    nallow, nblock));
3477*43a90889SApple OSS Distributions 
3478*43a90889SApple OSS Distributions 	return nbytes;
3479*43a90889SApple OSS Distributions }
3480*43a90889SApple OSS Distributions 
3481*43a90889SApple OSS Distributions static int
mld_v2_merge_state_changes(struct in6_multi * inm,struct ifqueue * ifscq)3482*43a90889SApple OSS Distributions mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq)
3483*43a90889SApple OSS Distributions {
3484*43a90889SApple OSS Distributions 	struct ifqueue  *gq;
3485*43a90889SApple OSS Distributions 	mbuf_ref_t       m;    /* pending state-change */
3486*43a90889SApple OSS Distributions 	mbuf_ref_t       m0;   /* copy of pending state-change */
3487*43a90889SApple OSS Distributions 	mbuf_ref_t       mt;   /* last state-change in packet */
3488*43a90889SApple OSS Distributions 	mbuf_ref_t       n;
3489*43a90889SApple OSS Distributions 	int              docopy, domerge;
3490*43a90889SApple OSS Distributions 	u_int            recslen;
3491*43a90889SApple OSS Distributions 
3492*43a90889SApple OSS Distributions 	IN6M_LOCK_ASSERT_HELD(inm);
3493*43a90889SApple OSS Distributions 
3494*43a90889SApple OSS Distributions 	docopy = 0;
3495*43a90889SApple OSS Distributions 	domerge = 0;
3496*43a90889SApple OSS Distributions 	recslen = 0;
3497*43a90889SApple OSS Distributions 
3498*43a90889SApple OSS Distributions 	/*
3499*43a90889SApple OSS Distributions 	 * If there are further pending retransmissions, make a writable
3500*43a90889SApple OSS Distributions 	 * copy of each queued state-change message before merging.
3501*43a90889SApple OSS Distributions 	 */
3502*43a90889SApple OSS Distributions 	if (inm->in6m_scrv > 0) {
3503*43a90889SApple OSS Distributions 		docopy = 1;
3504*43a90889SApple OSS Distributions 	}
3505*43a90889SApple OSS Distributions 
3506*43a90889SApple OSS Distributions 	gq = &inm->in6m_scq;
3507*43a90889SApple OSS Distributions #ifdef MLD_DEBUG
3508*43a90889SApple OSS Distributions 	if (gq->ifq_head == NULL) {
3509*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: WARNING: queue for inm 0x%llx is empty\n",
3510*43a90889SApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(inm)));
3511*43a90889SApple OSS Distributions 	}
3512*43a90889SApple OSS Distributions #endif
3513*43a90889SApple OSS Distributions 
3514*43a90889SApple OSS Distributions 	/*
3515*43a90889SApple OSS Distributions 	 * Use IF_REMQUEUE() instead of IF_DEQUEUE() below, since the
3516*43a90889SApple OSS Distributions 	 * packet might not always be at the head of the ifqueue.
3517*43a90889SApple OSS Distributions 	 */
3518*43a90889SApple OSS Distributions 	m = gq->ifq_head;
3519*43a90889SApple OSS Distributions 	while (m != NULL) {
3520*43a90889SApple OSS Distributions 		/*
3521*43a90889SApple OSS Distributions 		 * Only merge the report into the current packet if
3522*43a90889SApple OSS Distributions 		 * there is sufficient space to do so; an MLDv2 report
3523*43a90889SApple OSS Distributions 		 * packet may only contain 65,535 group records.
3524*43a90889SApple OSS Distributions 		 * Always use a simple mbuf chain concatentation to do this,
3525*43a90889SApple OSS Distributions 		 * as large state changes for single groups may have
3526*43a90889SApple OSS Distributions 		 * allocated clusters.
3527*43a90889SApple OSS Distributions 		 */
3528*43a90889SApple OSS Distributions 		domerge = 0;
3529*43a90889SApple OSS Distributions 		mt = ifscq->ifq_tail;
3530*43a90889SApple OSS Distributions 		if (mt != NULL) {
3531*43a90889SApple OSS Distributions 			recslen = m_length(m);
3532*43a90889SApple OSS Distributions 
3533*43a90889SApple OSS Distributions 			if ((mt->m_pkthdr.vt_nrecs +
3534*43a90889SApple OSS Distributions 			    m->m_pkthdr.vt_nrecs <=
3535*43a90889SApple OSS Distributions 			    MLD_V2_REPORT_MAXRECS) &&
3536*43a90889SApple OSS Distributions 			    (mt->m_pkthdr.len + recslen <=
3537*43a90889SApple OSS Distributions 			    (inm->in6m_ifp->if_mtu - MLD_MTUSPACE))) {
3538*43a90889SApple OSS Distributions 				domerge = 1;
3539*43a90889SApple OSS Distributions 			}
3540*43a90889SApple OSS Distributions 		}
3541*43a90889SApple OSS Distributions 
3542*43a90889SApple OSS Distributions 		if (!domerge && IF_QFULL(gq)) {
3543*43a90889SApple OSS Distributions 			os_log_info(OS_LOG_DEFAULT, "%s: outbound queue full",
3544*43a90889SApple OSS Distributions 			    __func__);
3545*43a90889SApple OSS Distributions 			n = m->m_nextpkt;
3546*43a90889SApple OSS Distributions 			if (!docopy) {
3547*43a90889SApple OSS Distributions 				IF_REMQUEUE(gq, m);
3548*43a90889SApple OSS Distributions 				m_freem(m);
3549*43a90889SApple OSS Distributions 			}
3550*43a90889SApple OSS Distributions 			m = n;
3551*43a90889SApple OSS Distributions 			continue;
3552*43a90889SApple OSS Distributions 		}
3553*43a90889SApple OSS Distributions 
3554*43a90889SApple OSS Distributions 		if (!docopy) {
3555*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: dequeueing 0x%llx\n", __func__,
3556*43a90889SApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3557*43a90889SApple OSS Distributions 			n = m->m_nextpkt;
3558*43a90889SApple OSS Distributions 			IF_REMQUEUE(gq, m);
3559*43a90889SApple OSS Distributions 			m0 = m;
3560*43a90889SApple OSS Distributions 			m = n;
3561*43a90889SApple OSS Distributions 		} else {
3562*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: copying 0x%llx\n", __func__,
3563*43a90889SApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3564*43a90889SApple OSS Distributions 			m0 = m_dup(m, M_NOWAIT);
3565*43a90889SApple OSS Distributions 			if (m0 == NULL) {
3566*43a90889SApple OSS Distributions 				return ENOMEM;
3567*43a90889SApple OSS Distributions 			}
3568*43a90889SApple OSS Distributions 			m0->m_nextpkt = NULL;
3569*43a90889SApple OSS Distributions 			m = m->m_nextpkt;
3570*43a90889SApple OSS Distributions 		}
3571*43a90889SApple OSS Distributions 
3572*43a90889SApple OSS Distributions 		if (!domerge) {
3573*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: queueing 0x%llx to ifscq 0x%llx)\n",
3574*43a90889SApple OSS Distributions 			    __func__, (uint64_t)VM_KERNEL_ADDRPERM(m0),
3575*43a90889SApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(ifscq)));
3576*43a90889SApple OSS Distributions 			IF_ENQUEUE(ifscq, m0);
3577*43a90889SApple OSS Distributions 		} else {
3578*43a90889SApple OSS Distributions 			struct mbuf *mtl;       /* last mbuf of packet mt */
3579*43a90889SApple OSS Distributions 
3580*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: merging 0x%llx with ifscq tail "
3581*43a90889SApple OSS Distributions 			    "0x%llx)\n", __func__,
3582*43a90889SApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m0),
3583*43a90889SApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(mt)));
3584*43a90889SApple OSS Distributions 
3585*43a90889SApple OSS Distributions 			mtl = m_last(mt);
3586*43a90889SApple OSS Distributions 			m0->m_flags &= ~M_PKTHDR;
3587*43a90889SApple OSS Distributions 			mt->m_pkthdr.len += recslen;
3588*43a90889SApple OSS Distributions 			mt->m_pkthdr.vt_nrecs +=
3589*43a90889SApple OSS Distributions 			    m0->m_pkthdr.vt_nrecs;
3590*43a90889SApple OSS Distributions 
3591*43a90889SApple OSS Distributions 			mtl->m_next = m0;
3592*43a90889SApple OSS Distributions 		}
3593*43a90889SApple OSS Distributions 	}
3594*43a90889SApple OSS Distributions 
3595*43a90889SApple OSS Distributions 	return 0;
3596*43a90889SApple OSS Distributions }
3597*43a90889SApple OSS Distributions 
3598*43a90889SApple OSS Distributions /*
3599*43a90889SApple OSS Distributions  * Respond to a pending MLDv2 General Query.
3600*43a90889SApple OSS Distributions  */
3601*43a90889SApple OSS Distributions static uint32_t
mld_v2_dispatch_general_query(struct mld_ifinfo * mli)3602*43a90889SApple OSS Distributions mld_v2_dispatch_general_query(struct mld_ifinfo *mli)
3603*43a90889SApple OSS Distributions {
3604*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
3605*43a90889SApple OSS Distributions 	struct in6_multi        *inm;
3606*43a90889SApple OSS Distributions 	struct in6_multistep    step;
3607*43a90889SApple OSS Distributions 	int                      retval;
3608*43a90889SApple OSS Distributions 
3609*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
3610*43a90889SApple OSS Distributions 
3611*43a90889SApple OSS Distributions 	VERIFY(mli->mli_version == MLD_VERSION_2);
3612*43a90889SApple OSS Distributions 
3613*43a90889SApple OSS Distributions 	ifp = mli->mli_ifp;
3614*43a90889SApple OSS Distributions 	MLI_UNLOCK(mli);
3615*43a90889SApple OSS Distributions 
3616*43a90889SApple OSS Distributions 	in6_multihead_lock_shared();
3617*43a90889SApple OSS Distributions 	IN6_FIRST_MULTI(step, inm);
3618*43a90889SApple OSS Distributions 	while (inm != NULL) {
3619*43a90889SApple OSS Distributions 		IN6M_LOCK(inm);
3620*43a90889SApple OSS Distributions 		if (inm->in6m_ifp != ifp) {
3621*43a90889SApple OSS Distributions 			goto next;
3622*43a90889SApple OSS Distributions 		}
3623*43a90889SApple OSS Distributions 
3624*43a90889SApple OSS Distributions 		switch (inm->in6m_state) {
3625*43a90889SApple OSS Distributions 		case MLD_NOT_MEMBER:
3626*43a90889SApple OSS Distributions 		case MLD_SILENT_MEMBER:
3627*43a90889SApple OSS Distributions 			break;
3628*43a90889SApple OSS Distributions 		case MLD_REPORTING_MEMBER:
3629*43a90889SApple OSS Distributions 		case MLD_IDLE_MEMBER:
3630*43a90889SApple OSS Distributions 		case MLD_LAZY_MEMBER:
3631*43a90889SApple OSS Distributions 		case MLD_SLEEPING_MEMBER:
3632*43a90889SApple OSS Distributions 		case MLD_AWAKENING_MEMBER:
3633*43a90889SApple OSS Distributions 			inm->in6m_state = MLD_REPORTING_MEMBER;
3634*43a90889SApple OSS Distributions 			MLI_LOCK(mli);
3635*43a90889SApple OSS Distributions 			retval = mld_v2_enqueue_group_record(&mli->mli_gq,
3636*43a90889SApple OSS Distributions 			    inm, 0, 0, 0, 0);
3637*43a90889SApple OSS Distributions 			MLI_UNLOCK(mli);
3638*43a90889SApple OSS Distributions 			MLD_PRINTF(("%s: enqueue record = %d\n",
3639*43a90889SApple OSS Distributions 			    __func__, retval));
3640*43a90889SApple OSS Distributions 			break;
3641*43a90889SApple OSS Distributions 		case MLD_G_QUERY_PENDING_MEMBER:
3642*43a90889SApple OSS Distributions 		case MLD_SG_QUERY_PENDING_MEMBER:
3643*43a90889SApple OSS Distributions 		case MLD_LEAVING_MEMBER:
3644*43a90889SApple OSS Distributions 			break;
3645*43a90889SApple OSS Distributions 		}
3646*43a90889SApple OSS Distributions next:
3647*43a90889SApple OSS Distributions 		IN6M_UNLOCK(inm);
3648*43a90889SApple OSS Distributions 		IN6_NEXT_MULTI(step, inm);
3649*43a90889SApple OSS Distributions 	}
3650*43a90889SApple OSS Distributions 	in6_multihead_lock_done();
3651*43a90889SApple OSS Distributions 
3652*43a90889SApple OSS Distributions 	MLI_LOCK(mli);
3653*43a90889SApple OSS Distributions 	mld_dispatch_queue_locked(mli, &mli->mli_gq, MLD_MAX_RESPONSE_BURST);
3654*43a90889SApple OSS Distributions 	MLI_LOCK_ASSERT_HELD(mli);
3655*43a90889SApple OSS Distributions 
3656*43a90889SApple OSS Distributions 	/*
3657*43a90889SApple OSS Distributions 	 * Slew transmission of bursts over 1 second intervals.
3658*43a90889SApple OSS Distributions 	 */
3659*43a90889SApple OSS Distributions 	if (mli->mli_gq.ifq_head != NULL) {
3660*43a90889SApple OSS Distributions 		mli->mli_v2_timer = 1 + MLD_RANDOM_DELAY(
3661*43a90889SApple OSS Distributions 			MLD_RESPONSE_BURST_INTERVAL);
3662*43a90889SApple OSS Distributions 	}
3663*43a90889SApple OSS Distributions 
3664*43a90889SApple OSS Distributions 	return mli->mli_v2_timer;
3665*43a90889SApple OSS Distributions }
3666*43a90889SApple OSS Distributions 
3667*43a90889SApple OSS Distributions /*
3668*43a90889SApple OSS Distributions  * Transmit the next pending message in the output queue.
3669*43a90889SApple OSS Distributions  *
3670*43a90889SApple OSS Distributions  * Must not be called with in6m_lockm or mli_lock held.
3671*43a90889SApple OSS Distributions  */
3672*43a90889SApple OSS Distributions __attribute__((noinline))
3673*43a90889SApple OSS Distributions static void
mld_dispatch_packet(struct mbuf * m)3674*43a90889SApple OSS Distributions mld_dispatch_packet(struct mbuf *m)
3675*43a90889SApple OSS Distributions {
3676*43a90889SApple OSS Distributions 	struct ip6_moptions     *im6o;
3677*43a90889SApple OSS Distributions 	struct ifnet            *ifp;
3678*43a90889SApple OSS Distributions 	struct ifnet            *__single oifp = NULL;
3679*43a90889SApple OSS Distributions 	mbuf_ref_t               m0, md;
3680*43a90889SApple OSS Distributions 	struct ip6_hdr          *ip6;
3681*43a90889SApple OSS Distributions 	struct icmp6_hdr        *icmp6;
3682*43a90889SApple OSS Distributions 	int                      error;
3683*43a90889SApple OSS Distributions 	int                      off;
3684*43a90889SApple OSS Distributions 	int                      type;
3685*43a90889SApple OSS Distributions 
3686*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: transmit 0x%llx\n", __func__,
3687*43a90889SApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3688*43a90889SApple OSS Distributions 
3689*43a90889SApple OSS Distributions 	/*
3690*43a90889SApple OSS Distributions 	 * Check if the ifnet is still attached.
3691*43a90889SApple OSS Distributions 	 */
3692*43a90889SApple OSS Distributions 	ifp = mld_restore_context(m);
3693*43a90889SApple OSS Distributions 	if (ifp == NULL || !ifnet_is_attached(ifp, 0)) {
3694*43a90889SApple OSS Distributions 		os_log_error(OS_LOG_DEFAULT, "%s: dropped 0x%llx as interface went away\n",
3695*43a90889SApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(m));
3696*43a90889SApple OSS Distributions 		m_freem(m);
3697*43a90889SApple OSS Distributions 		ip6stat.ip6s_noroute++;
3698*43a90889SApple OSS Distributions 		return;
3699*43a90889SApple OSS Distributions 	}
3700*43a90889SApple OSS Distributions 	im6o = ip6_allocmoptions(Z_WAITOK);
3701*43a90889SApple OSS Distributions 	if (im6o == NULL) {
3702*43a90889SApple OSS Distributions 		m_freem(m);
3703*43a90889SApple OSS Distributions 		return;
3704*43a90889SApple OSS Distributions 	}
3705*43a90889SApple OSS Distributions 
3706*43a90889SApple OSS Distributions 	im6o->im6o_multicast_hlim  = 1;
3707*43a90889SApple OSS Distributions 	im6o->im6o_multicast_loop = 0;
3708*43a90889SApple OSS Distributions 	im6o->im6o_multicast_ifp = ifp;
3709*43a90889SApple OSS Distributions 	if (m->m_flags & M_MLDV1) {
3710*43a90889SApple OSS Distributions 		m0 = m;
3711*43a90889SApple OSS Distributions 	} else {
3712*43a90889SApple OSS Distributions 		m0 = mld_v2_encap_report(ifp, m);
3713*43a90889SApple OSS Distributions 		if (m0 == NULL) {
3714*43a90889SApple OSS Distributions 			os_log_error(OS_LOG_DEFAULT, "%s: dropped 0x%llx\n", __func__,
3715*43a90889SApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m));
3716*43a90889SApple OSS Distributions 			/*
3717*43a90889SApple OSS Distributions 			 * mld_v2_encap_report() has already freed our mbuf.
3718*43a90889SApple OSS Distributions 			 */
3719*43a90889SApple OSS Distributions 			IM6O_REMREF(im6o);
3720*43a90889SApple OSS Distributions 			ip6stat.ip6s_odropped++;
3721*43a90889SApple OSS Distributions 			return;
3722*43a90889SApple OSS Distributions 		}
3723*43a90889SApple OSS Distributions 	}
3724*43a90889SApple OSS Distributions 	mld_scrub_context(m0);
3725*43a90889SApple OSS Distributions 	m->m_flags &= ~(M_PROTOFLAGS);
3726*43a90889SApple OSS Distributions 	m0->m_pkthdr.rcvif = lo_ifp;
3727*43a90889SApple OSS Distributions 
3728*43a90889SApple OSS Distributions 	ip6 = mtod(m0, struct ip6_hdr *);
3729*43a90889SApple OSS Distributions 	(void)in6_setscope(&ip6->ip6_dst, ifp, NULL);
3730*43a90889SApple OSS Distributions 	ip6_output_setdstifscope(m0, ifp->if_index, NULL);
3731*43a90889SApple OSS Distributions 	/*
3732*43a90889SApple OSS Distributions 	 * Retrieve the ICMPv6 type before handoff to ip6_output(),
3733*43a90889SApple OSS Distributions 	 * so we can bump the stats.
3734*43a90889SApple OSS Distributions 	 */
3735*43a90889SApple OSS Distributions 	md = m_getptr(m0, sizeof(struct ip6_hdr), &off);
3736*43a90889SApple OSS Distributions 	icmp6 = (struct icmp6_hdr *)(mtod(md, uint8_t *) + off);
3737*43a90889SApple OSS Distributions 	type = icmp6->icmp6_type;
3738*43a90889SApple OSS Distributions 
3739*43a90889SApple OSS Distributions 	if (ifp->if_eflags & IFEF_TXSTART) {
3740*43a90889SApple OSS Distributions 		/*
3741*43a90889SApple OSS Distributions 		 * Use control service class if the outgoing
3742*43a90889SApple OSS Distributions 		 * interface supports transmit-start model.
3743*43a90889SApple OSS Distributions 		 */
3744*43a90889SApple OSS Distributions 		(void) m_set_service_class(m0, MBUF_SC_CTL);
3745*43a90889SApple OSS Distributions 	}
3746*43a90889SApple OSS Distributions 
3747*43a90889SApple OSS Distributions 	error = ip6_output(m0, &mld_po, NULL, IPV6_UNSPECSRC, im6o,
3748*43a90889SApple OSS Distributions 	    &oifp, NULL);
3749*43a90889SApple OSS Distributions 
3750*43a90889SApple OSS Distributions 	IM6O_REMREF(im6o);
3751*43a90889SApple OSS Distributions 
3752*43a90889SApple OSS Distributions 	if (error) {
3753*43a90889SApple OSS Distributions 		os_log_error(OS_LOG_DEFAULT, "%s: ip6_output(0x%llx) = %d\n", __func__,
3754*43a90889SApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(m0), error);
3755*43a90889SApple OSS Distributions 		if (oifp != NULL) {
3756*43a90889SApple OSS Distributions 			ifnet_release(oifp);
3757*43a90889SApple OSS Distributions 		}
3758*43a90889SApple OSS Distributions 		return;
3759*43a90889SApple OSS Distributions 	}
3760*43a90889SApple OSS Distributions 
3761*43a90889SApple OSS Distributions 	icmp6stat.icp6s_outhist[type]++;
3762*43a90889SApple OSS Distributions 	if (oifp != NULL) {
3763*43a90889SApple OSS Distributions 		icmp6_ifstat_inc(oifp, ifs6_out_msg);
3764*43a90889SApple OSS Distributions 		switch (type) {
3765*43a90889SApple OSS Distributions 		case MLD_LISTENER_REPORT:
3766*43a90889SApple OSS Distributions 		case MLDV2_LISTENER_REPORT:
3767*43a90889SApple OSS Distributions 			icmp6_ifstat_inc(oifp, ifs6_out_mldreport);
3768*43a90889SApple OSS Distributions 			break;
3769*43a90889SApple OSS Distributions 		case MLD_LISTENER_DONE:
3770*43a90889SApple OSS Distributions 			icmp6_ifstat_inc(oifp, ifs6_out_mlddone);
3771*43a90889SApple OSS Distributions 			break;
3772*43a90889SApple OSS Distributions 		}
3773*43a90889SApple OSS Distributions 		ifnet_release(oifp);
3774*43a90889SApple OSS Distributions 	}
3775*43a90889SApple OSS Distributions }
3776*43a90889SApple OSS Distributions 
3777*43a90889SApple OSS Distributions /*
3778*43a90889SApple OSS Distributions  * Encapsulate an MLDv2 report.
3779*43a90889SApple OSS Distributions  *
3780*43a90889SApple OSS Distributions  * KAME IPv6 requires that hop-by-hop options be passed separately,
3781*43a90889SApple OSS Distributions  * and that the IPv6 header be prepended in a separate mbuf.
3782*43a90889SApple OSS Distributions  *
3783*43a90889SApple OSS Distributions  * Returns a pointer to the new mbuf chain head, or NULL if the
3784*43a90889SApple OSS Distributions  * allocation failed.
3785*43a90889SApple OSS Distributions  */
3786*43a90889SApple OSS Distributions static struct mbuf *
mld_v2_encap_report(struct ifnet * ifp,struct mbuf * m)3787*43a90889SApple OSS Distributions mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m)
3788*43a90889SApple OSS Distributions {
3789*43a90889SApple OSS Distributions 	struct mbuf             *mh;
3790*43a90889SApple OSS Distributions 	struct mldv2_report     *mld;
3791*43a90889SApple OSS Distributions 	struct ip6_hdr          *ip6;
3792*43a90889SApple OSS Distributions 	struct in6_ifaddr       *ia;
3793*43a90889SApple OSS Distributions 	int                      mldreclen;
3794*43a90889SApple OSS Distributions 
3795*43a90889SApple OSS Distributions 	VERIFY(m->m_flags & M_PKTHDR);
3796*43a90889SApple OSS Distributions 
3797*43a90889SApple OSS Distributions 	/*
3798*43a90889SApple OSS Distributions 	 * RFC3590: OK to send as :: or tentative during DAD.
3799*43a90889SApple OSS Distributions 	 */
3800*43a90889SApple OSS Distributions 	ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
3801*43a90889SApple OSS Distributions 	if (ia == NULL) {
3802*43a90889SApple OSS Distributions 		MLD_PRINTF(("%s: warning: ia is NULL\n", __func__));
3803*43a90889SApple OSS Distributions 	}
3804*43a90889SApple OSS Distributions 
3805*43a90889SApple OSS Distributions 	MGETHDR(mh, M_DONTWAIT, MT_HEADER);
3806*43a90889SApple OSS Distributions 	if (mh == NULL) {
3807*43a90889SApple OSS Distributions 		if (ia != NULL) {
3808*43a90889SApple OSS Distributions 			ifa_remref(&ia->ia_ifa);
3809*43a90889SApple OSS Distributions 		}
3810*43a90889SApple OSS Distributions 		m_freem(m);
3811*43a90889SApple OSS Distributions 		return NULL;
3812*43a90889SApple OSS Distributions 	}
3813*43a90889SApple OSS Distributions 	MH_ALIGN(mh, sizeof(struct ip6_hdr) + sizeof(struct mldv2_report));
3814*43a90889SApple OSS Distributions 
3815*43a90889SApple OSS Distributions 	mldreclen = m_length(m);
3816*43a90889SApple OSS Distributions 	MLD_PRINTF(("%s: mldreclen is %d\n", __func__, mldreclen));
3817*43a90889SApple OSS Distributions 
3818*43a90889SApple OSS Distributions 	mh->m_len = sizeof(struct ip6_hdr) + sizeof(struct mldv2_report);
3819*43a90889SApple OSS Distributions 	mh->m_pkthdr.len = sizeof(struct ip6_hdr) +
3820*43a90889SApple OSS Distributions 	    sizeof(struct mldv2_report) + mldreclen;
3821*43a90889SApple OSS Distributions 
3822*43a90889SApple OSS Distributions 	ip6 = mtod(mh, struct ip6_hdr *);
3823*43a90889SApple OSS Distributions 	ip6->ip6_flow = 0;
3824*43a90889SApple OSS Distributions 	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
3825*43a90889SApple OSS Distributions 	ip6->ip6_vfc |= IPV6_VERSION;
3826*43a90889SApple OSS Distributions 	ip6->ip6_nxt = IPPROTO_ICMPV6;
3827*43a90889SApple OSS Distributions 	if (ia != NULL) {
3828*43a90889SApple OSS Distributions 		IFA_LOCK(&ia->ia_ifa);
3829*43a90889SApple OSS Distributions 	}
3830*43a90889SApple OSS Distributions 	ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
3831*43a90889SApple OSS Distributions 	ip6_output_setsrcifscope(mh, IFSCOPE_NONE, ia);
3832*43a90889SApple OSS Distributions 
3833*43a90889SApple OSS Distributions 	if (ia != NULL) {
3834*43a90889SApple OSS Distributions 		IFA_UNLOCK(&ia->ia_ifa);
3835*43a90889SApple OSS Distributions 		ifa_remref(&ia->ia_ifa);
3836*43a90889SApple OSS Distributions 		ia = NULL;
3837*43a90889SApple OSS Distributions 	}
3838*43a90889SApple OSS Distributions 	ip6->ip6_dst = in6addr_linklocal_allv2routers;
3839*43a90889SApple OSS Distributions 	ip6_output_setdstifscope(mh, ifp->if_index, NULL);
3840*43a90889SApple OSS Distributions 	/* scope ID will be set in netisr */
3841*43a90889SApple OSS Distributions 
3842*43a90889SApple OSS Distributions 	mld = (struct mldv2_report *)(ip6 + 1);
3843*43a90889SApple OSS Distributions 	mld->mld_type = MLDV2_LISTENER_REPORT;
3844*43a90889SApple OSS Distributions 	mld->mld_code = 0;
3845*43a90889SApple OSS Distributions 	mld->mld_cksum = 0;
3846*43a90889SApple OSS Distributions 	mld->mld_v2_reserved = 0;
3847*43a90889SApple OSS Distributions 	mld->mld_v2_numrecs = htons(m->m_pkthdr.vt_nrecs);
3848*43a90889SApple OSS Distributions 	m->m_pkthdr.vt_nrecs = 0;
3849*43a90889SApple OSS Distributions 	m->m_flags &= ~M_PKTHDR;
3850*43a90889SApple OSS Distributions 
3851*43a90889SApple OSS Distributions 	mh->m_next = m;
3852*43a90889SApple OSS Distributions 	mld->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6,
3853*43a90889SApple OSS Distributions 	    sizeof(struct ip6_hdr), sizeof(struct mldv2_report) + mldreclen);
3854*43a90889SApple OSS Distributions 	return mh;
3855*43a90889SApple OSS Distributions }
3856*43a90889SApple OSS Distributions 
3857*43a90889SApple OSS Distributions #ifdef MLD_DEBUG
3858*43a90889SApple OSS Distributions static const char *
mld_rec_type_to_str(const int type)3859*43a90889SApple OSS Distributions mld_rec_type_to_str(const int type)
3860*43a90889SApple OSS Distributions {
3861*43a90889SApple OSS Distributions 	switch (type) {
3862*43a90889SApple OSS Distributions 	case MLD_CHANGE_TO_EXCLUDE_MODE:
3863*43a90889SApple OSS Distributions 		return "TO_EX";
3864*43a90889SApple OSS Distributions 	case MLD_CHANGE_TO_INCLUDE_MODE:
3865*43a90889SApple OSS Distributions 		return "TO_IN";
3866*43a90889SApple OSS Distributions 	case MLD_MODE_IS_EXCLUDE:
3867*43a90889SApple OSS Distributions 		return "MODE_EX";
3868*43a90889SApple OSS Distributions 	case MLD_MODE_IS_INCLUDE:
3869*43a90889SApple OSS Distributions 		return "MODE_IN";
3870*43a90889SApple OSS Distributions 	case MLD_ALLOW_NEW_SOURCES:
3871*43a90889SApple OSS Distributions 		return "ALLOW_NEW";
3872*43a90889SApple OSS Distributions 	case MLD_BLOCK_OLD_SOURCES:
3873*43a90889SApple OSS Distributions 		return "BLOCK_OLD";
3874*43a90889SApple OSS Distributions 	default:
3875*43a90889SApple OSS Distributions 		break;
3876*43a90889SApple OSS Distributions 	}
3877*43a90889SApple OSS Distributions 	return "unknown";
3878*43a90889SApple OSS Distributions }
3879*43a90889SApple OSS Distributions #endif
3880*43a90889SApple OSS Distributions 
3881*43a90889SApple OSS Distributions void
mld_init(void)3882*43a90889SApple OSS Distributions mld_init(void)
3883*43a90889SApple OSS Distributions {
3884*43a90889SApple OSS Distributions 	os_log(OS_LOG_DEFAULT, "%s: initializing\n", __func__);
3885*43a90889SApple OSS Distributions 
3886*43a90889SApple OSS Distributions 	ip6_initpktopts(&mld_po);
3887*43a90889SApple OSS Distributions 	mld_po.ip6po_hlim = 1;
3888*43a90889SApple OSS Distributions 	mld_po.ip6po_hbh = &mld_ra.hbh;
3889*43a90889SApple OSS Distributions 	mld_po.ip6po_prefer_tempaddr = IP6PO_TEMPADDR_NOTPREFER;
3890*43a90889SApple OSS Distributions 	mld_po.ip6po_flags = IP6PO_DONTFRAG;
3891*43a90889SApple OSS Distributions 	LIST_INIT(&mli_head);
3892*43a90889SApple OSS Distributions }
3893