Coverage Report

Created: 2026-06-25 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/SockFuzzer/third_party/xnu/bsd/netinet6/mld6.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3
 *
4
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5
 *
6
 * This file contains Original Code and/or Modifications of Original Code
7
 * as defined in and that are subject to the Apple Public Source License
8
 * Version 2.0 (the 'License'). You may not use this file except in
9
 * compliance with the License. The rights granted to you under the License
10
 * may not be used to create, or enable the creation or redistribution of,
11
 * unlawful or unlicensed copies of an Apple operating system, or to
12
 * circumvent, violate, or enable the circumvention or violation of, any
13
 * terms of an Apple operating system software license agreement.
14
 *
15
 * Please obtain a copy of the License at
16
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17
 *
18
 * The Original Code and all software distributed under the License are
19
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23
 * Please see the License for the specific language governing rights and
24
 * limitations under the License.
25
 *
26
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27
 */
28
/*-
29
 * Copyright (c) 2009 Bruce Simpson.
30
 *
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.
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.
42
 *
43
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
44
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
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
 */
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
 */
100
101
#include <sys/cdefs.h>
102
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>
108
#include <sys/sysctl.h>
109
#include <sys/kernel.h>
110
#include <sys/malloc.h>
111
#include <sys/mcache.h>
112
113
#include <dev/random/randomdev.h>
114
115
#include <kern/zalloc.h>
116
117
#include <net/if.h>
118
#include <net/route.h>
119
120
#include <netinet/in.h>
121
#include <netinet/in_var.h>
122
#include <netinet6/in6_var.h>
123
#include <netinet/ip6.h>
124
#include <netinet6/ip6_var.h>
125
#include <netinet6/scope6_var.h>
126
#include <netinet/icmp6.h>
127
#include <netinet6/mld6.h>
128
#include <netinet6/mld6_var.h>
129
130
/* Lock group and attribute for mld_mtx */
131
static lck_attr_t       *mld_mtx_attr;
132
static lck_grp_t        *mld_mtx_grp;
133
static 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
 *
158
 * Thus, the permitted lock order is:
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
 */
165
static decl_lck_mtx_data(, mld_mtx);
166
167
SLIST_HEAD(mld_in6m_relhead, in6_multi);
168
169
static void     mli_initvar(struct mld_ifinfo *, struct ifnet *, int);
170
static struct mld_ifinfo *mli_alloc(zalloc_flags_t);
171
static void     mli_free(struct mld_ifinfo *);
172
static void     mli_delete(const struct ifnet *, struct mld_in6m_relhead *);
173
static void     mld_dispatch_packet(struct mbuf *);
174
static void     mld_final_leave(struct in6_multi *, struct mld_ifinfo *,
175
    struct mld_tparams *);
176
static int      mld_handle_state_change(struct in6_multi *, struct mld_ifinfo *,
177
    struct mld_tparams *);
178
static int      mld_initial_join(struct in6_multi *, struct mld_ifinfo *,
179
    struct mld_tparams *, const int);
180
#ifdef MLD_DEBUG
181
static const char *     mld_rec_type_to_str(const int);
182
#endif
183
static uint32_t mld_set_version(struct mld_ifinfo *, const int);
184
static void     mld_flush_relq(struct mld_ifinfo *, struct mld_in6m_relhead *);
185
static void     mld_dispatch_queue_locked(struct mld_ifinfo *, struct ifqueue *, int);
186
static int      mld_v1_input_query(struct ifnet *, const struct ip6_hdr *,
187
    /*const*/ struct mld_hdr *);
188
static int      mld_v1_input_report(struct ifnet *, struct mbuf *,
189
    const struct ip6_hdr *, /*const*/ struct mld_hdr *);
190
static void     mld_v1_process_group_timer(struct in6_multi *, const int);
191
static void     mld_v1_process_querier_timers(struct mld_ifinfo *);
192
static int      mld_v1_transmit_report(struct in6_multi *, const uint8_t);
193
static uint32_t mld_v1_update_group(struct in6_multi *, const int);
194
static void     mld_v2_cancel_link_timers(struct mld_ifinfo *);
195
static uint32_t mld_v2_dispatch_general_query(struct mld_ifinfo *);
196
static struct mbuf *
197
mld_v2_encap_report(struct ifnet *, struct mbuf *);
198
static int      mld_v2_enqueue_filter_change(struct ifqueue *,
199
    struct in6_multi *);
200
static int      mld_v2_enqueue_group_record(struct ifqueue *,
201
    struct in6_multi *, const int, const int, const int,
202
    const int);
203
static int      mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
204
    struct mbuf *, const int, const int);
205
static int      mld_v2_merge_state_changes(struct in6_multi *,
206
    struct ifqueue *);
207
static void     mld_v2_process_group_timers(struct mld_ifinfo *,
208
    struct ifqueue *, struct ifqueue *,
209
    struct in6_multi *, const int);
210
static int      mld_v2_process_group_query(struct in6_multi *,
211
    int, struct mbuf *, const int);
212
static int      sysctl_mld_gsr SYSCTL_HANDLER_ARGS;
213
static int      sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS;
214
static int      sysctl_mld_v2enable SYSCTL_HANDLER_ARGS;
215
216
static int mld_timeout_run;             /* MLD timer is scheduled to run */
217
void mld_timeout(void *);
218
static void mld_sched_timeout(void);
219
220
/*
221
 * Normative references: RFC 2710, RFC 3590, RFC 3810.
222
 */
223
static struct timeval mld_gsrdelay = {.tv_sec = 10, .tv_usec = 0};
224
static LIST_HEAD(, mld_ifinfo) mli_head;
225
226
static int querier_present_timers_running6;
227
static int interface_timers_running6;
228
static int state_change_timers_running6;
229
static int current_state_timers_running6;
230
231
static unsigned int mld_mli_list_genid;
232
/*
233
 * Subsystem lock macros.
234
 */
235
#define MLD_LOCK()                      \
236
324k
  lck_mtx_lock(&mld_mtx)
237
#define MLD_LOCK_ASSERT_HELD()          \
238
443
  LCK_MTX_ASSERT(&mld_mtx, LCK_MTX_ASSERT_OWNED)
239
#define MLD_LOCK_ASSERT_NOTHELD()       \
240
7.35k
  LCK_MTX_ASSERT(&mld_mtx, LCK_MTX_ASSERT_NOTOWNED)
241
#define MLD_UNLOCK()                    \
242
324k
  lck_mtx_unlock(&mld_mtx)
243
244
0
#define MLD_ADD_DETACHED_IN6M(_head, _in6m) {                           \
245
0
  SLIST_INSERT_HEAD(_head, _in6m, in6m_dtle);                     \
246
0
}
247
248
322k
#define MLD_REMOVE_DETACHED_IN6M(_head) {                               \
249
322k
  struct in6_multi *_in6m, *_inm_tmp;                             \
250
322k
  SLIST_FOREACH_SAFE(_in6m, _head, in6m_dtle, _inm_tmp) {         \
251
0
          SLIST_REMOVE(_head, _in6m, in6_multi, in6m_dtle);       \
252
0
          IN6M_REMREF(_in6m);                                     \
253
0
  }                                                               \
254
322k
  VERIFY(SLIST_EMPTY(_head));                                     \
255
322k
}
256
257
static ZONE_DECLARE(mli_zone, "mld_ifinfo",
258
    sizeof(struct mld_ifinfo), ZC_ZFREE_CLEARMEM);
259
260
SYSCTL_DECL(_net_inet6);        /* Note: Not in any common header. */
261
262
SYSCTL_NODE(_net_inet6, OID_AUTO, mld, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
263
    "IPv6 Multicast Listener Discovery");
264
SYSCTL_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
269
SYSCTL_NODE(_net_inet6_mld, OID_AUTO, ifinfo, CTLFLAG_RD | CTLFLAG_LOCKED,
270
    sysctl_mld_ifinfo, "Per-interface MLDv2 state");
271
272
static int      mld_v1enable = 1;
273
SYSCTL_INT(_net_inet6_mld, OID_AUTO, v1enable, CTLFLAG_RW | CTLFLAG_LOCKED,
274
    &mld_v1enable, 0, "Enable fallback to MLDv1");
275
276
static int      mld_v2enable = 1;
277
SYSCTL_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
282
static int      mld_use_allow = 1;
283
SYSCTL_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
287
int mld_debug = 0;
288
SYSCTL_INT(_net_inet6_mld, OID_AUTO,
289
    debug, CTLFLAG_RW | CTLFLAG_LOCKED, &mld_debug, 0, "");
290
#endif
291
/*
292
 * Packed Router Alert option structure declaration.
293
 */
294
struct mld_raopt {
295
  struct ip6_hbh          hbh;
296
  struct ip6_opt          pad;
297
  struct ip6_opt_router   ra;
298
} __packed;
299
300
/*
301
 * Router Alert hop-by-hop option header.
302
 */
303
static struct mld_raopt mld_ra = {
304
  .hbh = { .ip6h_nxt = 0, .ip6h_len = 0 },
305
  .pad = { .ip6o_type = IP6OPT_PADN, .ip6o_len = 0 },
306
  .ra = {
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) }
311
  }
312
};
313
static struct ip6_pktopts mld_po;
314
315
/* Store MLDv2 record count in the module private scratch space */
316
0
#define vt_nrecs        pkt_mpriv.__mpriv_u.__mpriv32[0].__mpriv32_u.__val16[0]
317
318
static __inline void
319
mld_save_context(struct mbuf *m, struct ifnet *ifp)
320
0
{
321
0
  m->m_pkthdr.rcvif = ifp;
322
0
}
323
324
static __inline void
325
mld_scrub_context(struct mbuf *m)
326
0
{
327
0
  m->m_pkthdr.rcvif = NULL;
328
0
}
329
330
/*
331
 * Restore context from a queued output chain.
332
 * Return saved ifp.
333
 */
334
static __inline struct ifnet *
335
mld_restore_context(struct mbuf *m)
336
0
{
337
0
  return m->m_pkthdr.rcvif;
338
0
}
339
340
/*
341
 * Retrieve or set threshold between group-source queries in seconds.
342
 */
343
static int
344
sysctl_mld_gsr SYSCTL_HANDLER_ARGS
345
0
{
346
0
#pragma unused(arg1, arg2)
347
0
  int error;
348
0
  int i;
349
350
0
  MLD_LOCK();
351
352
0
  i = (int)mld_gsrdelay.tv_sec;
353
354
0
  error = sysctl_handle_int(oidp, &i, 0, req);
355
0
  if (error || !req->newptr) {
356
0
    goto out_locked;
357
0
  }
358
359
0
  if (i < -1 || i >= 60) {
360
0
    error = EINVAL;
361
0
    goto out_locked;
362
0
  }
363
364
0
  mld_gsrdelay.tv_sec = i;
365
366
0
out_locked:
367
0
  MLD_UNLOCK();
368
0
  return error;
369
0
}
370
/*
371
 * Expose struct mld_ifinfo to userland, keyed by ifindex.
372
 * For use by ifmcstat(8).
373
 *
374
 */
375
static int
376
sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS
377
0
{
378
0
#pragma unused(oidp)
379
0
  int                     *name;
380
0
  int                      error;
381
0
  u_int                    namelen;
382
0
  struct ifnet            *ifp;
383
0
  struct mld_ifinfo       *mli;
384
0
  struct mld_ifinfo_u     mli_u;
385
386
0
  name = (int *)arg1;
387
0
  namelen = arg2;
388
389
0
  if (req->newptr != USER_ADDR_NULL) {
390
0
    return EPERM;
391
0
  }
392
393
0
  if (namelen != 1) {
394
0
    return EINVAL;
395
0
  }
396
397
0
  MLD_LOCK();
398
399
0
  if (name[0] <= 0 || name[0] > (u_int)if_index) {
400
0
    error = ENOENT;
401
0
    goto out_locked;
402
0
  }
403
404
0
  error = ENOENT;
405
406
0
  ifnet_head_lock_shared();
407
0
  ifp = ifindex2ifnet[name[0]];
408
0
  ifnet_head_done();
409
0
  if (ifp == NULL) {
410
0
    goto out_locked;
411
0
  }
412
413
0
  bzero(&mli_u, sizeof(mli_u));
414
415
0
  LIST_FOREACH(mli, &mli_head, mli_link) {
416
0
    MLI_LOCK(mli);
417
0
    if (ifp != mli->mli_ifp) {
418
0
      MLI_UNLOCK(mli);
419
0
      continue;
420
0
    }
421
422
0
    mli_u.mli_ifindex = mli->mli_ifp->if_index;
423
0
    mli_u.mli_version = mli->mli_version;
424
0
    mli_u.mli_v1_timer = mli->mli_v1_timer;
425
0
    mli_u.mli_v2_timer = mli->mli_v2_timer;
426
0
    mli_u.mli_flags = mli->mli_flags;
427
0
    mli_u.mli_rv = mli->mli_rv;
428
0
    mli_u.mli_qi = mli->mli_qi;
429
0
    mli_u.mli_qri = mli->mli_qri;
430
0
    mli_u.mli_uri = mli->mli_uri;
431
0
    MLI_UNLOCK(mli);
432
433
0
    error = SYSCTL_OUT(req, &mli_u, sizeof(mli_u));
434
0
    break;
435
0
  }
436
437
0
out_locked:
438
0
  MLD_UNLOCK();
439
0
  return error;
440
0
}
441
442
static int
443
sysctl_mld_v2enable SYSCTL_HANDLER_ARGS
444
0
{
445
0
#pragma unused(arg1, arg2)
446
0
  int error;
447
0
  int i;
448
0
  struct mld_ifinfo *mli;
449
0
  struct mld_tparams mtp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
450
451
0
  MLD_LOCK();
452
453
0
  i = mld_v2enable;
454
455
0
  error = sysctl_handle_int(oidp, &i, 0, req);
456
0
  if (error || !req->newptr) {
457
0
    goto out_locked;
458
0
  }
459
460
0
  if (i < 0 || i > 1) {
461
0
    error = EINVAL;
462
0
    goto out_locked;
463
0
  }
464
465
0
  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
   */
471
0
  if (mld_v2enable == 1) {
472
0
    goto out_locked;
473
0
  }
474
475
0
  LIST_FOREACH(mli, &mli_head, mli_link) {
476
0
    MLI_LOCK(mli);
477
0
    if (mld_set_version(mli, MLD_VERSION_1) > 0) {
478
0
      mtp.qpt = 1;
479
0
    }
480
0
    MLI_UNLOCK(mli);
481
0
  }
482
483
0
out_locked:
484
0
  MLD_UNLOCK();
485
486
0
  mld_set_timeout(&mtp);
487
488
0
  return error;
489
0
}
490
491
/*
492
 * Dispatch an entire queue of pending packet chains.
493
 *
494
 * Must not be called with in6m_lock held.
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.
501
 */
502
static void
503
mld_dispatch_queue_locked(struct mld_ifinfo *mli, struct ifqueue *ifq, int limit)
504
443
{
505
443
  struct mbuf *m;
506
507
443
  MLD_LOCK_ASSERT_HELD();
508
509
443
  if (mli != NULL) {
510
443
    MLI_LOCK_ASSERT_HELD(mli);
511
443
  }
512
513
443
  for (;;) {
514
443
    IF_DEQUEUE(ifq, m);
515
443
    if (m == NULL) {
516
443
      break;
517
443
    }
518
0
    MLD_PRINTF(("%s: dispatch 0x%llx from 0x%llx\n", __func__,
519
0
        (uint64_t)VM_KERNEL_ADDRPERM(ifq),
520
0
        (uint64_t)VM_KERNEL_ADDRPERM(m)));
521
522
0
    if (mli != NULL) {
523
0
      MLI_UNLOCK(mli);
524
0
    }
525
0
    MLD_UNLOCK();
526
527
0
    mld_dispatch_packet(m);
528
529
0
    MLD_LOCK();
530
0
    if (mli != NULL) {
531
0
      MLI_LOCK(mli);
532
0
    }
533
534
0
    if (--limit == 0) {
535
0
      break;
536
0
    }
537
0
  }
538
539
443
  if (mli != NULL) {
540
443
    MLI_LOCK_ASSERT_HELD(mli);
541
443
  }
542
443
}
543
544
/*
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.
555
 */
556
static __inline__ int
557
mld_is_addr_reported(const struct in6_addr *addr)
558
0
{
559
0
  VERIFY(IN6_IS_ADDR_MULTICAST(addr));
560
561
0
  if (IPV6_ADDR_MC_SCOPE(addr) == IPV6_ADDR_SCOPE_NODELOCAL) {
562
0
    return 0;
563
0
  }
564
565
0
  if (IPV6_ADDR_MC_SCOPE(addr) == IPV6_ADDR_SCOPE_LINKLOCAL && !IN6_IS_ADDR_UNICAST_BASED_MULTICAST(addr)) {
566
0
    struct in6_addr tmp = *addr;
567
0
    in6_clearscope(&tmp);
568
0
    if (IN6_ARE_ADDR_EQUAL(&tmp, &in6addr_linklocal_allnodes)) {
569
0
      return 0;
570
0
    }
571
0
  }
572
573
0
  return 1;
574
0
}
575
576
/*
577
 * Attach MLD when PF_INET6 is attached to an interface.
578
 */
