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