579
struct mld_ifinfo *
580
mld_domifattach(struct ifnet *ifp, zalloc_flags_t how)
581
11
{
582
11
  struct mld_ifinfo *mli;
583
584
11
  MLD_PRINTF(("%s: called for ifp 0x%llx(%s)\n", __func__,
585
11
      (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
586
587
11
  mli = mli_alloc(how);
588
11
  if (mli == NULL) {
589
0
    return NULL;
590
0
  }
591
592
11
  MLD_LOCK();
593
594
11
  MLI_LOCK(mli);
595
11
  mli_initvar(mli, ifp, 0);
596
11
  mli->mli_debug |= IFD_ATTACHED;
597
11
  MLI_ADDREF_LOCKED(mli); /* hold a reference for mli_head */
598
11
  MLI_ADDREF_LOCKED(mli); /* hold a reference for caller */
599
11
  MLI_UNLOCK(mli);
600
11
  ifnet_lock_shared(ifp);
601
11
  mld6_initsilent(ifp, mli);
602
11
  ifnet_lock_done(ifp);
603
604
11
  LIST_INSERT_HEAD(&mli_head, mli, mli_link);
605
11
  mld_mli_list_genid++;
606
607
11
  MLD_UNLOCK();
608
609
11
  MLD_PRINTF(("%s: allocate mld_ifinfo for ifp 0x%llx(%s)\n",
610
11
      __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
611
612
11
  return mli;
613
11
}
614
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
 */
619
void
620
mld_domifreattach(struct mld_ifinfo *mli)
621
0
{
622
0
  struct ifnet *ifp;
623
624
0
  MLD_LOCK();
625
626
0
  MLI_LOCK(mli);
627
0
  VERIFY(!(mli->mli_debug & IFD_ATTACHED));
628
0
  ifp = mli->mli_ifp;
629
0
  VERIFY(ifp != NULL);
630
0
  mli_initvar(mli, ifp, 1);
631
0
  mli->mli_debug |= IFD_ATTACHED;
632
0
  MLI_ADDREF_LOCKED(mli); /* hold a reference for mli_head */
633
0
  MLI_UNLOCK(mli);
634
0
  ifnet_lock_shared(ifp);
635
0
  mld6_initsilent(ifp, mli);
636
0
  ifnet_lock_done(ifp);
637
638
0
  LIST_INSERT_HEAD(&mli_head, mli, mli_link);
639
0
  mld_mli_list_genid++;
640
641
0
  MLD_UNLOCK();
642
643
0
  MLD_PRINTF(("%s: reattached mld_ifinfo for ifp 0x%llx(%s)\n",
644
0
      __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
645
0
}
646
647
/*
648
 * Hook for domifdetach.
649
 */
650
void
651
mld_domifdetach(struct ifnet *ifp)
652
0
{
653
0
  SLIST_HEAD(, in6_multi) in6m_dthead;
654
655
0
  SLIST_INIT(&in6m_dthead);
656
657
0
  MLD_PRINTF(("%s: called for ifp 0x%llx(%s)\n", __func__,
658
0
      (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
659
660
0
  MLD_LOCK();
661
0
  mli_delete(ifp, (struct mld_in6m_relhead *)&in6m_dthead);
662
0
  MLD_UNLOCK();
663
664
  /* Now that we're dropped all locks, release detached records */
665
0
  MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
666
0
}
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
 */
674
static void
675
mli_delete(const struct ifnet *ifp, struct mld_in6m_relhead *in6m_dthead)
676
0
{
677
0
  struct mld_ifinfo *mli, *tmli;
678
679
0
  MLD_LOCK_ASSERT_HELD();
680
681
0
  LIST_FOREACH_SAFE(mli, &mli_head, mli_link, tmli) {
682
0
    MLI_LOCK(mli);
683
0
    if (mli->mli_ifp == ifp) {
684
      /*
685
       * Free deferred General Query responses.
686
       */
687
0
      IF_DRAIN(&mli->mli_gq);
688
0
      IF_DRAIN(&mli->mli_v1q);
689
0
      mld_flush_relq(mli, in6m_dthead);
690
0
      VERIFY(SLIST_EMPTY(&mli->mli_relinmhead));
691
0
      mli->mli_debug &= ~IFD_ATTACHED;
692
0
      MLI_UNLOCK(mli);
693
694
0
      LIST_REMOVE(mli, mli_link);
695
0
      MLI_REMREF(mli); /* release mli_head reference */
696
0
      mld_mli_list_genid++;
697
0
      return;
698
0
    }
699
0
    MLI_UNLOCK(mli);
700
0
  }
701
0
  panic("%s: mld_ifinfo not found for ifp %p(%s)\n", __func__,
702
0
      ifp, ifp->if_xname);
703
0
}
704
705
__private_extern__ void
706
mld6_initsilent(struct ifnet *ifp, struct mld_ifinfo *mli)
707
11
{
708
11
  ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_OWNED);
709
710
11
  MLI_LOCK_ASSERT_NOTHELD(mli);
711
11
  MLI_LOCK(mli);
712
11
  if (!(ifp->if_flags & IFF_MULTICAST) &&
713
10
      (ifp->if_eflags & (IFEF_IPV6_ND6ALT | IFEF_LOCALNET_PRIVATE))) {
714
0
    mli->mli_flags |= MLIF_SILENT;
715
11
  } else {
716
11
    mli->mli_flags &= ~MLIF_SILENT;
717
11
  }
718
11
  MLI_UNLOCK(mli);
719
11
}
720
721
static void
722
mli_initvar(struct mld_ifinfo *mli, struct ifnet *ifp, int reattach)
723
11
{
724
11
  MLI_LOCK_ASSERT_HELD(mli);
725
726
11
  mli->mli_ifp = ifp;
727
11
  if (mld_v2enable) {
728
11
    mli->mli_version = MLD_VERSION_2;
729
11
  } else {
730
0
    mli->mli_version = MLD_VERSION_1;
731
0
  }
732
11
  mli->mli_flags = 0;
733
11
  mli->mli_rv = MLD_RV_INIT;
734
11
  mli->mli_qi = MLD_QI_INIT;
735
11
  mli->mli_qri = MLD_QRI_INIT;
736
11
  mli->mli_uri = MLD_URI_INIT;
737
738
11
  if (mld_use_allow) {
739
11
    mli->mli_flags |= MLIF_USEALLOW;
740
11
  }
741
11
  if (!reattach) {
742
11
    SLIST_INIT(&mli->mli_relinmhead);
743
11
  }
744
745
  /*
746
   * Responses to general queries are subject to bounds.
747
   */
748
11
  mli->mli_gq.ifq_maxlen = MLD_MAX_RESPONSE_PACKETS;
749
11
  mli->mli_v1q.ifq_maxlen = MLD_MAX_RESPONSE_PACKETS;
750
11
}
751
752
static struct mld_ifinfo *
753
mli_alloc(zalloc_flags_t how)
754
11
{
755
11
  struct mld_ifinfo *mli = zalloc_flags(mli_zone, how | Z_ZERO);
756
11
  if (mli != NULL) {
757
11
    lck_mtx_init(&mli->mli_lock, mld_mtx_grp, mld_mtx_attr);
758
11
    mli->mli_debug |= IFD_ALLOC;
759
11
  }
760
11
  return mli;
761
11
}
762
763
static void
764
mli_free(struct mld_ifinfo *mli)
765
0
{
766
0
  MLI_LOCK(mli);
767
0
  if (mli->mli_debug & IFD_ATTACHED) {
768
0
    panic("%s: attached mli=%p is being freed", __func__, mli);
769
    /* NOTREACHED */
770
0
  } else if (mli->mli_ifp != NULL) {
771
0
    panic("%s: ifp not NULL for mli=%p", __func__, mli);
772
    /* NOTREACHED */
773
0
  } else if (!(mli->mli_debug & IFD_ALLOC)) {
774
0
    panic("%s: mli %p cannot be freed", __func__, mli);
775
    /* NOTREACHED */
776
0
  } else if (mli->mli_refcnt != 0) {
777
0
    panic("%s: non-zero refcnt mli=%p", __func__, mli);
778
    /* NOTREACHED */
779
0
  }
780
0
  mli->mli_debug &= ~IFD_ALLOC;
781
0
  MLI_UNLOCK(mli);
782
783
0
  lck_mtx_destroy(&mli->mli_lock, mld_mtx_grp);
784
0
  zfree(mli_zone, mli);
785
0
}
786
787
void
788
mli_addref(struct mld_ifinfo *mli, int locked)
789
5.34k
{
790
5.34k
  if (!locked) {
791
4
    MLI_LOCK_SPIN(mli);
792
5.33k
  } else {
793
5.33k
    MLI_LOCK_ASSERT_HELD(mli);
794
5.33k
  }
795
796
5.34k
  if (++mli->mli_refcnt == 0) {
797
0
    panic("%s: mli=%p wraparound refcnt", __func__, mli);
798
    /* NOTREACHED */
799
0
  }
800
5.34k
  if (!locked) {
801
4
    MLI_UNLOCK(mli);
802
4
  }
803
5.34k
}
804
805
void
806
mli_remref(struct mld_ifinfo *mli)
807
5.31k
{
808
5.31k
  SLIST_HEAD(, in6_multi) in6m_dthead;
809
5.31k
  struct ifnet *ifp;
810
811
5.31k
  MLI_LOCK_SPIN(mli);
812
813
5.31k
  if (mli->mli_refcnt == 0) {
814
0
    panic("%s: mli=%p negative refcnt", __func__, mli);
815
    /* NOTREACHED */
816
0
  }
817
818
5.31k
  --mli->mli_refcnt;
819
5.31k
  if (mli->mli_refcnt > 0) {
820
5.31k
    MLI_UNLOCK(mli);
821
5.31k
    return;
822
5.31k
  }
823
824
0
  ifp = mli->mli_ifp;
825
0
  mli->mli_ifp = NULL;
826
0
  IF_DRAIN(&mli->mli_gq);
827
0
  IF_DRAIN(&mli->mli_v1q);
828
0
  SLIST_INIT(&in6m_dthead);
829
0
  mld_flush_relq(mli, (struct mld_in6m_relhead *)&in6m_dthead);
830
0
  VERIFY(SLIST_EMPTY(&mli->mli_relinmhead));
831
0
  MLI_UNLOCK(mli);
832
833
  /* Now that we're dropped all locks, release detached records */
834
0
  MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
835
836
0
  MLD_PRINTF(("%s: freeing mld_ifinfo for ifp 0x%llx(%s)\n",
837
0
      __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
838
839
0
  mli_free(mli);
840
0
}
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
 */
849
static int
850
mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
851
    /*const*/ struct mld_hdr *mld)
852
1.76k
{
853
1.76k
  struct mld_ifinfo       *mli;
854
1.76k
  struct in6_multi        *inm;
855
1.76k
  int                      err = 0, is_general_query;
856
1.76k
  uint16_t                 timer;
857
1.76k
  struct mld_tparams       mtp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
858
859
1.76k
  MLD_LOCK_ASSERT_NOTHELD();
860
861
1.76k
  is_general_query = 0;
862
863
1.76k
  if (!mld_v1enable) {
864
0
    MLD_PRINTF(("%s: ignore v1 query %s on ifp 0x%llx(%s)\n",
865
0
        __func__, ip6_sprintf(&mld->mld_addr),
866
0
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
867
0
    goto done;
868
0
  }
869
870
  /*
871
   * RFC3810 Section 6.2: MLD queries must originate from
872
   * a router's link-local address.
873
   */
874
1.76k
  if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
875
115
    MLD_PRINTF(("%s: ignore v1 query src %s on ifp 0x%llx(%s)\n",
876
115
        __func__, ip6_sprintf(&ip6->ip6_src),
877
115
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
878
115
    goto done;
879
115
  }
880
881
  /*
882
   * Do address field validation upfront before we accept
883
   * the query.
884
   */
885
1.64k
  if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
886
    /*
887
     * MLDv1 General Query.
888
     * If this was not sent to the all-nodes group, ignore it.
889
     */
890
337
    struct in6_addr          dst;
891
892
337
    dst = ip6->ip6_dst;
893
337
    in6_clearscope(&dst);
894
337
    if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes)) {
895
322
      err = EINVAL;
896
322
      goto done;
897
322
    }
898
15
    is_general_query = 1;
899
1.31k
  } else {
900
    /*
901
     * Embed scope ID of receiving interface in MLD query for
902
     * lookup whilst we don't hold other locks.
903
     */
904
1.31k
    (void)in6_setscope(&mld->mld_addr, ifp, NULL);
905
1.31k
  }
906
907
  /*
908
   * Switch to MLDv1 host compatibility mode.
909
   */
910
1.32k
  mli = MLD_IFINFO(ifp);
911
1.32k
  VERIFY(mli != NULL);
912
913
1.32k
  MLI_LOCK(mli);
914
1.32k
  mtp.qpt = mld_set_version(mli, MLD_VERSION_1);
915
1.32k
  MLI_UNLOCK(mli);
916
917
1.32k
  timer = ntohs(mld->mld_maxdelay) / MLD_TIMER_SCALE;
918
1.32k
  if (timer == 0) {
919
1.27k
    timer = 1;
920
1.27k
  }
921
922
1.32k
  if (is_general_query) {
923
15
    struct in6_multistep step;
924
925
15
    MLD_PRINTF(("%s: process v1 general query on ifp 0x%llx(%s)\n",
926
15
        __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
927
    /*
928
     * For each reporting group joined on this
929
     * interface, kick the report timer.
930
     */
931
15
    in6_multihead_lock_shared();
932
15
    IN6_FIRST_MULTI(step, inm);
933
75
    while (inm != NULL) {
934
60
      IN6M_LOCK(inm);
935
60
      if (inm->in6m_ifp == ifp) {
936
60
        mtp.cst += mld_v1_update_group(inm, timer);
937
60
      }
938
60
      IN6M_UNLOCK(inm);
939
60
      IN6_NEXT_MULTI(step, inm);
940
60
    }
941
15
    in6_multihead_lock_done();
942
1.31k
  } 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
1.31k
    in6_multihead_lock_shared();
949
1.31k
    IN6_LOOKUP_MULTI(&mld->mld_addr, ifp, inm);
950
1.31k
    in6_multihead_lock_done();
951
952
1.31k
    if (inm != NULL) {
953
1
      IN6M_LOCK(inm);
954
1
      MLD_PRINTF(("%s: process v1 query %s on "
955
1
          "ifp 0x%llx(%s)\n", __func__,
956
1
          ip6_sprintf(&mld->mld_addr),
957
1
          (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
958
1
      mtp.cst = mld_v1_update_group(inm, timer);
959
1
      IN6M_UNLOCK(inm);
960
1
      IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
961
1
    }
962
    /* XXX Clear embedded scope ID as userland won't expect it. */
963
1.31k
    in6_clearscope(&mld->mld_addr);
964
1.31k
  }
965
1.76k
done:
966
1.76k
  mld_set_timeout(&mtp);
967
968
1.76k
  return err;
969
1.32k
}
970
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
981
 * for group and group-source query responses.
982
 *
983
 * Unlike MLDv2, the delay per group should be jittered
984
 * to avoid bursts of MLDv1 reports.
985
 */
986
static uint32_t
987
mld_v1_update_group(struct in6_multi *inm, const int timer)
988
61
{
989
61
  IN6M_LOCK_ASSERT_HELD(inm);
990
991
61
  MLD_PRINTF(("%s: %s/%s timer=%d\n", __func__,
992
61
      ip6_sprintf(&inm->in6m_addr),
993
61
      if_name(inm->in6m_ifp), timer));
994
995
61
  switch (inm->in6m_state) {
996
0
  case MLD_NOT_MEMBER:
997
61
  case MLD_SILENT_MEMBER:
998
61
    break;
999
0
  case MLD_REPORTING_MEMBER:
1000
0
    if (inm->in6m_timer != 0 &&
1001
0
        inm->in6m_timer <= timer) {
1002
0
      MLD_PRINTF(("%s: REPORTING and timer running, "
1003
0
          "skipping.\n", __func__));
1004
0
      break;
1005
0
    }
1006
0
    OS_FALLTHROUGH;
1007
0
  case MLD_SG_QUERY_PENDING_MEMBER:
1008
0
  case MLD_G_QUERY_PENDING_MEMBER:
1009
0
  case MLD_IDLE_MEMBER:
1010
0
  case MLD_LAZY_MEMBER:
1011
0
  case MLD_AWAKENING_MEMBER:
1012
0
    MLD_PRINTF(("%s: ->REPORTING\n", __func__));
1013
0
    inm->in6m_state = MLD_REPORTING_MEMBER;
1014
0
    inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1015
0
    break;
1016
0
  case MLD_SLEEPING_MEMBER:
1017
0
    MLD_PRINTF(("%s: ->AWAKENING\n", __func__));
1018
0
    inm->in6m_state = MLD_AWAKENING_MEMBER;
1019
0
    break;
1020
0
  case MLD_LEAVING_MEMBER:
1021
0
    break;
1022
61
  }
1023
1024
61
  return inm->in6m_timer;
1025
61
}
1026
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
 */
1035
static int
1036
mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
1037
    struct mbuf *m, const int off, const int icmp6len)
1038
1.90k
{
1039
1.90k
  struct mld_ifinfo       *mli;
1040
1.90k
  struct mldv2_query      *mld;
1041
1.90k
  struct in6_multi        *inm;
1042
1.90k
  uint32_t                 maxdelay, nsrc, qqi, timer;
1043
1.90k
  int                      err = 0, is_general_query;
1044
1.90k
  uint8_t                  qrv;
1045
1.90k
  struct mld_tparams       mtp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
1046
1047
1.90k
  MLD_LOCK_ASSERT_NOTHELD();
1048
1049
1.90k
  is_general_query = 0;
1050
1051
1.90k
  if (!mld_v2enable) {
1052
0
    MLD_PRINTF(("%s: ignore v2 query %s on ifp 0x%llx(%s)\n",
1053
0
        __func__, ip6_sprintf(&ip6->ip6_src),
1054
0
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1055
0
    goto done;
1056
0
  }
1057
1058
  /*
1059
   * RFC3810 Section 6.2: MLD queries must originate from
1060
   * a router's link-local address.
1061
   */
1062
1.90k
  if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
1063
353
    MLD_PRINTF(("%s: ignore v1 query src %s on ifp 0x%llx(%s)\n",
1064
353
        __func__, ip6_sprintf(&ip6->ip6_src),
1065
353
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1066
353
    goto done;
1067
353
  }
1068
1069
1.55k
  MLD_PRINTF(("%s: input v2 query on ifp 0x%llx(%s)\n", __func__,
1070
1.55k
      (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1071
1072
1.55k
  mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
1073
1074
1.55k
  maxdelay = ntohs(mld->mld_maxdelay);    /* in 1/10ths of a second */
1075
1.55k
  if (maxdelay > SHRT_MAX) {
1076
190
    maxdelay = (MLD_MRC_MANT((uint16_t)maxdelay) | 0x1000) <<
1077
190
        (MLD_MRC_EXP((uint16_t)maxdelay) + 3);
1078
190
  }
1079
1.55k
  timer = maxdelay / MLD_TIMER_SCALE;
1080
1.55k
  if (timer == 0) {
1081
1.33k
    timer = 1;
1082
1.33k
  }
1083
1084
1.55k
  qrv = MLD_QRV(mld->mld_misc);
1085
1.55k
  if (qrv < 2) {
1086
1.50k
    MLD_PRINTF(("%s: clamping qrv %d to %d\n", __func__,
1087
1.50k
        qrv, MLD_RV_INIT));
1088
1.50k
    qrv = MLD_RV_INIT;
1089
1.50k
  }
1090
1091
1.55k
  qqi = mld->mld_qqi;
1092
1.55k
  if (qqi >= 128) {
1093
93
    qqi = MLD_QQIC_MANT(mld->mld_qqi) <<
1094
93
        (MLD_QQIC_EXP(mld->mld_qqi) + 3);
1095
93
  }
1096
1097
1.55k
  nsrc = ntohs(mld->mld_numsrc);
1098
1.55k
  if (nsrc > MLD_MAX_GS_SOURCES) {
1099
195
    err = EMSGSIZE;
1100
195
    goto done;
1101
195
  }
1102
1.36k
  if (icmp6len < sizeof(struct mldv2_query) +
1103
1.36k
      (nsrc * sizeof(struct in6_addr))) {
1104
44
    err = EMSGSIZE;
1105
44
    goto done;
1106
44
  }
1107
1108
  /*
1109
   * Do further input validation upfront to avoid resetting timers
1110
   * should we need to discard this query.
1111
   */
1112
1.31k
  if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
1113
    /*
1114
     * A general query with a source list has undefined
1115
     * behaviour; discard it.
1116
     */
1117
930
    if (nsrc > 0) {
1118
0
      err = EINVAL;
1119
0
      goto done;
1120
0
    }
1121
930
    is_general_query = 1;
1122
930
  } 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
     */
1128
387
    (void)in6_setscope(&mld->mld_addr, ifp, NULL);
1129
387
  }
1130
1131
1.31k
  mli = MLD_IFINFO(ifp);
1132
1.31k
  VERIFY(mli != NULL);
1133
1134
1.31k
  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
1.31k
  if (mli->mli_version != MLD_VERSION_2) {
1141
57
    MLI_UNLOCK(mli);
1142
57
    goto done;
1143
57
  }
1144
1145
1.26k
  mtp.qpt = mld_set_version(mli, MLD_VERSION_2);
1146
1.26k
  mli->mli_rv = qrv;
1147
1.26k
  mli->mli_qi = qqi;
1148
1.26k
  mli->mli_qri = MAX(timer, MLD_QRI_MIN);
1149
1150
1.26k
  MLD_PRINTF(("%s: qrv %d qi %d qri %d\n", __func__, mli->mli_rv,
1151
1.26k
      mli->mli_qi, mli->mli_qri));
1152
1153
1.26k
  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
     */
1165
920
    MLD_PRINTF(("%s: process v2 general query on ifp 0x%llx(%s)\n",
1166
920
        __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1167
920
    if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) {
1168
898
      mtp.it = mli->mli_v2_timer = MLD_RANDOM_DELAY(timer);
1169
898
    }
1170
920
    MLI_UNLOCK(mli);
1171
920
  } else {
1172
340
    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
340
    in6_multihead_lock_shared();
1182
340
    IN6_LOOKUP_MULTI(&mld->mld_addr, ifp, inm);
1183
340
    in6_multihead_lock_done();
1184
340
    if (inm == NULL) {
1185
324
      goto done;
1186
324
    }
1187
1188
16
    IN6M_LOCK(inm);
1189
16
    if (nsrc > 0) {
1190
16
      if (!ratecheck(&inm->in6m_lastgsrtv,
1191
16
          &mld_gsrdelay)) {
1192
0
        MLD_PRINTF(("%s: GS query throttled.\n",
1193
0
            __func__));
1194
0
        IN6M_UNLOCK(inm);
1195
0
        IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1196
0
        goto done;
1197
0
      }
1198
16
    }
1199
16
    MLD_PRINTF(("%s: process v2 group query on ifp 0x%llx(%s)\n",
1200
16
        __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
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
16
    MLI_LOCK(mli);
1209
16
    mtp.it = mli->mli_v2_timer;
1210
16
    MLI_UNLOCK(mli);
1211
16
    if (mtp.it == 0 || mtp.it >= timer) {
1212
15
      (void) mld_v2_process_group_query(inm, timer, m, off);
1213
15
      mtp.cst = inm->in6m_timer;
1214
15
    }
1215
16
    IN6M_UNLOCK(inm);
1216
16
    IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1217
    /* XXX Clear embedded scope ID as userland won't expect it. */
1218
16
    in6_clearscope(&mld->mld_addr);
1219
16
  }
1220
1.90k
done:
1221
1.90k
  if (mtp.it > 0) {
1222
899
    MLD_PRINTF(("%s: v2 general query response scheduled in "
1223
899
        "T+%d seconds on ifp 0x%llx(%s)\n", __func__, mtp.it,
1224
899
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1225
899
  }
1226
1.90k
  mld_set_timeout(&mtp);
1227
1228
1.90k
  return err;
1229
1.26k
}
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
 */
1236
static int
1237
mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
1238
    const int off)
1239
15
{
1240
15
  struct mldv2_query      *mld;
1241
15
  int                      retval;
1242
15
  uint16_t                 nsrc;
1243
1244
15
  IN6M_LOCK_ASSERT_HELD(inm);
1245
1246
15
  retval = 0;
1247
15
  mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off);
1248
1249
15
  switch (inm->in6m_state) {
1250
0
  case MLD_NOT_MEMBER:
1251
15
  case MLD_SILENT_MEMBER:
1252
15
  case MLD_SLEEPING_MEMBER:
1253
15
  case MLD_LAZY_MEMBER:
1254
15
  case MLD_AWAKENING_MEMBER:
1255
15
  case MLD_IDLE_MEMBER:
1256
15
  case MLD_LEAVING_MEMBER:
1257
15
    return retval;
1258
0
  case MLD_REPORTING_MEMBER:
1259
0
  case MLD_G_QUERY_PENDING_MEMBER:
1260
0
  case MLD_SG_QUERY_PENDING_MEMBER:
1261
0
    break;
1262
15
  }
1263
1264
0
  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
0
  if (nsrc == 0) {
1273
0
    if (inm->in6m_state == MLD_G_QUERY_PENDING_MEMBER ||
1274
0
        inm->in6m_state == MLD_SG_QUERY_PENDING_MEMBER) {
1275
0
      in6m_clear_recorded(inm);
1276
0
      timer = min(inm->in6m_timer, timer);
1277
0
    }
1278
0
    inm->in6m_state = MLD_G_QUERY_PENDING_MEMBER;
1279
0
    inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1280
0
    return retval;
1281
0
  }
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
0
  if (inm->in6m_state == MLD_G_QUERY_PENDING_MEMBER) {
1288
0
    timer = min(inm->in6m_timer, timer);
1289
0
    inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1290
0
    return retval;
1291
0
  }
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
0
  if (inm->in6m_nsrc > 0) {
1305
0
    struct mbuf             *m;
1306
0
    uint8_t                 *sp;
1307
0
    int                      i, nrecorded;
1308
0
    int                      soff;
1309
1310
0
    m = m0;
1311
0
    soff = off + sizeof(struct mldv2_query);
1312
0
    nrecorded = 0;
1313
0
    for (i = 0; i < nsrc; i++) {
1314
0
      sp = mtod(m, uint8_t *) + soff;
1315
0
      retval = in6m_record_source(inm,
1316
0
          (const struct in6_addr *)(void *)sp);
1317
0
      if (retval < 0) {
1318
0
        break;
1319
0
      }
1320
0
      nrecorded += retval;
1321
0
      soff += sizeof(struct in6_addr);
1322
0
      if (soff >= m->m_len) {
1323
0
        soff = soff - m->m_len;
1324
0
        m = m->m_next;
1325
0
        if (m == NULL) {
1326
0
          break;
1327
0
        }
1328
0
      }
1329
0
    }
1330
0
    if (nrecorded > 0) {
1331
0
      MLD_PRINTF(("%s: schedule response to SG query\n",
1332
0
          __func__));
1333
0
      inm->in6m_state = MLD_SG_QUERY_PENDING_MEMBER;
1334
0
      inm->in6m_timer = MLD_RANDOM_DELAY(timer);
1335
0
    }
1336
0
  }
1337
1338
0
  return retval;
1339
0
}
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
 */
1348
static int
1349
mld_v1_input_report(struct ifnet *ifp, struct mbuf *m,
1350
    const struct ip6_hdr *ip6, /*const*/ struct mld_hdr *mld)
1351
4.28k
{
1352
4.28k
  struct in6_addr          src, dst;
1353
4.28k
  struct in6_ifaddr       *ia;
1354
4.28k
  struct in6_multi        *inm;
1355
1356
4.28k
  if (!mld_v1enable) {
1357
0
    MLD_PRINTF(("%s: ignore v1 report %s on ifp 0x%llx(%s)\n",
1358
0
        __func__, ip6_sprintf(&mld->mld_addr),
1359
0
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1360
0
    return 0;
1361
0
  }
1362
1363
4.28k
  if ((ifp->if_flags & IFF_LOOPBACK) ||
1364
4.28k
      (m->m_pkthdr.pkt_flags & PKTF_LOOP)) {
1365
4.28k
    return 0;
1366
4.28k
  }
1367
1368
  /*
1369
   * MLDv1 reports must originate from a host's link-local address,
1370
   * or the unspecified address (when booting).
1371
   */
1372
0
  src = ip6->ip6_src;
1373
0
  in6_clearscope(&src);
1374
0
  if (!IN6_IS_SCOPE_LINKLOCAL(&src) && !IN6_IS_ADDR_UNSPECIFIED(&src)) {
1375
0
    MLD_PRINTF(("%s: ignore v1 query src %s on ifp 0x%llx(%s)\n",
1376
0
        __func__, ip6_sprintf(&ip6->ip6_src),
1377
0
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1378
0
    return EINVAL;
1379
0
  }
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
0
  dst = ip6->ip6_dst;
1386
0
  in6_clearscope(&dst);
1387
0
  if (!IN6_IS_ADDR_MULTICAST(&mld->mld_addr) ||
1388
0
      !IN6_ARE_ADDR_EQUAL(&mld->mld_addr, &dst)) {
1389
0
    MLD_PRINTF(("%s: ignore v1 query dst %s on ifp 0x%llx(%s)\n",
1390
0
        __func__, ip6_sprintf(&ip6->ip6_dst),
1391
0
        (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1392
0
    return EINVAL;
1393
0
  }
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
   */
1405
0
  ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
1406
0
  if (ia != NULL) {
1407
0
    IFA_LOCK(&ia->ia_ifa);
1408
0
    if ((IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, IA6_IN6(ia)))) {
1409
0
      IFA_UNLOCK(&ia->ia_ifa);
1410
0
      IFA_REMREF(&ia->ia_ifa);
1411
0
      return 0;
1412
0
    }
1413
0
    IFA_UNLOCK(&ia->ia_ifa);
1414
0
    IFA_REMREF(&ia->ia_ifa);
1415
0
  } else if (IN6_IS_ADDR_UNSPECIFIED(&src)) {
1416
0
    return 0;
1417
0
  }
1418
1419
0
  MLD_PRINTF(("%s: process v1 report %s on ifp 0x%llx(%s)\n",
1420
0
      __func__, ip6_sprintf(&mld->mld_addr),
1421
0
      (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
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
   */
1427
0
  if (!IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
1428
0
    (void)in6_setscope(&mld->mld_addr, ifp, NULL);
1429
0
  }
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
0
  in6_multihead_lock_shared();
1438
0
  IN6_LOOKUP_MULTI(&mld->mld_addr, ifp, inm);
1439
0
  in6_multihead_lock_done();
1440
1441
0
  if (inm != NULL) {
1442
0
    struct mld_ifinfo *mli;
1443
1444
0
    IN6M_LOCK(inm);
1445
0
    mli = inm->in6m_mli;
1446
0
    VERIFY(mli != NULL);
1447
1448
0
    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
0
    if (mli->mli_version == MLD_VERSION_2) {
1454
0
      MLI_UNLOCK(mli);
1455
0
      IN6M_UNLOCK(inm);
1456
0
      IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1457
0
      goto out;
1458
0
    }
1459
0
    MLI_UNLOCK(mli);
1460
1461
0
    inm->in6m_timer = 0;
1462
1463
0
    switch (inm->in6m_state) {
1464
0
    case MLD_NOT_MEMBER:
1465
0
    case MLD_SILENT_MEMBER:
1466
0
    case MLD_SLEEPING_MEMBER:
1467
0
      break;
1468
0
    case MLD_REPORTING_MEMBER:
1469
0
    case MLD_IDLE_MEMBER:
1470
0
    case MLD_AWAKENING_MEMBER:
1471
0
      MLD_PRINTF(("%s: report suppressed for %s on "
1472
0
          "ifp 0x%llx(%s)\n", __func__,
1473
0
          ip6_sprintf(&mld->mld_addr),
1474
0
          (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1475
0
      OS_FALLTHROUGH;
1476
0
    case MLD_LAZY_MEMBER:
1477
0
      inm->in6m_state = MLD_LAZY_MEMBER;
1478
0
      break;
1479
0
    case MLD_G_QUERY_PENDING_MEMBER:
1480
0
    case MLD_SG_QUERY_PENDING_MEMBER:
1481
0
    case MLD_LEAVING_MEMBER:
1482
0
      break;
1483
0
    }
1484
0
    IN6M_UNLOCK(inm);
1485
0
    IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
1486
0
  }
1487
1488
0
out:
1489
  /* XXX Clear embedded scope ID as userland won't expect it. */
1490
0
  in6_clearscope(&mld->mld_addr);
1491
1492
0
  return 0;
1493
0
}
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
 */
1506
int
1507
mld_input(struct mbuf *m, int off, int icmp6len)
1508
7.99k
{
1509
7.99k
  struct ifnet    *ifp = NULL;
1510
7.99k
  struct ip6_hdr  *ip6 = NULL;
1511
7.99k
  struct mld_hdr  *mld = NULL;
1512
7.99k
  int              mldlen = 0;
1513
1514
7.99k
  MLD_PRINTF(("%s: called w/mbuf (0x%llx,%d)\n", __func__,
1515
7.99k
      (uint64_t)VM_KERNEL_ADDRPERM(m), off));
1516
1517
7.99k
  ifp = m->m_pkthdr.rcvif;
1518
1519
  /* Pullup to appropriate size. */
1520
7.99k
  mld = (struct mld_hdr *)(mtod(m, uint8_t *) + off);
1521
7.99k
  if (mld->mld_type == MLD_LISTENER_QUERY &&
1522
3.71k
      icmp6len >= sizeof(struct mldv2_query)) {
1523
1.90k
    mldlen = sizeof(struct mldv2_query);
1524
6.08k
  } else {
1525
6.08k
    mldlen = sizeof(struct mld_hdr);
1526
6.08k
  }
1527
  // check if mldv2_query/mld_hdr fits in the first mbuf
1528
7.99k
  IP6_EXTHDR_CHECK(m, off, mldlen, return IPPROTO_DONE);
1529
7.99k
  IP6_EXTHDR_GET(mld, struct mld_hdr *, m, off, mldlen);
1530
7.99k
  if (mld == NULL) {
1531
0
    icmp6stat.icp6s_badlen++;
1532
0
    return IPPROTO_DONE;
1533
0
  }
1534
7.99k
  ip6 = mtod(m, struct ip6_hdr *);
1535
1536
  /*
1537
   * Userland needs to see all of this traffic for implementing
1538
   * the endpoint discovery portion of multicast routing.
1539
   */
1540
7.99k
  switch (mld->mld_type) {
1541
3.71k
  case MLD_LISTENER_QUERY:
1542
3.71k
    icmp6_ifstat_inc(ifp, ifs6_in_mldquery);
1543
3.71k
    if (icmp6len == sizeof(struct mld_hdr)) {
1544
1.76k
      if (mld_v1_input_query(ifp, ip6, mld) != 0) {
1545
322
        return 0;
1546
322
      }
1547
1.95k
    } else if (icmp6len >= sizeof(struct mldv2_query)) {
1548
1.90k
      if (mld_v2_input_query(ifp, ip6, m, off,
1549
1.90k
          icmp6len) != 0) {
1550
239
        return 0;
1551
239
      }
1552
1.90k
    }
1553
3.15k
    break;
1554
4.28k
  case MLD_LISTENER_REPORT:
1555
4.28k
    icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
1556
4.28k
    if (mld_v1_input_report(ifp, m, ip6, mld) != 0) {
1557
0
      return 0;
1558
0
    }
1559
4.28k
    break;
1560
4.28k
  case MLDV2_LISTENER_REPORT:
1561
0
    icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
1562
0
    break;
1563
0
  case MLD_LISTENER_DONE:
1564
0
    icmp6_ifstat_inc(ifp, ifs6_in_mlddone);
1565
0
    break;
1566
0
  default:
1567
0
    break;
1568
7.99k
  }
1569
1570
7.43k
  return 0;
1571
7.99k
}
1572
1573
/*
1574
 * Schedule MLD timer based on various parameters; caller must ensure that
1575
 * lock ordering is maintained as this routine acquires MLD global lock.
1576
 */
1577
void
1578
mld_set_timeout(struct mld_tparams *mtp)
1579
3.67k
{
1580
3.67k
  MLD_LOCK_ASSERT_NOTHELD();
1581
3.67k
  VERIFY(mtp != NULL);
1582
1583
3.67k
  if (mtp->qpt != 0 || mtp->it != 0 || mtp->cst != 0 || mtp->sct != 0) {
1584
2.22k
    MLD_LOCK();
1585
2.22k
    if (mtp->qpt != 0) {
1586
1.32k
      querier_present_timers_running6 = 1;
1587
1.32k
    }
1588
2.22k
    if (mtp->it != 0) {
1589
899
      interface_timers_running6 = 1;
1590
899
    }
1591
2.22k
    if (mtp->cst != 0) {
1592
0
      current_state_timers_running6 = 1;
1593
0
    }
1594
2.22k
    if (mtp->sct != 0) {
1595
0
      state_change_timers_running6 = 1;
1596
0
    }
1597
    // mld_sched_timeout();
1598
2.22k
    MLD_UNLOCK();
1599
2.22k
  }
1600
3.67k
}
1601
1602
/*
1603
 * MLD6 timer handler (per 1 second).
1604
 */
1605
void
1606
mld_timeout(void *arg)
1607
322k
{
1608
322k
#pragma unused(arg)
1609
322k
  struct ifqueue           scq;   /* State-change packets */
1610
322k
  struct ifqueue           qrq;   /* Query response packets */
1611
322k
  struct ifnet            *ifp;
1612
322k
  struct mld_ifinfo       *mli;
1613
322k
  struct in6_multi        *inm;
1614
322k
  int                      uri_sec = 0;
1615
322k
  unsigned int genid = mld_mli_list_genid;
1616
1617
322k
  SLIST_HEAD(, in6_multi) in6m_dthead;
1618
1619
322k
  SLIST_INIT(&in6m_dthead);
1620
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
322k
  net_update_uptime();
1627
1628
322k
  MLD_LOCK();
1629
1630
322k
  MLD_PRINTF(("%s: qpt %d, it %d, cst %d, sct %d\n", __func__,
1631
322k
      querier_present_timers_running6, interface_timers_running6,
1632
322k
      current_state_timers_running6, state_change_timers_running6));
1633
1634
  /*
1635
   * MLDv1 querier present timer processing.
1636
   */
1637
322k
  if (querier_present_timers_running6) {
1638
7.76k
    querier_present_timers_running6 = 0;
1639
79.4k
    LIST_FOREACH(mli, &mli_head, mli_link) {
1640
79.4k
      MLI_LOCK(mli);
1641
79.4k
      mld_v1_process_querier_timers(mli);
1642
79.4k
      if (mli->mli_v1_timer > 0) {
1643
7.63k
        querier_present_timers_running6 = 1;
1644
7.63k
      }
1645
79.4k
      MLI_UNLOCK(mli);
1646
79.4k
    }
1647
7.76k
  }
1648
1649
  /*
1650
   * MLDv2 General Query response timer processing.
1651
   */
1652
322k
  if (interface_timers_running6) {
1653
484
    MLD_PRINTF(("%s: interface timers running\n", __func__));
1654
484
    interface_timers_running6 = 0;
1655
484
    mli = LIST_FIRST(&mli_head);
1656
1657
5.80k
    while (mli != NULL) {
1658
5.31k
      if (mli->mli_flags & MLIF_PROCESSED) {
1659
0
        mli = LIST_NEXT(mli, mli_link);
1660
0
        continue;
1661
0
      }
1662
1663
5.31k
      MLI_LOCK(mli);
1664
5.31k
      if (mli->mli_version != MLD_VERSION_2) {
1665
0
        MLI_UNLOCK(mli);
1666
0
        mli = LIST_NEXT(mli, mli_link);
1667
0
        continue;
1668
0
      }
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
5.31k
      MLI_ADDREF_LOCKED(mli);
1678
1679
5.31k
      if (mli->mli_v2_timer == 0) {
1680
        /* Do nothing. */
1681
4.83k
      } else if (--mli->mli_v2_timer == 0) {
1682
443
        if (mld_v2_dispatch_general_query(mli) > 0) {
1683
0
          interface_timers_running6 = 1;
1684
0
        }
1685
443
      } else {
1686
41
        interface_timers_running6 = 1;
1687
41
      }
1688
5.31k
      mli->mli_flags |= MLIF_PROCESSED;
1689
5.31k
      MLI_UNLOCK(mli);
1690
5.31k
      MLI_REMREF(mli);
1691
1692
5.31k
      if (genid != mld_mli_list_genid) {
1693
0
        MLD_PRINTF(("%s: MLD information list changed "
1694
0
            "in the middle of iteration! Restart iteration.\n",
1695
0
            __func__));
1696
0
        mli = LIST_FIRST(&mli_head);
1697
0
        genid = mld_mli_list_genid;
1698
5.31k
      } else {
1699
5.31k
        mli = LIST_NEXT(mli, mli_link);
1700
5.31k
      }
1701
5.31k
    }
1702
1703
484
    LIST_FOREACH(mli, &mli_head, mli_link)
1704
5.31k
    mli->mli_flags &= ~MLIF_PROCESSED;
1705
484
  }
1706
1707
1708
1709
322k
  if (!current_state_timers_running6 &&
1710
322k
      !state_change_timers_running6) {
1711
322k
    goto out_locked;
1712
322k
  }
1713
1714
0
  current_state_timers_running6 = 0;
1715
0
  state_change_timers_running6 = 0;
1716
1717
0
  MLD_PRINTF(("%s: state change timers running\n", __func__));
1718
1719
0
  memset(&qrq, 0, sizeof(struct ifqueue));
1720
0
  qrq.ifq_maxlen = MLD_MAX_G_GS_PACKETS;
1721
1722
0
  memset(&scq, 0, sizeof(struct ifqueue));
1723
0
  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
   */
1729
0
  mli = LIST_FIRST(&mli_head);
1730
1731
0
  while (mli != NULL) {
1732
0
    struct in6_multistep step;
1733
1734
0
    if (mli->mli_flags & MLIF_PROCESSED) {
1735
0
      mli = LIST_NEXT(mli, mli_link);
1736
0
      continue;
1737
0
    }
1738
1739
0
    MLI_LOCK(mli);
1740
0
    ifp = mli->mli_ifp;
1741
0
    uri_sec = MLD_RANDOM_DELAY(mli->mli_uri);
1742
0
    MLI_UNLOCK(mli);
1743
1744
0
    in6_multihead_lock_shared();
1745
0
    IN6_FIRST_MULTI(step, inm);
1746
0
    while (inm != NULL) {
1747
0
      IN6M_LOCK(inm);
1748
0
      if (inm->in6m_ifp != ifp) {
1749
0
        goto next;
1750
0
      }
1751
1752
0
      MLI_LOCK(mli);
1753
0
      switch (mli->mli_version) {
1754
0
      case MLD_VERSION_1:
1755
0
        mld_v1_process_group_timer(inm,
1756
0
            mli->mli_version);
1757
0
        break;
1758
0
      case MLD_VERSION_2:
1759
0
        mld_v2_process_group_timers(mli, &qrq,
1760
0
            &scq, inm, uri_sec);
1761
0
        break;
1762
0
      }
1763
0
      MLI_UNLOCK(mli);
1764
0
next:
1765
0
      IN6M_UNLOCK(inm);
1766
0
      IN6_NEXT_MULTI(step, inm);
1767
0
    }
1768
0
    in6_multihead_lock_done();
1769
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
     */
1778
0
    MLI_LOCK(mli);
1779
0
    MLI_ADDREF_LOCKED(mli);
1780
0
    if (mli->mli_version == MLD_VERSION_1) {
1781
0
      mld_dispatch_queue_locked(mli, &mli->mli_v1q, 0);
1782
0
    } else if (mli->mli_version == MLD_VERSION_2) {
1783
0
      MLI_UNLOCK(mli);
1784
0
      mld_dispatch_queue_locked(NULL, &qrq, 0);
1785
0
      mld_dispatch_queue_locked(NULL, &scq, 0);
1786
0
      VERIFY(qrq.ifq_len == 0);
1787
0
      VERIFY(scq.ifq_len == 0);
1788
0
      MLI_LOCK(mli);
1789
0
    }
1790
    /*
1791
     * In case there are still any pending membership reports
1792
     * which didn't get drained at version change time.
1793
     */
1794
0
    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
     */
1801
0
    mld_flush_relq(mli, (struct mld_in6m_relhead *)&in6m_dthead);
1802
0
    VERIFY(SLIST_EMPTY(&mli->mli_relinmhead));
1803
0
    mli->mli_flags |= MLIF_PROCESSED;
1804
0
    MLI_UNLOCK(mli);
1805
0
    MLI_REMREF(mli);
1806
1807
0
    IF_DRAIN(&qrq);
1808
0
    IF_DRAIN(&scq);
1809
1810
0
    if (genid != mld_mli_list_genid) {
1811
0
      MLD_PRINTF(("%s: MLD information list changed "
1812
0
          "in the middle of iteration! Restart iteration.\n",
1813
0
          __func__));
1814
0
      mli = LIST_FIRST(&mli_head);
1815
0
      genid = mld_mli_list_genid;
1816
0
    } else {
1817
0
      mli = LIST_NEXT(mli, mli_link);
1818
0
    }
1819
0
  }
1820
1821
0
  LIST_FOREACH(mli, &mli_head, mli_link)
1822
0
  mli->mli_flags &= ~MLIF_PROCESSED;
1823
1824
322k
out_locked:
1825
  /* re-arm the timer if there's work to do */
1826
322k
  mld_timeout_run = 0;
1827
  // mld_sched_timeout();
1828
322k
  MLD_UNLOCK();
1829
1830
  /* Now that we're dropped all locks, release detached records */
1831
322k
  MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
1832
322k
}
1833
1834
static void
1835
mld_sched_timeout(void)
1836
0
{
1837
0
  MLD_LOCK_ASSERT_HELD();
1838
0
1839
0
  if (!mld_timeout_run &&
1840
0
      (querier_present_timers_running6 || current_state_timers_running6 ||
1841
0
      interface_timers_running6 || state_change_timers_running6)) {
1842
0
    mld_timeout_run = 1;
1843
0
    timeout(mld_timeout, NULL, hz);
1844
0
  }
1845
0
}
1846
1847
/*
1848
 * Free the in6_multi reference(s) for this MLD lifecycle.
1849
 *
1850
 * Caller must be holding mli_lock.
1851
 */
1852
static void
1853
mld_flush_relq(struct mld_ifinfo *mli, struct mld_in6m_relhead *in6m_dthead)
1854
0
{
1855
0
  struct in6_multi *inm;
1856
1857
0
again:
1858
0
  MLI_LOCK_ASSERT_HELD(mli);
1859
0
  inm = SLIST_FIRST(&mli->mli_relinmhead);
1860
0
  if (inm != NULL) {
1861
0
    int lastref;
1862
1863
0
    SLIST_REMOVE_HEAD(&mli->mli_relinmhead, in6m_nrele);
1864
0
    MLI_UNLOCK(mli);
1865
1866
0
    in6_multihead_lock_exclusive();
1867
0
    IN6M_LOCK(inm);
1868
0
    VERIFY(inm->in6m_nrelecnt != 0);
1869
0
    inm->in6m_nrelecnt--;
1870
0
    lastref = in6_multi_detach(inm);
1871
0
    VERIFY(!lastref || (!(inm->in6m_debug & IFD_ATTACHED) &&
1872
0
        inm->in6m_reqcnt == 0));
1873
0
    IN6M_UNLOCK(inm);
1874
0
    in6_multihead_lock_done();
1875
    /* from mli_relinmhead */
1876
0
    IN6M_REMREF(inm);
1877
    /* from in6_multihead_list */
1878
0
    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
0
      MLD_ADD_DETACHED_IN6M(in6m_dthead, inm);
1888
0
    }
1889
0
    MLI_LOCK(mli);
1890
0
    goto again;
1891
0
  }
1892
0
}
1893
1894
/*
1895
 * Update host report group timer.
1896
 * Will update the global pending timer flags.
1897
 */
1898
static void
1899
mld_v1_process_group_timer(struct in6_multi *inm, const int mld_version)
1900
0
{
1901
0
#pragma unused(mld_version)
1902
0
  int report_timer_expired;
1903
1904
0
  MLD_LOCK_ASSERT_HELD();
1905
0
  IN6M_LOCK_ASSERT_HELD(inm);
1906
0
  MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
1907
1908
0
  if (inm->in6m_timer == 0) {
1909
0
    report_timer_expired = 0;
1910
0
  } else if (--inm->in6m_timer == 0) {
1911
0
    report_timer_expired = 1;
1912
0
  } else {
1913
0
    current_state_timers_running6 = 1;
1914
    /* caller will schedule timer */
1915
0
    return;
1916
0
  }
1917
1918
0
  switch (inm->in6m_state) {
1919
0
  case MLD_NOT_MEMBER:
1920
0
  case MLD_SILENT_MEMBER:
1921
0
  case MLD_IDLE_MEMBER:
1922
0
  case MLD_LAZY_MEMBER:
1923
0
  case MLD_SLEEPING_MEMBER:
1924
0
  case MLD_AWAKENING_MEMBER:
1925
0
    break;
1926
0
  case MLD_REPORTING_MEMBER:
1927
0
    if (report_timer_expired) {
1928
0
      inm->in6m_state = MLD_IDLE_MEMBER;
1929
0
      (void) mld_v1_transmit_report(inm,
1930
0
          MLD_LISTENER_REPORT);
1931
0
      IN6M_LOCK_ASSERT_HELD(inm);
1932
0
      MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
1933
0
    }
1934
0
    break;
1935
0
  case MLD_G_QUERY_PENDING_MEMBER:
1936
0
  case MLD_SG_QUERY_PENDING_MEMBER:
1937
0
  case MLD_LEAVING_MEMBER:
1938
0
    break;
1939
0
  }
1940
0
}
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
 */
1947
static void
1948
mld_v2_process_group_timers(struct mld_ifinfo *mli,
1949
    struct ifqueue *qrq, struct ifqueue *scq,
1950
    struct in6_multi *inm, const int uri_sec)
1951
0
{
1952
0
  int query_response_timer_expired;
1953
0
  int state_change_retransmit_timer_expired;
1954
1955
0
  MLD_LOCK_ASSERT_HELD();
1956
0
  IN6M_LOCK_ASSERT_HELD(inm);
1957
0
  MLI_LOCK_ASSERT_HELD(mli);
1958
0
  VERIFY(mli == inm->in6m_mli);
1959
1960
0
  query_response_timer_expired = 0;
1961
0
  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
1967
   * to deal with it here than to complicate the timeout path.
1968
   */
1969
0
  if (inm->in6m_timer == 0) {
1970
0
    query_response_timer_expired = 0;
1971
0
  } else if (--inm->in6m_timer == 0) {
1972
0
    query_response_timer_expired = 1;
1973
0
  } else {
1974
0
    current_state_timers_running6 = 1;
1975
    /* caller will schedule timer */
1976
0
  }
1977
1978
0
  if (inm->in6m_sctimer == 0) {
1979
0
    state_change_retransmit_timer_expired = 0;
1980
0
  } else if (--inm->in6m_sctimer == 0) {
1981
0
    state_change_retransmit_timer_expired = 1;
1982
0
  } else {
1983
0
    state_change_timers_running6 = 1;
1984
    /* caller will schedule timer */
1985
0
  }
1986
1987
  /* We are in timer callback, so be quick about it. */
1988
0
  if (!state_change_retransmit_timer_expired &&
1989
0
      !query_response_timer_expired) {
1990
0
    return;
1991
0
  }
1992
1993
0
  switch (inm->in6m_state) {
1994
0
  case MLD_NOT_MEMBER:
1995
0
  case MLD_SILENT_MEMBER:
1996
0
  case MLD_SLEEPING_MEMBER:
1997
0
  case MLD_LAZY_MEMBER:
1998
0
  case MLD_AWAKENING_MEMBER:
1999
0
  case MLD_IDLE_MEMBER:
2000
0
    break;
2001
0
  case MLD_G_QUERY_PENDING_MEMBER:
2002
0
  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
0
    if (query_response_timer_expired) {
2010
0
      int retval;
2011
2012
0
      retval = mld_v2_enqueue_group_record(qrq, inm, 0, 1,
2013
0
          (inm->in6m_state == MLD_SG_QUERY_PENDING_MEMBER),
2014
0
          0);
2015
0
      MLD_PRINTF(("%s: enqueue record = %d\n",
2016
0
          __func__, retval));
2017
0
      inm->in6m_state = MLD_REPORTING_MEMBER;
2018
0
      in6m_clear_recorded(inm);
2019
0
    }
2020
0
    OS_FALLTHROUGH;
2021
0
  case MLD_REPORTING_MEMBER:
2022
0
  case MLD_LEAVING_MEMBER:
2023
0
    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
0
      if (--inm->in6m_scrv > 0) {
2031
0
        inm->in6m_sctimer = (uint16_t)uri_sec;
2032
0
        state_change_timers_running6 = 1;
2033
        /* caller will schedule timer */
2034
0
      }
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
0
      (void) mld_v2_merge_state_changes(inm, scq);
2043
2044
0
      in6m_commit(inm);
2045
0
      MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2046
0
          ip6_sprintf(&inm->in6m_addr),
2047
0
          if_name(inm->in6m_ifp)));
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
0
      if (inm->in6m_state == MLD_LEAVING_MEMBER &&
2057
0
          inm->in6m_scrv == 0) {
2058
0
        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
0
        VERIFY(inm->in6m_nrelecnt != 0);
2069
0
        SLIST_INSERT_HEAD(&mli->mli_relinmhead,
2070
0
            inm, in6m_nrele);
2071
0
      }
2072
0
    }
2073
0
    break;
2074
0
  }
2075
0
}
2076
2077
/*
2078
 * Switch to a different version on the given interface,
2079
 * as per Section 9.12.
2080
 */
2081
static uint32_t
2082
mld_set_version(struct mld_ifinfo *mli, const int mld_version)
2083
2.58k
{
2084
2.58k
  int old_version_timer;
2085
2086
2.58k
  MLI_LOCK_ASSERT_HELD(mli);
2087
2088
2.58k
  MLD_PRINTF(("%s: switching to v%d on ifp 0x%llx(%s)\n", __func__,
2089
2.58k
      mld_version, (uint64_t)VM_KERNEL_ADDRPERM(mli->mli_ifp),
2090
2.58k
      if_name(mli->mli_ifp)));
2091
2092
2.58k
  if (mld_version == MLD_VERSION_1) {
2093
    /*
2094
     * Compute the "Older Version Querier Present" timer as per
2095
     * Section 9.12, in seconds.
2096
     */
2097
1.32k
    old_version_timer = (mli->mli_rv * mli->mli_qi) + mli->mli_qri;
2098
1.32k
    mli->mli_v1_timer = old_version_timer;
2099
1.32k
  }
2100
2101
2.58k
  if (mli->mli_v1_timer > 0 && mli->mli_version != MLD_VERSION_1) {
2102
122
    mli->mli_version = MLD_VERSION_1;
2103
122
    mld_v2_cancel_link_timers(mli);
2104
122
  }
2105
2106
2.58k
  MLI_LOCK_ASSERT_HELD(mli);
2107
2108
2.58k
  return mli->mli_v1_timer;
2109
2.58k
}
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.
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.
2119
 */
2120
static void
2121
mld_v2_cancel_link_timers(struct mld_ifinfo *mli)
2122
122
{
2123
122
  struct ifnet            *ifp;
2124
122
  struct in6_multi        *inm;
2125
122
  struct in6_multistep    step;
2126
2127
122
  MLI_LOCK_ASSERT_HELD(mli);
2128
2129
122
  MLD_PRINTF(("%s: cancel v2 timers on ifp 0x%llx(%s)\n", __func__,
2130
122
      (uint64_t)VM_KERNEL_ADDRPERM(mli->mli_ifp), if_name(mli->mli_ifp)));
2131
2132
  /*
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.
2136
   */
2137
122
  mli->mli_v2_timer = 0;
2138
2139
  /*
2140
   * Now clear the current-state and state-change report timers
2141
   * for all memberships scoped to this link.
2142
   */
2143
122
  ifp = mli->mli_ifp;
2144
122
  MLI_UNLOCK(mli);
2145
2146
122
  in6_multihead_lock_shared();
2147
122
  IN6_FIRST_MULTI(step, inm);
2148
610
  while (inm != NULL) {
2149
488
    IN6M_LOCK(inm);
2150
488
    if (inm->in6m_ifp != ifp) {
2151
0
      goto next;
2152
0
    }
2153
2154
488
    switch (inm->in6m_state) {
2155
0
    case MLD_NOT_MEMBER:
2156
488
    case MLD_SILENT_MEMBER:
2157
488
    case MLD_IDLE_MEMBER:
2158
488
    case MLD_LAZY_MEMBER:
2159
488
    case MLD_SLEEPING_MEMBER:
2160
488
    case MLD_AWAKENING_MEMBER:
2161
      /*
2162
       * These states are either not relevant in v2 mode,
2163
       * or are unreported. Do nothing.
2164
       */
2165
488
      break;
2166
0
    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
0
      VERIFY(inm->in6m_nrelecnt != 0);
2177
0
      MLI_LOCK(mli);
2178
0
      SLIST_INSERT_HEAD(&mli->mli_relinmhead, inm,
2179
0
          in6m_nrele);
2180
0
      MLI_UNLOCK(mli);
2181
0
      OS_FALLTHROUGH;
2182
0
    case MLD_G_QUERY_PENDING_MEMBER:
2183
0
    case MLD_SG_QUERY_PENDING_MEMBER:
2184
0
      in6m_clear_recorded(inm);
2185
0
      OS_FALLTHROUGH;
2186
0
    case MLD_REPORTING_MEMBER:
2187
0
      inm->in6m_state = MLD_REPORTING_MEMBER;
2188
0
      break;
2189
488
    }
2190
    /*
2191
     * Always clear state-change and group report timers.
2192
     * Free any pending MLDv2 state-change records.
2193
     */
2194
488
    inm->in6m_sctimer = 0;
2195
488
    inm->in6m_timer = 0;
2196
488
    IF_DRAIN(&inm->in6m_scq);
2197
488
next:
2198
488
    IN6M_UNLOCK(inm);
2199
488
    IN6_NEXT_MULTI(step, inm);
2200
488
  }
2201
122
  in6_multihead_lock_done();
2202
2203
122
  MLI_LOCK(mli);
2204
122
}
2205
2206
/*
2207
 * Update the Older Version Querier Present timers for a link.
2208
 * See Section 9.12 of RFC 3810.
2209
 */
2210
static void
2211
mld_v1_process_querier_timers(struct mld_ifinfo *mli)
2212
79.4k
{
2213
79.4k
  MLI_LOCK_ASSERT_HELD(mli);
2214
2215
79.4k
  if (mld_v2enable && mli->mli_version != MLD_VERSION_2 &&
2216
7.76k
      --mli->mli_v1_timer == 0) {
2217
    /*
2218
     * MLDv1 Querier Present timer expired; revert to MLDv2.
2219
     */
2220
122
    MLD_PRINTF(("%s: transition from v%d -> v%d on 0x%llx(%s)\n",
2221
122
        __func__, mli->mli_version, MLD_VERSION_2,
2222
122
        (uint64_t)VM_KERNEL_ADDRPERM(mli->mli_ifp),
2223
122
        if_name(mli->mli_ifp)));
2224
122
    mli->mli_version = MLD_VERSION_2;
2225
122
  }
2226
79.4k
}
2227
2228
/*
2229
 * Transmit an MLDv1 report immediately.
2230
 */
2231
static int
2232
mld_v1_transmit_report(struct in6_multi *in6m, const uint8_t type)
2233
0
{
2234
0
  struct ifnet            *ifp;
2235
0
  struct in6_ifaddr       *ia;
2236
0
  struct ip6_hdr          *ip6;
2237
0
  struct mbuf             *mh, *md;
2238
0
  struct mld_hdr          *mld;
2239
0
  int                     error = 0;
2240
2241
0
  IN6M_LOCK_ASSERT_HELD(in6m);
2242
0
  MLI_LOCK_ASSERT_HELD(in6m->in6m_mli);
2243
2244
0
  ifp = in6m->in6m_ifp;
2245
  /* ia may be NULL if link-local address is tentative. */
2246
0
  ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
2247
2248
0
  MGETHDR(mh, M_DONTWAIT, MT_HEADER);
2249
0
  if (mh == NULL) {
2250
0
    if (ia != NULL) {
2251
0
      IFA_REMREF(&ia->ia_ifa);
2252
0
    }
2253
0
    return ENOMEM;
2254
0
  }
2255
0
  MGET(md, M_DONTWAIT, MT_DATA);
2256
0
  if (md == NULL) {
2257
0
    m_free(mh);
2258
0
    if (ia != NULL) {
2259
0
      IFA_REMREF(&ia->ia_ifa);
2260
0
    }
2261
0
    return ENOMEM;
2262
0
  }
2263
0
  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
0
  MH_ALIGN(mh, sizeof(struct ip6_hdr));
2271
0
  mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
2272
0
  mh->m_len = sizeof(struct ip6_hdr);
2273
2274
0
  ip6 = mtod(mh, struct ip6_hdr *);
2275
0
  ip6->ip6_flow = 0;
2276
0
  ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
2277
0
  ip6->ip6_vfc |= IPV6_VERSION;
2278
0
  ip6->ip6_nxt = IPPROTO_ICMPV6;
2279
0
  if (ia != NULL) {
2280
0
    IFA_LOCK(&ia->ia_ifa);
2281
0
  }
2282
0
  ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
2283
0
  if (ia != NULL) {
2284
0
    IFA_UNLOCK(&ia->ia_ifa);
2285
0
    IFA_REMREF(&ia->ia_ifa);
2286
0
    ia = NULL;
2287
0
  }
2288
0
  ip6->ip6_dst = in6m->in6m_addr;
2289
2290
0
  md->m_len = sizeof(struct mld_hdr);
2291
0
  mld = mtod(md, struct mld_hdr *);
2292
0
  mld->mld_type = type;
2293
0
  mld->mld_code = 0;
2294
0
  mld->mld_cksum = 0;
2295
0
  mld->mld_maxdelay = 0;
2296
0
  mld->mld_reserved = 0;
2297
0
  mld->mld_addr = in6m->in6m_addr;
2298
0
  in6_clearscope(&mld->mld_addr);
2299
0
  mld->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6,
2300
0
      sizeof(struct ip6_hdr), sizeof(struct mld_hdr));
2301
2302
0
  mld_save_context(mh, ifp);
2303
0
  mh->m_flags |= M_MLDV1;
2304
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.
2311
   * Instead we defer the work to the mld_timeout() thread, thus
2312
   * avoiding unlocking in_multihead_lock here.
2313
   */
2314
0
  if (IF_QFULL(&in6m->in6m_mli->mli_v1q)) {
2315
0
    MLD_PRINTF(("%s: v1 outbound queue full\n", __func__));
2316
0
    error = ENOMEM;
2317
0
    m_freem(mh);
2318
0
  } else {
2319
0
    IF_ENQUEUE(&in6m->in6m_mli->mli_v1q, mh);
2320
0
    VERIFY(error == 0);
2321
0
  }
2322
2323
0
  return error;
2324
0
}
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
2342
 * in units of seconds if MLDv1 is active on the link; otherwise
2343
 * the initial MLDv2 state change report will be delayed by whichever
2344
 * is sooner, a pending state-change timer or delay itself.
2345
 */
2346
int
2347
mld_change_state(struct in6_multi *inm, struct mld_tparams *mtp,
2348
    const int delay)
2349
4
{
2350
4
  struct mld_ifinfo *mli;
2351
4
  struct ifnet *ifp;
2352
4
  int error = 0;
2353
2354
4
  VERIFY(mtp != NULL);
2355
0
  bzero(mtp, sizeof(*mtp));
2356
2357
4
  IN6M_LOCK_ASSERT_HELD(inm);
2358
4
  VERIFY(inm->in6m_mli != NULL);
2359
4
  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
4
  VERIFY(inm->in6m_ifma != NULL);
2366
0
  ifp = inm->in6m_ifma->ifma_ifp;
2367
  /*
2368
   * Sanity check that netinet6's notion of ifp is the same as net's.
2369
   */
2370
4
  VERIFY(inm->in6m_ifp == ifp);
2371
2372
4
  mli = MLD_IFINFO(ifp);
2373
4
  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
4
  if (inm->in6m_st[1].iss_fmode != inm->in6m_st[0].iss_fmode) {
2381
4
    MLD_PRINTF(("%s: inm transition %d -> %d\n", __func__,
2382
4
        inm->in6m_st[0].iss_fmode, inm->in6m_st[1].iss_fmode));
2383
4
    if (inm->in6m_st[0].iss_fmode == MCAST_UNDEFINED) {
2384
4
      MLD_PRINTF(("%s: initial join\n", __func__));
2385
4
      error = mld_initial_join(inm, mli, mtp, delay);
2386
4
      goto out;
2387
4
    } else if (inm->in6m_st[1].iss_fmode == MCAST_UNDEFINED) {
2388
0
      MLD_PRINTF(("%s: final leave\n", __func__));
2389
0
      mld_final_leave(inm, mli, mtp);
2390
0
      goto out;
2391
0
    }
2392
4
  } else {
2393
0
    MLD_PRINTF(("%s: filter set change\n", __func__));
2394
0
  }
2395
2396
0
  error = mld_handle_state_change(inm, mli, mtp);
2397
4
out:
2398
4
  return error;
2399
0
}
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
2411
 * initial state change for delay ticks (in units of seconds).
2412
 */
2413
static int
2414
mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
2415
    struct mld_tparams *mtp, const int delay)
2416
4
{
2417
4
  struct ifnet            *ifp;
2418
4
  struct ifqueue          *ifq;
2419
4
  int                      error, retval, syncstates;
2420
4
  int                      odelay;
2421
2422
4
  IN6M_LOCK_ASSERT_HELD(inm);
2423
4
  MLI_LOCK_ASSERT_NOTHELD(mli);
2424
4
  VERIFY(mtp != NULL);
2425
2426
4
  MLD_PRINTF(("%s: initial join %s on ifp 0x%llx(%s)\n",
2427
4
      __func__, ip6_sprintf(&inm->in6m_addr),
2428
4
      (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
2429
4
      if_name(inm->in6m_ifp)));
2430
2431
4
  error = 0;
2432
4
  syncstates = 1;
2433
2434
4
  ifp = inm->in6m_ifp;
2435
2436
4
  MLI_LOCK(mli);
2437
4
  VERIFY(mli->mli_ifp == ifp);
2438
2439
  /*
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.
2447
   * All other groups enter the appropriate state machine
2448
   * for the version in use on this link.
2449
   */
2450
4
  if ((ifp->if_flags & IFF_LOOPBACK) ||
2451
0
      (mli->mli_flags & MLIF_SILENT) ||
2452
0
      (IFNET_IS_CELLULAR(ifp) &&
2453
0
      (IN6_IS_ADDR_MC_LINKLOCAL(&inm->in6m_addr) || IN6_IS_ADDR_MC_UNICAST_BASED_LINKLOCAL(&inm->in6m_addr))) ||
2454
4
      !mld_is_addr_reported(&inm->in6m_addr)) {
2455
4
    MLD_PRINTF(("%s: not kicking state machine for silent group\n",
2456
4
        __func__));
2457
4
    inm->in6m_state = MLD_SILENT_MEMBER;
2458
4
    inm->in6m_timer = 0;
2459
4
  } 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
0
    if (mli->mli_version == MLD_VERSION_2 &&
2469
0
        inm->in6m_state == MLD_LEAVING_MEMBER) {
2470
0
      VERIFY(inm->in6m_nrelecnt != 0);
2471
0
      SLIST_INSERT_HEAD(&mli->mli_relinmhead, inm,
2472
0
          in6m_nrele);
2473
0
    }
2474
2475
0
    inm->in6m_state = MLD_REPORTING_MEMBER;
2476
2477
0
    switch (mli->mli_version) {
2478
0
    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
       */
2486
0
      odelay = MLD_RANDOM_DELAY(MLD_V1_MAX_RI);
2487
0
      if (delay) {
2488
0
        inm->in6m_timer = max(delay, odelay);
2489
0
        mtp->cst = 1;
2490
0
      } else {
2491
0
        inm->in6m_state = MLD_IDLE_MEMBER;
2492
0
        error = mld_v1_transmit_report(inm,
2493
0
            MLD_LISTENER_REPORT);
2494
2495
0
        IN6M_LOCK_ASSERT_HELD(inm);
2496
0
        MLI_LOCK_ASSERT_HELD(mli);
2497
2498
0
        if (error == 0) {
2499
0
          inm->in6m_timer = odelay;
2500
0
          mtp->cst = 1;
2501
0
        }
2502
0
      }
2503
0
      break;
2504
2505
0
    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
0
      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
0
      ifq = &inm->in6m_scq;
2519
0
      IF_DRAIN(ifq);
2520
0
      retval = mld_v2_enqueue_group_record(ifq, inm, 1,
2521
0
          0, 0, (mli->mli_flags & MLIF_USEALLOW));
2522
0
      mtp->cst = (ifq->ifq_len > 0);
2523
0
      MLD_PRINTF(("%s: enqueue record = %d\n",
2524
0
          __func__, retval));
2525
0
      if (retval <= 0) {
2526
0
        error = retval * -1;
2527
0
        break;
2528
0
      }
2529
2530
      /*
2531
       * Schedule transmission of pending state-change
2532
       * report up to RV times for this link. The timer
2533
       * will fire at the next mld_timeout (1 second)),
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
0
      VERIFY(mli->mli_rv > 1);
2540
0
      inm->in6m_scrv = (uint16_t)mli->mli_rv;
2541
0
      if (delay) {
2542
0
        if (inm->in6m_sctimer > 1) {
2543
0
          inm->in6m_sctimer =
2544
0
              MIN(inm->in6m_sctimer, (uint16_t)delay);
2545
0
        } else {
2546
0
          inm->in6m_sctimer = (uint16_t)delay;
2547
0
        }
2548
0
      } else {
2549
0
        inm->in6m_sctimer = 1;
2550
0
      }
2551
0
      mtp->sct = 1;
2552
0
      error = 0;
2553
0
      break;
2554
0
    }
2555
0
  }
2556
4
  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
4
  if (syncstates) {
2564
4
    in6m_commit(inm);
2565
4
    MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2566
4
        ip6_sprintf(&inm->in6m_addr),
2567
4
        if_name(inm->in6m_ifp)));
2568
4
  }
2569
2570
4
  return error;
2571
4
}
2572
2573
/*
2574
 * Issue an intermediate state change during the life-cycle.
2575
 */
2576
static int
2577
mld_handle_state_change(struct in6_multi *inm, struct mld_ifinfo *mli,
2578
    struct mld_tparams *mtp)
2579
0
{
2580
0
  struct ifnet            *ifp;
2581
0
  int                      retval = 0;
2582
2583
0
  IN6M_LOCK_ASSERT_HELD(inm);
2584
0
  MLI_LOCK_ASSERT_NOTHELD(mli);
2585
0
  VERIFY(mtp != NULL);
2586
2587
0
  MLD_PRINTF(("%s: state change for %s on ifp 0x%llx(%s)\n",
2588
0
      __func__, ip6_sprintf(&inm->in6m_addr),
2589
0
      (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
2590
0
      if_name(inm->in6m_ifp)));
2591
2592
0
  ifp = inm->in6m_ifp;
2593
2594
0
  MLI_LOCK(mli);
2595
0
  VERIFY(mli->mli_ifp == ifp);
2596
2597
0
  if ((ifp->if_flags & IFF_LOOPBACK) ||
2598
0
      (mli->mli_flags & MLIF_SILENT) ||
2599
0
      !mld_is_addr_reported(&inm->in6m_addr) ||
2600
0
      (mli->mli_version != MLD_VERSION_2)) {
2601
0
    MLI_UNLOCK(mli);
2602
0
    if (!mld_is_addr_reported(&inm->in6m_addr)) {
2603
0
      MLD_PRINTF(("%s: not kicking state machine for silent "
2604
0
          "group\n", __func__));
2605
0
    }
2606
0
    MLD_PRINTF(("%s: nothing to do\n", __func__));
2607
0
    in6m_commit(inm);
2608
0
    MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2609
0
        ip6_sprintf(&inm->in6m_addr),
2610
0
        if_name(inm->in6m_ifp)));
2611
0
    goto done;
2612
0
  }
2613
2614
0
  IF_DRAIN(&inm->in6m_scq);
2615
2616
0
  retval = mld_v2_enqueue_group_record(&inm->in6m_scq, inm, 1, 0, 0,
2617
0
      (mli->mli_flags & MLIF_USEALLOW));
2618
0
  mtp->cst = (inm->in6m_scq.ifq_len > 0);
2619
0
  MLD_PRINTF(("%s: enqueue record = %d\n", __func__, retval));
2620
0
  if (retval <= 0) {
2621
0
    MLI_UNLOCK(mli);
2622
0
    retval *= -1;
2623
0
    goto done;
2624
0
  } else {
2625
0
    retval = 0;
2626
0
  }
2627
2628
  /*
2629
   * If record(s) were enqueued, start the state-change
2630
   * report timer for this group.
2631
   */
2632
0
  inm->in6m_scrv = (uint16_t)mli->mli_rv;
2633
0
  inm->in6m_sctimer = 1;
2634
0
  mtp->sct = 1;
2635
0
  MLI_UNLOCK(mli);
2636
2637
0
done:
2638
0
  return retval;
2639
0
}
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
 */
2649
static void
2650
mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli,
2651
    struct mld_tparams *mtp)
2652
0
{
2653
0
  int syncstates = 1;
2654
2655
0
  IN6M_LOCK_ASSERT_HELD(inm);
2656
0
  MLI_LOCK_ASSERT_NOTHELD(mli);
2657
0
  VERIFY(mtp != NULL);
2658
2659
0
  MLD_PRINTF(("%s: final leave %s on ifp 0x%llx(%s)\n",
2660
0
      __func__, ip6_sprintf(&inm->in6m_addr),
2661
0
      (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
2662
0
      if_name(inm->in6m_ifp)));
2663
2664
0
  switch (inm->in6m_state) {
2665
0
  case MLD_NOT_MEMBER:
2666
0
  case MLD_SILENT_MEMBER:
2667
0
  case MLD_LEAVING_MEMBER:
2668
    /* Already leaving or left; do nothing. */
2669
0
    MLD_PRINTF(("%s: not kicking state machine for silent group\n",
2670
0
        __func__));
2671
0
    break;
2672
0
  case MLD_REPORTING_MEMBER:
2673
0
  case MLD_IDLE_MEMBER:
2674
0
  case MLD_G_QUERY_PENDING_MEMBER:
2675
0
  case MLD_SG_QUERY_PENDING_MEMBER:
2676
0
    MLI_LOCK(mli);
2677
0
    if (mli->mli_version == MLD_VERSION_1) {
2678
0
      if (inm->in6m_state == MLD_G_QUERY_PENDING_MEMBER ||
2679
0
          inm->in6m_state == MLD_SG_QUERY_PENDING_MEMBER) {
2680
0
        panic("%s: MLDv2 state reached, not MLDv2 "
2681
0
            "mode\n", __func__);
2682
        /* NOTREACHED */
2683
0
      }
2684
      /* scheduler timer if enqueue is successful */
2685
0
      mtp->cst = (mld_v1_transmit_report(inm,
2686
0
          MLD_LISTENER_DONE) == 0);
2687
2688
0
      IN6M_LOCK_ASSERT_HELD(inm);
2689
0
      MLI_LOCK_ASSERT_HELD(mli);
2690
2691
0
      inm->in6m_state = MLD_NOT_MEMBER;
2692
0
    } else if (mli->mli_version == MLD_VERSION_2) {
2693
      /*
2694
       * Stop group timer and all pending reports.
2695
       * Immediately enqueue a state-change report
2696
       * TO_IN {} to be sent on the next timeout,
2697
       * giving us an opportunity to merge reports.
2698
       */
2699
0
      IF_DRAIN(&inm->in6m_scq);
2700
0
      inm->in6m_timer = 0;
2701
0
      inm->in6m_scrv = (uint16_t)mli->mli_rv;
2702
0
      MLD_PRINTF(("%s: Leaving %s/%s with %d "
2703
0
          "pending retransmissions.\n", __func__,
2704
0
          ip6_sprintf(&inm->in6m_addr),
2705
0
          if_name(inm->in6m_ifp),
2706
0
          inm->in6m_scrv));
2707
0
      if (inm->in6m_scrv == 0) {
2708
0
        inm->in6m_state = MLD_NOT_MEMBER;
2709
0
        inm->in6m_sctimer = 0;
2710
0
      } else {
2711
0
        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
0
        IN6M_ADDREF_LOCKED(inm);
2719
0
        VERIFY(inm->in6m_debug & IFD_ATTACHED);
2720
0
        inm->in6m_reqcnt++;
2721
0
        VERIFY(inm->in6m_reqcnt >= 1);
2722
0
        inm->in6m_nrelecnt++;
2723
0
        VERIFY(inm->in6m_nrelecnt != 0);
2724
2725
0
        retval = mld_v2_enqueue_group_record(
2726
0
          &inm->in6m_scq, inm, 1, 0, 0,
2727
0
          (mli->mli_flags & MLIF_USEALLOW));
2728
0
        mtp->cst = (inm->in6m_scq.ifq_len > 0);
2729
0
        KASSERT(retval != 0,
2730
0
            ("%s: enqueue record = %d\n", __func__,
2731
0
            retval));
2732
2733
0
        inm->in6m_state = MLD_LEAVING_MEMBER;
2734
0
        inm->in6m_sctimer = 1;
2735
0
        mtp->sct = 1;
2736
0
        syncstates = 0;
2737
0
      }
2738
0
    }
2739
0
    MLI_UNLOCK(mli);
2740
0
    break;
2741
0
  case MLD_LAZY_MEMBER:
2742
0
  case MLD_SLEEPING_MEMBER:
2743
0
  case MLD_AWAKENING_MEMBER:
2744
    /* Our reports are suppressed; do nothing. */
2745
0
    break;
2746
0
  }
2747
2748
0
  if (syncstates) {
2749
0
    in6m_commit(inm);
2750
0
    MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
2751
0
        ip6_sprintf(&inm->in6m_addr),
2752
0
        if_name(inm->in6m_ifp)));
2753
0
    inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED;
2754
0
    MLD_PRINTF(("%s: T1 now MCAST_UNDEFINED for 0x%llx/%s\n",
2755
0
        __func__, (uint64_t)VM_KERNEL_ADDRPERM(&inm->in6m_addr),
2756
0
        if_name(inm->in6m_ifp)));
2757
0
  }
2758
0
}
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
 */
2787
static int
2788
mld_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
0
{
2792
0
  struct mldv2_record      mr;
2793
0
  struct mldv2_record     *pmr;
2794
0
  struct ifnet            *ifp;
2795
0
  struct ip6_msource      *ims, *nims;
2796
0
  struct mbuf             *m0, *m, *md;
2797
0
  int                      error, is_filter_list_change;
2798
0
  int                      minrec0len, m0srcs, msrcs, nbytes, off;
2799
0
  int                      record_has_sources;
2800
0
  int                      now;
2801
0
  uint8_t                  type;
2802
0
  uint8_t                  mode;
2803
2804
0
  IN6M_LOCK_ASSERT_HELD(inm);
2805
0
  MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
2806
2807
0
  error = 0;
2808
0
  ifp = inm->in6m_ifp;
2809
0
  is_filter_list_change = 0;
2810
0
  m = NULL;
2811
0
  m0 = NULL;
2812
0
  m0srcs = 0;
2813
0
  msrcs = 0;
2814
0
  nbytes = 0;
2815
0
  nims = NULL;
2816
0
  record_has_sources = 1;
2817
0
  pmr = NULL;
2818
0
  type = MLD_DO_NOTHING;
2819
0
  mode = (uint8_t)inm->in6m_st[1].iss_fmode;
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
0
  if (inm->in6m_st[0].iss_asm > 0 && inm->in6m_st[1].iss_asm > 0 &&
2827
0
      inm->in6m_nsrc == 0) {
2828
0
    record_has_sources = 0;
2829
0
  }
2830
2831
0
  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
0
    if (mode != inm->in6m_st[0].iss_fmode) {
2849
0
      if (mode == MCAST_EXCLUDE) {
2850
0
        MLD_PRINTF(("%s: change to EXCLUDE\n",
2851
0
            __func__));
2852
0
        type = MLD_CHANGE_TO_EXCLUDE_MODE;
2853
0
      } else {
2854
0
        MLD_PRINTF(("%s: change to INCLUDE\n",
2855
0
            __func__));
2856
0
        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
0
          if (mode == MCAST_UNDEFINED) {
2867
0
            type = MLD_BLOCK_OLD_SOURCES;
2868
0
          } else {
2869
0
            type = MLD_ALLOW_NEW_SOURCES;
2870
0
          }
2871
0
        } else {
2872
0
          type = MLD_CHANGE_TO_INCLUDE_MODE;
2873
0
          if (mode == MCAST_UNDEFINED) {
2874
0
            record_has_sources = 0;
2875
0
          }
2876
0
        }
2877
0
      }
2878
0
    } else {
2879
0
      if (record_has_sources) {
2880
0
        is_filter_list_change = 1;
2881
0
      } else {
2882
0
        type = MLD_DO_NOTHING;
2883
0
      }
2884
0
    }
2885
0
  } else {
2886
    /*
2887
     * Queue a current state record.
2888
     */
2889
0
    if (mode == MCAST_EXCLUDE) {
2890
0
      type = MLD_MODE_IS_EXCLUDE;
2891
0
    } else if (mode == MCAST_INCLUDE) {
2892
0
      type = MLD_MODE_IS_INCLUDE;
2893
0
      VERIFY(inm->in6m_st[1].iss_asm == 0);
2894
0
    }
2895
0
  }
2896
2897
  /*
2898
   * Generate the filter list changes using a separate function.
2899
   */
2900
0
  if (is_filter_list_change) {
2901
0
    return mld_v2_enqueue_filter_change(ifq, inm);
2902
0
  }
2903
2904
0
  if (type == MLD_DO_NOTHING) {
2905
0
    MLD_PRINTF(("%s: nothing to do for %s/%s\n",
2906
0
        __func__, ip6_sprintf(&inm->in6m_addr),
2907
0
        if_name(inm->in6m_ifp)));
2908
0
    return 0;
2909
0
  }
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
0
  minrec0len = sizeof(struct mldv2_record);
2917
0
  if (record_has_sources) {
2918
0
    minrec0len += sizeof(struct in6_addr);
2919
0
  }
2920
0
  MLD_PRINTF(("%s: queueing %s for %s/%s\n", __func__,
2921
0
      mld_rec_type_to_str(type),
2922
0
      ip6_sprintf(&inm->in6m_addr),
2923
0
      if_name(inm->in6m_ifp)));
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
0
  m0 = ifq->ifq_tail;
2934
0
  if (!is_group_query &&
2935
0
      m0 != NULL &&
2936
0
      (m0->m_pkthdr.vt_nrecs + 1 <= MLD_V2_REPORT_MAXRECS) &&
2937
0
      (m0->m_pkthdr.len + minrec0len) <
2938
0
      (ifp->if_mtu - MLD_MTUSPACE)) {
2939
0
    m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
2940
0
        sizeof(struct mldv2_record)) /
2941
0
        sizeof(struct in6_addr);
2942
0
    m = m0;
2943
0
    MLD_PRINTF(("%s: use existing packet\n", __func__));
2944
0
  } else {
2945
0
    if (IF_QFULL(ifq)) {
2946
0
      MLD_PRINTF(("%s: outbound queue full\n", __func__));
2947
0
      return -ENOMEM;
2948
0
    }
2949
0
    m = NULL;
2950
0
    m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
2951
0
        sizeof(struct mldv2_record)) / sizeof(struct in6_addr);
2952
0
    if (!is_state_change && !is_group_query) {
2953
0
      m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2954
0
    }
2955
0
    if (m == NULL) {
2956
0
      m = m_gethdr(M_DONTWAIT, MT_DATA);
2957
0
    }
2958
0
    if (m == NULL) {
2959
0
      return -ENOMEM;
2960
0
    }
2961
2962
0
    mld_save_context(m, ifp);
2963
2964
0
    MLD_PRINTF(("%s: allocated first packet\n", __func__));
2965
0
  }
2966
2967
  /*
2968
   * Append group record.
2969
   * If we have sources, we don't know how many yet.
2970
   */
2971
0
  mr.mr_type = type;
2972
0
  mr.mr_datalen = 0;
2973
0
  mr.mr_numsrc = 0;
2974
0
  mr.mr_addr = inm->in6m_addr;
2975
0
  in6_clearscope(&mr.mr_addr);
2976
0
  if (!m_append(m, sizeof(struct mldv2_record), (void *)&mr)) {
2977
0
    if (m != m0) {
2978
0
      m_freem(m);
2979
0
    }
2980
0
    MLD_PRINTF(("%s: m_append() failed.\n", __func__));
2981
0
    return -ENOMEM;
2982
0
  }
2983
0
  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
0
  if (record_has_sources) {
3002
0
    if (m == m0) {
3003
0
      md = m_last(m);
3004
0
      pmr = (struct mldv2_record *)(mtod(md, uint8_t *) +
3005
0
          md->m_len - nbytes);
3006
0
    } else {
3007
0
      md = m_getptr(m, 0, &off);
3008
0
      pmr = (struct mldv2_record *)(mtod(md, uint8_t *) +
3009
0
          off);
3010
0
    }
3011
0
    msrcs = 0;
3012
0
    RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs,
3013
0
        nims) {
3014
0
      MLD_PRINTF(("%s: visit node %s\n", __func__,
3015
0
          ip6_sprintf(&ims->im6s_addr)));
3016
0
      now = im6s_get_mode(inm, ims, 1);
3017
0
      MLD_PRINTF(("%s: node is %d\n", __func__, now));
3018
0
      if ((now != mode) ||
3019
0
          (now == mode &&
3020
0
          (!use_block_allow && mode == MCAST_UNDEFINED))) {
3021
0
        MLD_PRINTF(("%s: skip node\n", __func__));
3022
0
        continue;
3023
0
      }
3024
0
      if (is_source_query && ims->im6s_stp == 0) {
3025
0
        MLD_PRINTF(("%s: skip unrecorded node\n",
3026
0
            __func__));
3027
0
        continue;
3028
0
      }
3029
0
      MLD_PRINTF(("%s: append node\n", __func__));
3030
0
      if (!m_append(m, sizeof(struct in6_addr),
3031
0
          (void *)&ims->im6s_addr)) {
3032
0
        if (m != m0) {
3033
0
          m_freem(m);
3034
0
        }
3035
0
        MLD_PRINTF(("%s: m_append() failed.\n",
3036
0
            __func__));
3037
0
        return -ENOMEM;
3038
0
      }
3039
0
      nbytes += sizeof(struct in6_addr);
3040
0
      ++msrcs;
3041
0
      if (msrcs == m0srcs) {
3042
0
        break;
3043
0
      }
3044
0
    }
3045
0
    MLD_PRINTF(("%s: msrcs is %d this packet\n", __func__,
3046
0
        msrcs));
3047
0
    pmr->mr_numsrc = htons((uint16_t)msrcs);
3048
0
    nbytes += (msrcs * sizeof(struct in6_addr));
3049
0
  }
3050
3051
0
  if (is_source_query && msrcs == 0) {
3052
0
    MLD_PRINTF(("%s: no recorded sources to report\n", __func__));
3053
0
    if (m != m0) {
3054
0
      m_freem(m);
3055
0
    }
3056
0
    return 0;
3057
0
  }
3058
3059
  /*
3060
   * We are good to go with first packet.
3061
   */
3062
0
  if (m != m0) {
3063
0
    MLD_PRINTF(("%s: enqueueing first packet\n", __func__));
3064
0
    m->m_pkthdr.vt_nrecs = 1;
3065
0
    IF_ENQUEUE(ifq, m);
3066
0
  } else {
3067
0
    m->m_pkthdr.vt_nrecs++;
3068
0
  }
3069
  /*
3070
   * No further work needed if no source list in packet(s).
3071
   */
3072
0
  if (!record_has_sources) {
3073
0
    return nbytes;
3074
0
  }
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
0
  while (nims != NULL) {
3082
0
    if (IF_QFULL(ifq)) {
3083
0
      MLD_PRINTF(("%s: outbound queue full\n", __func__));
3084
0
      return -ENOMEM;
3085
0
    }
3086
0
    m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3087
0
    if (m == NULL) {
3088
0
      m = m_gethdr(M_DONTWAIT, MT_DATA);
3089
0
    }
3090
0
    if (m == NULL) {
3091
0
      return -ENOMEM;
3092
0
    }
3093
0
    mld_save_context(m, ifp);
3094
0
    md = m_getptr(m, 0, &off);
3095
0
    pmr = (struct mldv2_record *)(mtod(md, uint8_t *) + off);
3096
0
    MLD_PRINTF(("%s: allocated next packet\n", __func__));
3097
3098
0
    if (!m_append(m, sizeof(struct mldv2_record), (void *)&mr)) {
3099
0
      if (m != m0) {
3100
0
        m_freem(m);
3101
0
      }
3102
0
      MLD_PRINTF(("%s: m_append() failed.\n", __func__));
3103
0
      return -ENOMEM;
3104
0
    }
3105
0
    m->m_pkthdr.vt_nrecs = 1;
3106
0
    nbytes += sizeof(struct mldv2_record);
3107
3108
0
    m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
3109
0
        sizeof(struct mldv2_record)) / sizeof(struct in6_addr);
3110
3111
0
    msrcs = 0;
3112
0
    RB_FOREACH_FROM(ims, ip6_msource_tree, nims) {
3113
0
      MLD_PRINTF(("%s: visit node %s\n",
3114
0
          __func__, ip6_sprintf(&ims->im6s_addr)));
3115
0
      now = im6s_get_mode(inm, ims, 1);
3116
0
      if ((now != mode) ||
3117
0
          (now == mode &&
3118
0
          (!use_block_allow && mode == MCAST_UNDEFINED))) {
3119
0
        MLD_PRINTF(("%s: skip node\n", __func__));
3120
0
        continue;
3121
0
      }
3122
0
      if (is_source_query && ims->im6s_stp == 0) {
3123
0
        MLD_PRINTF(("%s: skip unrecorded node\n",
3124
0
            __func__));
3125
0
        continue;
3126
0
      }
3127
0
      MLD_PRINTF(("%s: append node\n", __func__));
3128
0
      if (!m_append(m, sizeof(struct in6_addr),
3129
0
          (void *)&ims->im6s_addr)) {
3130
0
        if (m != m0) {
3131
0
          m_freem(m);
3132
0
        }
3133
0
        MLD_PRINTF(("%s: m_append() failed.\n",
3134
0
            __func__));
3135
0
        return -ENOMEM;
3136
0
      }
3137
0
      ++msrcs;
3138
0
      if (msrcs == m0srcs) {
3139
0
        break;
3140
0
      }
3141
0
    }
3142
0
    pmr->mr_numsrc = htons((uint16_t)msrcs);
3143
0
    nbytes += (msrcs * sizeof(struct in6_addr));
3144
3145
0
    MLD_PRINTF(("%s: enqueueing next packet\n", __func__));
3146
0
    IF_ENQUEUE(ifq, m);
3147
0
  }
3148
3149
0
  return nbytes;
3150
0
}
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
 */
3157
typedef enum {
3158
  REC_NONE = 0x00,        /* MCAST_UNDEFINED */
3159
  REC_ALLOW = 0x01,       /* MCAST_INCLUDE */
3160
  REC_BLOCK = 0x02,       /* MCAST_EXCLUDE */
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
 */
3185
static int
3186
mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
3187
0
{
3188
0
  static const int MINRECLEN =
3189
0
      sizeof(struct mldv2_record) + sizeof(struct in6_addr);
3190
0
  struct ifnet            *ifp;
3191
0
  struct mldv2_record      mr;
3192
0
  struct mldv2_record     *pmr;
3193
0
  struct ip6_msource      *ims, *nims;
3194
0
  struct mbuf             *m, *m0, *md;
3195
0
  int                      m0srcs, nbytes, npbytes, off, rsrcs, schanged;
3196
0
  int                      nallow, nblock;
3197
0
  uint8_t                  mode, now, then;
3198
0
  rectype_t                crt, drt, nrt;
3199
3200
0
  IN6M_LOCK_ASSERT_HELD(inm);
3201
3202
0
  if (inm->in6m_nsrc == 0 ||
3203
0
      (inm->in6m_st[0].iss_asm > 0 && inm->in6m_st[1].iss_asm > 0)) {
3204
0
    return 0;
3205
0
  }
3206
3207
0
  ifp = inm->in6m_ifp;                    /* interface */
3208
0
  mode = (uint8_t)inm->in6m_st[1].iss_fmode;       /* filter mode at t1 */
3209
0
  crt = REC_NONE; /* current group record type */
3210
0
  drt = REC_NONE; /* mask of completed group record types */
3211
0
  nrt = REC_NONE; /* record type for current node */
3212
0
  m0srcs = 0;     /* # source which will fit in current mbuf chain */
3213
0
  npbytes = 0;    /* # of bytes appended this packet */
3214
0
  nbytes = 0;     /* # of bytes appended to group's state-change queue */
3215
0
  rsrcs = 0;      /* # sources encoded in current record */
3216
0
  schanged = 0;   /* # nodes encoded in overall filter change */
3217
0
  nallow = 0;     /* # of source entries in ALLOW_NEW */
3218
0
  nblock = 0;     /* # of source entries in BLOCK_OLD */
3219
0
  nims = NULL;    /* next tree node pointer */
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
0
  while (drt != REC_FULL) {
3229
0
    do {
3230
0
      m0 = ifq->ifq_tail;
3231
0
      if (m0 != NULL &&
3232
0
          (m0->m_pkthdr.vt_nrecs + 1 <=
3233
0
          MLD_V2_REPORT_MAXRECS) &&
3234
0
          (m0->m_pkthdr.len + MINRECLEN) <
3235
0
          (ifp->if_mtu - MLD_MTUSPACE)) {
3236
0
        m = m0;
3237
0
        m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
3238
0
            sizeof(struct mldv2_record)) /
3239
0
            sizeof(struct in6_addr);
3240
0
        MLD_PRINTF(("%s: use previous packet\n",
3241
0
            __func__));
3242
0
      } else {
3243
0
        m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3244
0
        if (m == NULL) {
3245
0
          m = m_gethdr(M_DONTWAIT, MT_DATA);
3246
0
        }
3247
0
        if (m == NULL) {
3248
0
          MLD_PRINTF(("%s: m_get*() failed\n",
3249
0
              __func__));
3250
0
          return -ENOMEM;
3251
0
        }
3252
0
        m->m_pkthdr.vt_nrecs = 0;
3253
0
        mld_save_context(m, ifp);
3254
0
        m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
3255
0
            sizeof(struct mldv2_record)) /
3256
0
            sizeof(struct in6_addr);
3257
0
        npbytes = 0;
3258
0
        MLD_PRINTF(("%s: allocated new packet\n",
3259
0
            __func__));
3260
0
      }
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
0
      memset(&mr, 0, sizeof(mr));
3269
0
      mr.mr_addr = inm->in6m_addr;
3270
0
      in6_clearscope(&mr.mr_addr);
3271
0
      if (!m_append(m, sizeof(mr), (void *)&mr)) {
3272
0
        if (m != m0) {
3273
0
          m_freem(m);
3274
0
        }
3275
0
        MLD_PRINTF(("%s: m_append() failed\n",
3276
0
            __func__));
3277
0
        return -ENOMEM;
3278
0
      }
3279
0
      npbytes += sizeof(struct mldv2_record);
3280
0
      if (m != m0) {
3281
        /* new packet; offset in chain */
3282
0
        md = m_getptr(m, npbytes -
3283
0
            sizeof(struct mldv2_record), &off);
3284
0
        pmr = (struct mldv2_record *)(mtod(md,
3285
0
            uint8_t *) + off);
3286
0
      } else {
3287
        /* current packet; offset from last append */
3288
0
        md = m_last(m);
3289
0
        pmr = (struct mldv2_record *)(mtod(md,
3290
0
            uint8_t *) + md->m_len -
3291
0
            sizeof(struct mldv2_record));
3292
0
      }
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
0
      rsrcs = 0;
3303
0
      if (nims == NULL) {
3304
0
        nims = RB_MIN(ip6_msource_tree,
3305
0
            &inm->in6m_srcs);
3306
0
      }
3307
0
      RB_FOREACH_FROM(ims, ip6_msource_tree, nims) {
3308
0
        MLD_PRINTF(("%s: visit node %s\n", __func__,
3309
0
            ip6_sprintf(&ims->im6s_addr)));
3310
0
        now = im6s_get_mode(inm, ims, 1);
3311
0
        then = im6s_get_mode(inm, ims, 0);
3312
0
        MLD_PRINTF(("%s: mode: t0 %d, t1 %d\n",
3313
0
            __func__, then, now));
3314
0
        if (now == then) {
3315
0
          MLD_PRINTF(("%s: skip unchanged\n",
3316
0
              __func__));
3317
0
          continue;
3318
0
        }
3319
0
        if (mode == MCAST_EXCLUDE &&
3320
0
            now == MCAST_INCLUDE) {
3321
0
          MLD_PRINTF(("%s: skip IN src on EX "
3322
0
              "group\n", __func__));
3323
0
          continue;
3324
0
        }
3325
0
        nrt = (rectype_t)now;
3326
0
        if (nrt == REC_NONE) {
3327
0
          nrt = (rectype_t)(~mode & REC_FULL);
3328
0
        }
3329
0
        if (schanged++ == 0) {
3330
0
          crt = nrt;
3331
0
        } else if (crt != nrt) {
3332
0
          continue;
3333
0
        }
3334
0
        if (!m_append(m, sizeof(struct in6_addr),
3335
0
            (void *)&ims->im6s_addr)) {
3336
0
          if (m != m0) {
3337
0
            m_freem(m);
3338
0
          }
3339
0
          MLD_PRINTF(("%s: m_append() failed\n",
3340
0
              __func__));
3341
0
          return -ENOMEM;
3342
0
        }
3343
0
        nallow += !!(crt == REC_ALLOW);
3344
0
        nblock += !!(crt == REC_BLOCK);
3345
0
        if (++rsrcs == m0srcs) {
3346
0
          break;
3347
0
        }
3348
0
      }
3349
      /*
3350
       * If we did not append any tree nodes on this
3351
       * pass, back out of allocations.
3352
       */
3353
0
      if (rsrcs == 0) {
3354
0
        npbytes -= sizeof(struct mldv2_record);
3355
0
        if (m != m0) {
3356
0
          MLD_PRINTF(("%s: m_free(m)\n",
3357
0
              __func__));
3358
0
          m_freem(m);
3359
0
        } else {
3360
0
          MLD_PRINTF(("%s: m_adj(m, -mr)\n",
3361
0
              __func__));
3362
0
          m_adj(m, -((int)sizeof(
3363
0
                struct mldv2_record)));
3364
0
        }
3365
0
        continue;
3366
0
      }
3367
0
      npbytes += (rsrcs * sizeof(struct in6_addr));
3368
0
      if (crt == REC_ALLOW) {
3369
0
        pmr->mr_type = MLD_ALLOW_NEW_SOURCES;
3370
0
      } else if (crt == REC_BLOCK) {
3371
0
        pmr->mr_type = MLD_BLOCK_OLD_SOURCES;
3372
0
      }
3373
0
      pmr->mr_numsrc = htons((uint16_t)rsrcs);
3374
      /*
3375
       * Count the new group record, and enqueue this
3376
       * packet if it wasn't already queued.
3377
       */
3378
0
      m->m_pkthdr.vt_nrecs++;
3379
0
      if (m != m0) {
3380
0
        IF_ENQUEUE(ifq, m);
3381
0
      }
3382
0
      nbytes += npbytes;
3383
0
    } while (nims != NULL);
3384
0
    drt |= crt;
3385
0
    crt = (~crt & REC_FULL);
3386
0
  }
3387
3388
0
  MLD_PRINTF(("%s: queued %d ALLOW_NEW, %d BLOCK_OLD\n", __func__,
3389
0
      nallow, nblock));
3390
3391
0
  return nbytes;
3392
0
}
3393
3394
static int
3395
mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq)
3396
0
{
3397
0
  struct ifqueue  *gq;
3398
0
  struct mbuf     *m;             /* pending state-change */
3399
0
  struct mbuf     *m0;            /* copy of pending state-change */
3400
0
  struct mbuf     *mt;            /* last state-change in packet */
3401
0
  struct mbuf     *n;
3402
0
  int              docopy, domerge;
3403
0
  u_int            recslen;
3404
3405
0
  IN6M_LOCK_ASSERT_HELD(inm);
3406
3407
0
  docopy = 0;
3408
0
  domerge = 0;
3409
0
  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
   */
3415
0
  if (inm->in6m_scrv > 0) {
3416
0
    docopy = 1;
3417
0
  }
3418
3419
0
  gq = &inm->in6m_scq;
3420
0
#ifdef MLD_DEBUG
3421
0
  if (gq->ifq_head == NULL) {
3422
0
    MLD_PRINTF(("%s: WARNING: queue for inm 0x%llx is empty\n",
3423
0
        __func__, (uint64_t)VM_KERNEL_ADDRPERM(inm)));
3424
0
  }
3425
0
#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
0
  m = gq->ifq_head;
3432
0
  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
0
    domerge = 0;
3442
0
    mt = ifscq->ifq_tail;
3443
0
    if (mt != NULL) {
3444
0
      recslen = m_length(m);
3445
3446
0
      if ((mt->m_pkthdr.vt_nrecs +
3447
0
          m->m_pkthdr.vt_nrecs <=
3448
0
          MLD_V2_REPORT_MAXRECS) &&
3449
0
          (mt->m_pkthdr.len + recslen <=
3450
0
          (inm->in6m_ifp->if_mtu - MLD_MTUSPACE))) {
3451
0
        domerge = 1;
3452
0
      }
3453
0
    }
3454
3455
0
    if (!domerge && IF_QFULL(gq)) {
3456
0
      MLD_PRINTF(("%s: outbound queue full, skipping whole "
3457
0
          "packet 0x%llx\n", __func__,
3458
0
          (uint64_t)VM_KERNEL_ADDRPERM(m)));
3459
0
      n = m->m_nextpkt;
3460
0
      if (!docopy) {
3461
0
        IF_REMQUEUE(gq, m);
3462
0
        m_freem(m);
3463
0
      }
3464
0
      m = n;
3465
0
      continue;
3466
0
    }
3467
3468
0
    if (!docopy) {
3469
0
      MLD_PRINTF(("%s: dequeueing 0x%llx\n", __func__,
3470
0
          (uint64_t)VM_KERNEL_ADDRPERM(m)));
3471
0
      n = m->m_nextpkt;
3472
0
      IF_REMQUEUE(gq, m);
3473
0
      m0 = m;
3474
0
      m = n;
3475
0
    } else {
3476
0
      MLD_PRINTF(("%s: copying 0x%llx\n", __func__,
3477
0
          (uint64_t)VM_KERNEL_ADDRPERM(m)));
3478
0
      m0 = m_dup(m, M_NOWAIT);
3479
0
      if (m0 == NULL) {
3480
0
        return ENOMEM;
3481
0
      }
3482
0
      m0->m_nextpkt = NULL;
3483
0
      m = m->m_nextpkt;
3484
0
    }
3485
3486
0
    if (!domerge) {
3487
0
      MLD_PRINTF(("%s: queueing 0x%llx to ifscq 0x%llx)\n",
3488
0
          __func__, (uint64_t)VM_KERNEL_ADDRPERM(m0),
3489
0
          (uint64_t)VM_KERNEL_ADDRPERM(ifscq)));
3490
0
      IF_ENQUEUE(ifscq, m0);
3491
0
    } else {
3492
0
      struct mbuf *mtl;       /* last mbuf of packet mt */
3493
3494
0
      MLD_PRINTF(("%s: merging 0x%llx with ifscq tail "
3495
0
          "0x%llx)\n", __func__,
3496
0
          (uint64_t)VM_KERNEL_ADDRPERM(m0),
3497
0
          (uint64_t)VM_KERNEL_ADDRPERM(mt)));
3498
3499
0
      mtl = m_last(mt);
3500
0
      m0->m_flags &= ~M_PKTHDR;
3501
0
      mt->m_pkthdr.len += recslen;
3502
0
      mt->m_pkthdr.vt_nrecs +=
3503
0
          m0->m_pkthdr.vt_nrecs;
3504
3505
0
      mtl->m_next = m0;
3506
0
    }
3507
0
  }
3508
3509
0
  return 0;
3510
0
}
3511
3512
/*
3513
 * Respond to a pending MLDv2 General Query.
3514
 */
3515
static uint32_t
3516
mld_v2_dispatch_general_query(struct mld_ifinfo *mli)
3517
443
{
3518
443
  struct ifnet            *ifp;
3519
443
  struct in6_multi        *inm;
3520
443
  struct in6_multistep    step;
3521
443
  int                      retval;
3522
3523
443
  MLI_LOCK_ASSERT_HELD(mli);
3524
3525
443
  VERIFY(mli->mli_version == MLD_VERSION_2);
3526
3527
0
  ifp = mli->mli_ifp;
3528
443
  MLI_UNLOCK(mli);
3529
3530
443
  in6_multihead_lock_shared();
3531
443
  IN6_FIRST_MULTI(step, inm);
3532
2.21k
  while (inm != NULL) {
3533
1.77k
    IN6M_LOCK(inm);
3534
1.77k
    if (inm->in6m_ifp != ifp) {
3535
0
      goto next;
3536
0
    }
3537
3538
1.77k
    switch (inm->in6m_state) {
3539
0
    case MLD_NOT_MEMBER:
3540
1.77k
    case MLD_SILENT_MEMBER:
3541
1.77k
      break;
3542
0
    case MLD_REPORTING_MEMBER:
3543
0
    case MLD_IDLE_MEMBER:
3544
0
    case MLD_LAZY_MEMBER:
3545
0
    case MLD_SLEEPING_MEMBER:
3546
0
    case MLD_AWAKENING_MEMBER:
3547
0
      inm->in6m_state = MLD_REPORTING_MEMBER;
3548
0
      MLI_LOCK(mli);
3549
0
      retval = mld_v2_enqueue_group_record(&mli->mli_gq,
3550
0
          inm, 0, 0, 0, 0);
3551
0
      MLI_UNLOCK(mli);
3552
0
      MLD_PRINTF(("%s: enqueue record = %d\n",
3553
0
          __func__, retval));
3554
0
      break;
3555
0
    case MLD_G_QUERY_PENDING_MEMBER:
3556
0
    case MLD_SG_QUERY_PENDING_MEMBER:
3557
0
    case MLD_LEAVING_MEMBER:
3558
0
      break;
3559
1.77k
    }
3560
1.77k
next:
3561
1.77k
    IN6M_UNLOCK(inm);
3562
1.77k
    IN6_NEXT_MULTI(step, inm);
3563
1.77k
  }
3564
443
  in6_multihead_lock_done();
3565
3566
443
  MLI_LOCK(mli);
3567
443
  mld_dispatch_queue_locked(mli, &mli->mli_gq, MLD_MAX_RESPONSE_BURST);
3568
443
  MLI_LOCK_ASSERT_HELD(mli);
3569
3570
  /*
3571
   * Slew transmission of bursts over 1 second intervals.
3572
   */
3573
443
  if (mli->mli_gq.ifq_head != NULL) {
3574
0
    mli->mli_v2_timer = 1 + MLD_RANDOM_DELAY(
3575
0
      MLD_RESPONSE_BURST_INTERVAL);
3576
0
  }
3577
3578
443
  return mli->mli_v2_timer;
3579
443
}
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
 */
3586
static void
3587
mld_dispatch_packet(struct mbuf *m)
3588
0
{
3589
0
  struct ip6_moptions     *im6o;
3590
0
  struct ifnet            *ifp;
3591
0
  struct ifnet            *oifp = NULL;
3592
0
  struct mbuf             *m0;
3593
0
  struct mbuf             *md;
3594
0
  struct ip6_hdr          *ip6;
3595
0
  struct mld_hdr          *mld;
3596
0
  int                      error;
3597
0
  int                      off;
3598
0
  int                      type;
3599
3600
0
  MLD_PRINTF(("%s: transmit 0x%llx\n", __func__,
3601
0
      (uint64_t)VM_KERNEL_ADDRPERM(m)));
3602
3603
  /*
3604
   * Check if the ifnet is still attached.
3605
   */
3606
0
  ifp = mld_restore_context(m);
3607
0
  if (ifp == NULL || !ifnet_is_attached(ifp, 0)) {
3608
0
    MLD_PRINTF(("%s: dropped 0x%llx as ifindex %u went away.\n",
3609
0
        __func__, (uint64_t)VM_KERNEL_ADDRPERM(m),
3610
0
        (u_int)if_index));
3611
0
    m_freem(m);
3612
0
    ip6stat.ip6s_noroute++;
3613
0
    return;
3614
0
  }
3615
3616
0
  im6o = ip6_allocmoptions(Z_WAITOK);
3617
0
  if (im6o == NULL) {
3618
0
    m_freem(m);
3619
0
    return;
3620
0
  }
3621
3622
0
  im6o->im6o_multicast_hlim  = 1;
3623
0
  im6o->im6o_multicast_loop = 0;
3624
0
  im6o->im6o_multicast_ifp = ifp;
3625
3626
0
  if (m->m_flags & M_MLDV1) {
3627
0
    m0 = m;
3628
0
  } else {
3629
0
    m0 = mld_v2_encap_report(ifp, m);
3630
0
    if (m0 == NULL) {
3631
0
      MLD_PRINTF(("%s: dropped 0x%llx\n", __func__,
3632
0
          (uint64_t)VM_KERNEL_ADDRPERM(m)));
3633
      /*
3634
       * mld_v2_encap_report() has already freed our mbuf.
3635
       */
3636
0
      IM6O_REMREF(im6o);
3637
0
      ip6stat.ip6s_odropped++;
3638
0
      return;
3639
0
    }
3640
0
  }
3641
3642
0
  mld_scrub_context(m0);
3643
0
  m->m_flags &= ~(M_PROTOFLAGS);
3644
0
  m0->m_pkthdr.rcvif = lo_ifp;
3645
3646
0
  ip6 = mtod(m0, struct ip6_hdr *);
3647
0
  (void)in6_setscope(&ip6->ip6_dst, ifp, NULL);
3648
3649
  /*
3650
   * Retrieve the ICMPv6 type before handoff to ip6_output(),
3651
   * so we can bump the stats.
3652
   */
3653
0
  md = m_getptr(m0, sizeof(struct ip6_hdr), &off);
3654
0
  mld = (struct mld_hdr *)(mtod(md, uint8_t *) + off);
3655
0
  type = mld->mld_type;
3656
3657
0
  if (ifp->if_eflags & IFEF_TXSTART) {
3658
    /*
3659
     * Use control service class if the outgoing
3660
     * interface supports transmit-start model.
3661
     */
3662
0
    (void) m_set_service_class(m0, MBUF_SC_CTL);
3663
0
  }
3664
3665
0
  error = ip6_output(m0, &mld_po, NULL, IPV6_UNSPECSRC, im6o,
3666
0
      &oifp, NULL);
3667
3668
0
  IM6O_REMREF(im6o);
3669
3670
0
  if (error) {
3671
0
    MLD_PRINTF(("%s: ip6_output(0x%llx) = %d\n", __func__,
3672
0
        (uint64_t)VM_KERNEL_ADDRPERM(m0), error));
3673
0
    if (oifp != NULL) {
3674
0
      ifnet_release(oifp);
3675
0
    }
3676
0
    return;
3677
0
  }
3678
3679
0
  icmp6stat.icp6s_outhist[type]++;
3680
0
  if (oifp != NULL) {
3681
0
    icmp6_ifstat_inc(oifp, ifs6_out_msg);
3682
0
    switch (type) {
3683
0
    case MLD_LISTENER_REPORT:
3684
0
    case MLDV2_LISTENER_REPORT:
3685
0
      icmp6_ifstat_inc(oifp, ifs6_out_mldreport);
3686
0
      break;
3687
0
    case MLD_LISTENER_DONE:
3688
0
      icmp6_ifstat_inc(oifp, ifs6_out_mlddone);
3689
0
      break;
3690
0
    }
3691
0
    ifnet_release(oifp);
3692
0
  }
3693
0
}
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
 */
3704
static struct mbuf *
3705
mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m)
3706
0
{
3707
0
  struct mbuf             *mh;
3708
0
  struct mldv2_report     *mld;
3709
0
  struct ip6_hdr          *ip6;
3710
0
  struct in6_ifaddr       *ia;
3711
0
  int                      mldreclen;
3712
3713
0
  VERIFY(m->m_flags & M_PKTHDR);
3714
3715
  /*
3716
   * RFC3590: OK to send as :: or tentative during DAD.
3717
   */
3718
0
  ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
3719
0
  if (ia == NULL) {
3720
0
    MLD_PRINTF(("%s: warning: ia is NULL\n", __func__));
3721
0
  }
3722
3723
0
  MGETHDR(mh, M_DONTWAIT, MT_HEADER);
3724
0
  if (mh == NULL) {
3725
0
    if (ia != NULL) {
3726
0
      IFA_REMREF(&ia->ia_ifa);
3727
0
    }
3728
0
    m_freem(m);
3729
0
    return NULL;
3730
0
  }
3731
0
  MH_ALIGN(mh, sizeof(struct ip6_hdr) + sizeof(struct mldv2_report));
3732
3733
0
  mldreclen = m_length(m);
3734
0
  MLD_PRINTF(("%s: mldreclen is %d\n", __func__, mldreclen));
3735
3736
0
  mh->m_len = sizeof(struct ip6_hdr) + sizeof(struct mldv2_report);
3737
0
  mh->m_pkthdr.len = sizeof(struct ip6_hdr) +
3738
0
      sizeof(struct mldv2_report) + mldreclen;
3739
3740
0
  ip6 = mtod(mh, struct ip6_hdr *);
3741
0
  ip6->ip6_flow = 0;
3742
0
  ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
3743
0
  ip6->ip6_vfc |= IPV6_VERSION;
3744
0
  ip6->ip6_nxt = IPPROTO_ICMPV6;
3745
0
  if (ia != NULL) {
3746
0
    IFA_LOCK(&ia->ia_ifa);
3747
0
  }
3748
0
  ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
3749
0
  if (ia != NULL) {
3750
0
    IFA_UNLOCK(&ia->ia_ifa);
3751
0
    IFA_REMREF(&ia->ia_ifa);
3752
0
    ia = NULL;
3753
0
  }
3754
0
  ip6->ip6_dst = in6addr_linklocal_allv2routers;
3755
  /* scope ID will be set in netisr */
3756
3757
0
  mld = (struct mldv2_report *)(ip6 + 1);
3758
0
  mld->mld_type = MLDV2_LISTENER_REPORT;
3759
0
  mld->mld_code = 0;
3760
0
  mld->mld_cksum = 0;
3761
0
  mld->mld_v2_reserved = 0;
3762
0
  mld->mld_v2_numrecs = htons(m->m_pkthdr.vt_nrecs);
3763
0
  m->m_pkthdr.vt_nrecs = 0;
3764
0
  m->m_flags &= ~M_PKTHDR;
3765
3766
0
  mh->m_next = m;
3767
0
  mld->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6,
3768
0
      sizeof(struct ip6_hdr), sizeof(struct mldv2_report) + mldreclen);
3769
0
  return mh;
3770
0
}
3771
3772
#ifdef MLD_DEBUG
3773
static const char *
3774
mld_rec_type_to_str(const int type)
3775
0
{
3776
0
  switch (type) {
3777
0
  case MLD_CHANGE_TO_EXCLUDE_MODE:
3778
0
    return "TO_EX";
3779
0
  case MLD_CHANGE_TO_INCLUDE_MODE:
3780
0
    return "TO_IN";
3781
0
  case MLD_MODE_IS_EXCLUDE:
3782
0
    return "MODE_EX";
3783
0
  case MLD_MODE_IS_INCLUDE:
3784
0
    return "MODE_IN";
3785
0
  case MLD_ALLOW_NEW_SOURCES:
3786
0
    return "ALLOW_NEW";
3787
0
  case MLD_BLOCK_OLD_SOURCES:
3788
0
    return "BLOCK_OLD";
3789
0
  default:
3790
0
    break;
3791
0
  }
3792
0
  return "unknown";
3793
0
}
3794
#endif
3795
3796
void
3797
mld_init(void)
3798
1
{
3799
1
  MLD_PRINTF(("%s: initializing\n", __func__));
3800
3801
  /* Setup lock group and attribute for mld_mtx */
3802
1
  mld_mtx_grp_attr = lck_grp_attr_alloc_init();
3803
1
  mld_mtx_grp = lck_grp_alloc_init("mld_mtx\n", mld_mtx_grp_attr);
3804
1
  mld_mtx_attr = lck_attr_alloc_init();
3805
1
  lck_mtx_init(&mld_mtx, mld_mtx_grp, mld_mtx_attr);
3806
3807
1
  ip6_initpktopts(&mld_po);
3808
1
  mld_po.ip6po_hlim = 1;
3809
1
  mld_po.ip6po_hbh = &mld_ra.hbh;
3810
1
  mld_po.ip6po_prefer_tempaddr = IP6PO_TEMPADDR_NOTPREFER;
3811
1
  mld_po.ip6po_flags = IP6PO_DONTFRAG;
3812
1
  LIST_INIT(&mli_head);
3813
1
}