xref: /xnu-8796.101.5/bsd/netinet/igmp.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1*aca3beaaSApple OSS Distributions /*
2*aca3beaaSApple OSS Distributions  * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3*aca3beaaSApple OSS Distributions  *
4*aca3beaaSApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*aca3beaaSApple OSS Distributions  *
6*aca3beaaSApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*aca3beaaSApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*aca3beaaSApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*aca3beaaSApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*aca3beaaSApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*aca3beaaSApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*aca3beaaSApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*aca3beaaSApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*aca3beaaSApple OSS Distributions  *
15*aca3beaaSApple OSS Distributions  * Please obtain a copy of the License at
16*aca3beaaSApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*aca3beaaSApple OSS Distributions  *
18*aca3beaaSApple OSS Distributions  * The Original Code and all software distributed under the License are
19*aca3beaaSApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*aca3beaaSApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*aca3beaaSApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*aca3beaaSApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*aca3beaaSApple OSS Distributions  * Please see the License for the specific language governing rights and
24*aca3beaaSApple OSS Distributions  * limitations under the License.
25*aca3beaaSApple OSS Distributions  *
26*aca3beaaSApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*aca3beaaSApple OSS Distributions  */
28*aca3beaaSApple OSS Distributions /*-
29*aca3beaaSApple OSS Distributions  * Copyright (c) 2007-2009 Bruce Simpson.
30*aca3beaaSApple OSS Distributions  * Copyright (c) 1988 Stephen Deering.
31*aca3beaaSApple OSS Distributions  * Copyright (c) 1992, 1993
32*aca3beaaSApple OSS Distributions  *	The Regents of the University of California.  All rights reserved.
33*aca3beaaSApple OSS Distributions  *
34*aca3beaaSApple OSS Distributions  * This code is derived from software contributed to Berkeley by
35*aca3beaaSApple OSS Distributions  * Stephen Deering of Stanford University.
36*aca3beaaSApple OSS Distributions  *
37*aca3beaaSApple OSS Distributions  * Redistribution and use in source and binary forms, with or without
38*aca3beaaSApple OSS Distributions  * modification, are permitted provided that the following conditions
39*aca3beaaSApple OSS Distributions  * are met:
40*aca3beaaSApple OSS Distributions  * 1. Redistributions of source code must retain the above copyright
41*aca3beaaSApple OSS Distributions  *    notice, this list of conditions and the following disclaimer.
42*aca3beaaSApple OSS Distributions  * 2. Redistributions in binary form must reproduce the above copyright
43*aca3beaaSApple OSS Distributions  *    notice, this list of conditions and the following disclaimer in the
44*aca3beaaSApple OSS Distributions  *    documentation and/or other materials provided with the distribution.
45*aca3beaaSApple OSS Distributions  * 3. All advertising materials mentioning features or use of this software
46*aca3beaaSApple OSS Distributions  *    must display the following acknowledgement:
47*aca3beaaSApple OSS Distributions  *	This product includes software developed by the University of
48*aca3beaaSApple OSS Distributions  *	California, Berkeley and its contributors.
49*aca3beaaSApple OSS Distributions  * 4. Neither the name of the University nor the names of its contributors
50*aca3beaaSApple OSS Distributions  *    may be used to endorse or promote products derived from this software
51*aca3beaaSApple OSS Distributions  *    without specific prior written permission.
52*aca3beaaSApple OSS Distributions  *
53*aca3beaaSApple OSS Distributions  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54*aca3beaaSApple OSS Distributions  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55*aca3beaaSApple OSS Distributions  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56*aca3beaaSApple OSS Distributions  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57*aca3beaaSApple OSS Distributions  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58*aca3beaaSApple OSS Distributions  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59*aca3beaaSApple OSS Distributions  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60*aca3beaaSApple OSS Distributions  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61*aca3beaaSApple OSS Distributions  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62*aca3beaaSApple OSS Distributions  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63*aca3beaaSApple OSS Distributions  * SUCH DAMAGE.
64*aca3beaaSApple OSS Distributions  *
65*aca3beaaSApple OSS Distributions  *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
66*aca3beaaSApple OSS Distributions  */
67*aca3beaaSApple OSS Distributions /*
68*aca3beaaSApple OSS Distributions  * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
69*aca3beaaSApple OSS Distributions  * support for mandatory and extensible security protections.  This notice
70*aca3beaaSApple OSS Distributions  * is included in support of clause 2.2 (b) of the Apple Public License,
71*aca3beaaSApple OSS Distributions  * Version 2.0.
72*aca3beaaSApple OSS Distributions  */
73*aca3beaaSApple OSS Distributions 
74*aca3beaaSApple OSS Distributions /*
75*aca3beaaSApple OSS Distributions  * Internet Group Management Protocol (IGMP) routines.
76*aca3beaaSApple OSS Distributions  * [RFC1112, RFC2236, RFC3376]
77*aca3beaaSApple OSS Distributions  *
78*aca3beaaSApple OSS Distributions  * Written by Steve Deering, Stanford, May 1988.
79*aca3beaaSApple OSS Distributions  * Modified by Rosen Sharma, Stanford, Aug 1994.
80*aca3beaaSApple OSS Distributions  * Modified by Bill Fenner, Xerox PARC, Feb 1995.
81*aca3beaaSApple OSS Distributions  * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995.
82*aca3beaaSApple OSS Distributions  * Significantly rewritten for IGMPv3, VIMAGE, and SMP by Bruce Simpson.
83*aca3beaaSApple OSS Distributions  *
84*aca3beaaSApple OSS Distributions  * MULTICAST Revision: 3.5.1.4
85*aca3beaaSApple OSS Distributions  */
86*aca3beaaSApple OSS Distributions 
87*aca3beaaSApple OSS Distributions #include <sys/cdefs.h>
88*aca3beaaSApple OSS Distributions 
89*aca3beaaSApple OSS Distributions #include <sys/param.h>
90*aca3beaaSApple OSS Distributions #include <sys/systm.h>
91*aca3beaaSApple OSS Distributions #include <sys/malloc.h>
92*aca3beaaSApple OSS Distributions #include <sys/mbuf.h>
93*aca3beaaSApple OSS Distributions #include <sys/socket.h>
94*aca3beaaSApple OSS Distributions #include <sys/protosw.h>
95*aca3beaaSApple OSS Distributions #include <sys/kernel.h>
96*aca3beaaSApple OSS Distributions #include <sys/sysctl.h>
97*aca3beaaSApple OSS Distributions #include <sys/mcache.h>
98*aca3beaaSApple OSS Distributions 
99*aca3beaaSApple OSS Distributions #include <libkern/libkern.h>
100*aca3beaaSApple OSS Distributions #include <kern/zalloc.h>
101*aca3beaaSApple OSS Distributions 
102*aca3beaaSApple OSS Distributions #include <net/if.h>
103*aca3beaaSApple OSS Distributions #include <net/route.h>
104*aca3beaaSApple OSS Distributions 
105*aca3beaaSApple OSS Distributions #include <netinet/in.h>
106*aca3beaaSApple OSS Distributions #include <netinet/in_var.h>
107*aca3beaaSApple OSS Distributions #include <netinet/in_systm.h>
108*aca3beaaSApple OSS Distributions #include <netinet/ip.h>
109*aca3beaaSApple OSS Distributions #include <netinet/ip_var.h>
110*aca3beaaSApple OSS Distributions #include <netinet/igmp.h>
111*aca3beaaSApple OSS Distributions #include <netinet/igmp_var.h>
112*aca3beaaSApple OSS Distributions #include <netinet/kpi_ipfilter_var.h>
113*aca3beaaSApple OSS Distributions 
114*aca3beaaSApple OSS Distributions #if SKYWALK
115*aca3beaaSApple OSS Distributions #include <skywalk/core/skywalk_var.h>
116*aca3beaaSApple OSS Distributions #endif /* SKYWALK */
117*aca3beaaSApple OSS Distributions 
118*aca3beaaSApple OSS Distributions SLIST_HEAD(igmp_inm_relhead, in_multi);
119*aca3beaaSApple OSS Distributions 
120*aca3beaaSApple OSS Distributions static void     igi_initvar(struct igmp_ifinfo *, struct ifnet *, int);
121*aca3beaaSApple OSS Distributions static struct igmp_ifinfo *igi_alloc(zalloc_flags_t);
122*aca3beaaSApple OSS Distributions static void     igi_free(struct igmp_ifinfo *);
123*aca3beaaSApple OSS Distributions static void     igi_delete(const struct ifnet *, struct igmp_inm_relhead *);
124*aca3beaaSApple OSS Distributions static void     igmp_dispatch_queue(struct igmp_ifinfo *, struct ifqueue *,
125*aca3beaaSApple OSS Distributions     int, const int);
126*aca3beaaSApple OSS Distributions static void     igmp_final_leave(struct in_multi *, struct igmp_ifinfo *,
127*aca3beaaSApple OSS Distributions     struct igmp_tparams *);
128*aca3beaaSApple OSS Distributions static int      igmp_handle_state_change(struct in_multi *,
129*aca3beaaSApple OSS Distributions     struct igmp_ifinfo *, struct igmp_tparams *);
130*aca3beaaSApple OSS Distributions static int      igmp_initial_join(struct in_multi *, struct igmp_ifinfo *,
131*aca3beaaSApple OSS Distributions     struct igmp_tparams *);
132*aca3beaaSApple OSS Distributions static int      igmp_input_v1_query(struct ifnet *, const struct ip *,
133*aca3beaaSApple OSS Distributions     const struct igmp *);
134*aca3beaaSApple OSS Distributions static int      igmp_input_v2_query(struct ifnet *, const struct ip *,
135*aca3beaaSApple OSS Distributions     const struct igmp *);
136*aca3beaaSApple OSS Distributions static int      igmp_input_v3_query(struct ifnet *, const struct ip *,
137*aca3beaaSApple OSS Distributions     /*const*/ struct igmpv3 *);
138*aca3beaaSApple OSS Distributions static int      igmp_input_v3_group_query(struct in_multi *,
139*aca3beaaSApple OSS Distributions     int, /*const*/ struct igmpv3 *);
140*aca3beaaSApple OSS Distributions static int      igmp_input_v1_report(struct ifnet *, struct mbuf *,
141*aca3beaaSApple OSS Distributions     /*const*/ struct ip *, /*const*/ struct igmp *);
142*aca3beaaSApple OSS Distributions static int      igmp_input_v2_report(struct ifnet *, struct mbuf *,
143*aca3beaaSApple OSS Distributions     /*const*/ struct ip *, /*const*/ struct igmp *);
144*aca3beaaSApple OSS Distributions static void     igmp_sendpkt(struct mbuf *);
145*aca3beaaSApple OSS Distributions static __inline__ int   igmp_isgroupreported(const struct in_addr);
146*aca3beaaSApple OSS Distributions static struct mbuf *igmp_ra_alloc(void);
147*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
148*aca3beaaSApple OSS Distributions static const char *igmp_rec_type_to_str(const int);
149*aca3beaaSApple OSS Distributions #endif
150*aca3beaaSApple OSS Distributions static uint32_t igmp_set_version(struct igmp_ifinfo *, const int);
151*aca3beaaSApple OSS Distributions static void     igmp_flush_relq(struct igmp_ifinfo *,
152*aca3beaaSApple OSS Distributions     struct igmp_inm_relhead *);
153*aca3beaaSApple OSS Distributions static int      igmp_v1v2_queue_report(struct in_multi *, const int);
154*aca3beaaSApple OSS Distributions static void     igmp_v1v2_process_group_timer(struct in_multi *, const int);
155*aca3beaaSApple OSS Distributions static void     igmp_v1v2_process_querier_timers(struct igmp_ifinfo *);
156*aca3beaaSApple OSS Distributions static uint32_t igmp_v2_update_group(struct in_multi *, const int);
157*aca3beaaSApple OSS Distributions static void     igmp_v3_cancel_link_timers(struct igmp_ifinfo *);
158*aca3beaaSApple OSS Distributions static uint32_t igmp_v3_dispatch_general_query(struct igmp_ifinfo *);
159*aca3beaaSApple OSS Distributions static struct mbuf *
160*aca3beaaSApple OSS Distributions igmp_v3_encap_report(struct ifnet *, struct mbuf *);
161*aca3beaaSApple OSS Distributions static int      igmp_v3_enqueue_group_record(struct ifqueue *,
162*aca3beaaSApple OSS Distributions     struct in_multi *, const int, const int, const int);
163*aca3beaaSApple OSS Distributions static int      igmp_v3_enqueue_filter_change(struct ifqueue *,
164*aca3beaaSApple OSS Distributions     struct in_multi *);
165*aca3beaaSApple OSS Distributions static void     igmp_v3_process_group_timers(struct igmp_ifinfo *,
166*aca3beaaSApple OSS Distributions     struct ifqueue *, struct ifqueue *, struct in_multi *,
167*aca3beaaSApple OSS Distributions     const unsigned int);
168*aca3beaaSApple OSS Distributions static int      igmp_v3_merge_state_changes(struct in_multi *,
169*aca3beaaSApple OSS Distributions     struct ifqueue *);
170*aca3beaaSApple OSS Distributions static void     igmp_v3_suppress_group_record(struct in_multi *);
171*aca3beaaSApple OSS Distributions static int      sysctl_igmp_ifinfo SYSCTL_HANDLER_ARGS;
172*aca3beaaSApple OSS Distributions static int      sysctl_igmp_gsr SYSCTL_HANDLER_ARGS;
173*aca3beaaSApple OSS Distributions static int      sysctl_igmp_default_version SYSCTL_HANDLER_ARGS;
174*aca3beaaSApple OSS Distributions 
175*aca3beaaSApple OSS Distributions static int igmp_timeout_run;            /* IGMP timer is scheduled to run */
176*aca3beaaSApple OSS Distributions static void igmp_timeout(void *);
177*aca3beaaSApple OSS Distributions static void igmp_sched_timeout(bool);
178*aca3beaaSApple OSS Distributions 
179*aca3beaaSApple OSS Distributions static struct mbuf *m_raopt;            /* Router Alert option */
180*aca3beaaSApple OSS Distributions 
181*aca3beaaSApple OSS Distributions static int querier_present_timers_running;      /* IGMPv1/v2 older version
182*aca3beaaSApple OSS Distributions                                                  * querier present */
183*aca3beaaSApple OSS Distributions static int interface_timers_running;            /* IGMPv3 general
184*aca3beaaSApple OSS Distributions                                                  * query response */
185*aca3beaaSApple OSS Distributions static int state_change_timers_running;         /* IGMPv3 state-change
186*aca3beaaSApple OSS Distributions                                                  * retransmit */
187*aca3beaaSApple OSS Distributions static int current_state_timers_running;        /* IGMPv1/v2 host
188*aca3beaaSApple OSS Distributions                                                  * report; IGMPv3 g/sg
189*aca3beaaSApple OSS Distributions                                                  * query response */
190*aca3beaaSApple OSS Distributions 
191*aca3beaaSApple OSS Distributions /*
192*aca3beaaSApple OSS Distributions  * Subsystem lock macros.
193*aca3beaaSApple OSS Distributions  */
194*aca3beaaSApple OSS Distributions #define IGMP_LOCK()                     \
195*aca3beaaSApple OSS Distributions 	lck_mtx_lock(&igmp_mtx)
196*aca3beaaSApple OSS Distributions #define IGMP_LOCK_ASSERT_HELD()         \
197*aca3beaaSApple OSS Distributions 	LCK_MTX_ASSERT(&igmp_mtx, LCK_MTX_ASSERT_OWNED)
198*aca3beaaSApple OSS Distributions #define IGMP_LOCK_ASSERT_NOTHELD()      \
199*aca3beaaSApple OSS Distributions 	LCK_MTX_ASSERT(&igmp_mtx, LCK_MTX_ASSERT_NOTOWNED)
200*aca3beaaSApple OSS Distributions #define IGMP_UNLOCK()                   \
201*aca3beaaSApple OSS Distributions 	lck_mtx_unlock(&igmp_mtx)
202*aca3beaaSApple OSS Distributions 
203*aca3beaaSApple OSS Distributions static LIST_HEAD(, igmp_ifinfo) igi_head;
204*aca3beaaSApple OSS Distributions static struct igmpstat_v3 igmpstat_v3 = {
205*aca3beaaSApple OSS Distributions 	.igps_version = IGPS_VERSION_3,
206*aca3beaaSApple OSS Distributions 	.igps_len = sizeof(struct igmpstat_v3),
207*aca3beaaSApple OSS Distributions };
208*aca3beaaSApple OSS Distributions static struct igmpstat igmpstat; /* old IGMPv2 stats structure */
209*aca3beaaSApple OSS Distributions static struct timeval igmp_gsrdelay = {.tv_sec = 10, .tv_usec = 0};
210*aca3beaaSApple OSS Distributions 
211*aca3beaaSApple OSS Distributions static int igmp_recvifkludge = 1;
212*aca3beaaSApple OSS Distributions static int igmp_sendra = 1;
213*aca3beaaSApple OSS Distributions static int igmp_sendlocal = 1;
214*aca3beaaSApple OSS Distributions static int igmp_v1enable = 1;
215*aca3beaaSApple OSS Distributions static int igmp_v2enable = 1;
216*aca3beaaSApple OSS Distributions static int igmp_legacysupp = 0;
217*aca3beaaSApple OSS Distributions static int igmp_default_version = IGMP_VERSION_3;
218*aca3beaaSApple OSS Distributions 
219*aca3beaaSApple OSS Distributions SYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RD | CTLFLAG_LOCKED,
220*aca3beaaSApple OSS Distributions     &igmpstat, igmpstat, "");
221*aca3beaaSApple OSS Distributions SYSCTL_STRUCT(_net_inet_igmp, OID_AUTO, v3stats,
222*aca3beaaSApple OSS Distributions     CTLFLAG_RD | CTLFLAG_LOCKED, &igmpstat_v3, igmpstat_v3, "");
223*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_igmp, OID_AUTO, recvifkludge, CTLFLAG_RW | CTLFLAG_LOCKED,
224*aca3beaaSApple OSS Distributions     &igmp_recvifkludge, 0,
225*aca3beaaSApple OSS Distributions     "Rewrite IGMPv1/v2 reports from 0.0.0.0 to contain subnet address");
226*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_igmp, OID_AUTO, sendra, CTLFLAG_RW | CTLFLAG_LOCKED,
227*aca3beaaSApple OSS Distributions     &igmp_sendra, 0,
228*aca3beaaSApple OSS Distributions     "Send IP Router Alert option in IGMPv2/v3 messages");
229*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_igmp, OID_AUTO, sendlocal, CTLFLAG_RW | CTLFLAG_LOCKED,
230*aca3beaaSApple OSS Distributions     &igmp_sendlocal, 0,
231*aca3beaaSApple OSS Distributions     "Send IGMP membership reports for 224.0.0.0/24 groups");
232*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_igmp, OID_AUTO, v1enable, CTLFLAG_RW | CTLFLAG_LOCKED,
233*aca3beaaSApple OSS Distributions     &igmp_v1enable, 0,
234*aca3beaaSApple OSS Distributions     "Enable backwards compatibility with IGMPv1");
235*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_igmp, OID_AUTO, v2enable, CTLFLAG_RW | CTLFLAG_LOCKED,
236*aca3beaaSApple OSS Distributions     &igmp_v2enable, 0,
237*aca3beaaSApple OSS Distributions     "Enable backwards compatibility with IGMPv2");
238*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_igmp, OID_AUTO, legacysupp, CTLFLAG_RW | CTLFLAG_LOCKED,
239*aca3beaaSApple OSS Distributions     &igmp_legacysupp, 0,
240*aca3beaaSApple OSS Distributions     "Allow v1/v2 reports to suppress v3 group responses");
241*aca3beaaSApple OSS Distributions SYSCTL_PROC(_net_inet_igmp, OID_AUTO, default_version,
242*aca3beaaSApple OSS Distributions     CTLTYPE_INT | CTLFLAG_RW,
243*aca3beaaSApple OSS Distributions     &igmp_default_version, 0, sysctl_igmp_default_version, "I",
244*aca3beaaSApple OSS Distributions     "Default version of IGMP to run on each interface");
245*aca3beaaSApple OSS Distributions SYSCTL_PROC(_net_inet_igmp, OID_AUTO, gsrdelay,
246*aca3beaaSApple OSS Distributions     CTLTYPE_INT | CTLFLAG_RW,
247*aca3beaaSApple OSS Distributions     &igmp_gsrdelay.tv_sec, 0, sysctl_igmp_gsr, "I",
248*aca3beaaSApple OSS Distributions     "Rate limit for IGMPv3 Group-and-Source queries in seconds");
249*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
250*aca3beaaSApple OSS Distributions int igmp_debug = 0;
251*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_igmp, OID_AUTO,
252*aca3beaaSApple OSS Distributions     debug, CTLFLAG_RW | CTLFLAG_LOCKED, &igmp_debug, 0, "");
253*aca3beaaSApple OSS Distributions #endif
254*aca3beaaSApple OSS Distributions 
255*aca3beaaSApple OSS Distributions SYSCTL_NODE(_net_inet_igmp, OID_AUTO, ifinfo, CTLFLAG_RD | CTLFLAG_LOCKED,
256*aca3beaaSApple OSS Distributions     sysctl_igmp_ifinfo, "Per-interface IGMPv3 state");
257*aca3beaaSApple OSS Distributions 
258*aca3beaaSApple OSS Distributions /* Lock group and attribute for igmp_mtx */
259*aca3beaaSApple OSS Distributions static LCK_ATTR_DECLARE(igmp_mtx_attr, 0, 0);
260*aca3beaaSApple OSS Distributions static LCK_GRP_DECLARE(igmp_mtx_grp, "igmp_mtx");
261*aca3beaaSApple OSS Distributions 
262*aca3beaaSApple OSS Distributions /*
263*aca3beaaSApple OSS Distributions  * Locking and reference counting:
264*aca3beaaSApple OSS Distributions  *
265*aca3beaaSApple OSS Distributions  * igmp_mtx mainly protects igi_head.  In cases where both igmp_mtx and
266*aca3beaaSApple OSS Distributions  * in_multihead_lock must be held, the former must be acquired first in order
267*aca3beaaSApple OSS Distributions  * to maintain lock ordering.  It is not a requirement that igmp_mtx be
268*aca3beaaSApple OSS Distributions  * acquired first before in_multihead_lock, but in case both must be acquired
269*aca3beaaSApple OSS Distributions  * in succession, the correct lock ordering must be followed.
270*aca3beaaSApple OSS Distributions  *
271*aca3beaaSApple OSS Distributions  * Instead of walking the if_multiaddrs list at the interface and returning
272*aca3beaaSApple OSS Distributions  * the ifma_protospec value of a matching entry, we search the global list
273*aca3beaaSApple OSS Distributions  * of in_multi records and find it that way; this is done with in_multihead
274*aca3beaaSApple OSS Distributions  * lock held.  Doing so avoids the race condition issues that many other BSDs
275*aca3beaaSApple OSS Distributions  * suffer from (therefore in our implementation, ifma_protospec will never be
276*aca3beaaSApple OSS Distributions  * NULL for as long as the in_multi is valid.)
277*aca3beaaSApple OSS Distributions  *
278*aca3beaaSApple OSS Distributions  * The above creates a requirement for the in_multi to stay in in_multihead
279*aca3beaaSApple OSS Distributions  * list even after the final IGMP leave (in IGMPv3 mode) until no longer needs
280*aca3beaaSApple OSS Distributions  * be retransmitted (this is not required for IGMPv1/v2.)  In order to handle
281*aca3beaaSApple OSS Distributions  * this, the request and reference counts of the in_multi are bumped up when
282*aca3beaaSApple OSS Distributions  * the state changes to IGMP_LEAVING_MEMBER, and later dropped in the timeout
283*aca3beaaSApple OSS Distributions  * handler.  Each in_multi holds a reference to the underlying igmp_ifinfo.
284*aca3beaaSApple OSS Distributions  *
285*aca3beaaSApple OSS Distributions  * Thus, the permitted lock oder is:
286*aca3beaaSApple OSS Distributions  *
287*aca3beaaSApple OSS Distributions  *	igmp_mtx, in_multihead_lock, inm_lock, igi_lock
288*aca3beaaSApple OSS Distributions  *
289*aca3beaaSApple OSS Distributions  * Any may be taken independently, but if any are held at the same time,
290*aca3beaaSApple OSS Distributions  * the above lock order must be followed.
291*aca3beaaSApple OSS Distributions  */
292*aca3beaaSApple OSS Distributions static LCK_MTX_DECLARE_ATTR(igmp_mtx, &igmp_mtx_grp, &igmp_mtx_attr);
293*aca3beaaSApple OSS Distributions static int igmp_timers_are_running;
294*aca3beaaSApple OSS Distributions 
295*aca3beaaSApple OSS Distributions #define IGMP_ADD_DETACHED_INM(_head, _inm) {                            \
296*aca3beaaSApple OSS Distributions 	SLIST_INSERT_HEAD(_head, _inm, inm_dtle);                       \
297*aca3beaaSApple OSS Distributions }
298*aca3beaaSApple OSS Distributions 
299*aca3beaaSApple OSS Distributions #define IGMP_REMOVE_DETACHED_INM(_head) {                               \
300*aca3beaaSApple OSS Distributions 	struct in_multi *_inm, *_inm_tmp;                               \
301*aca3beaaSApple OSS Distributions 	SLIST_FOREACH_SAFE(_inm, _head, inm_dtle, _inm_tmp) {           \
302*aca3beaaSApple OSS Distributions 	        SLIST_REMOVE(_head, _inm, in_multi, inm_dtle);          \
303*aca3beaaSApple OSS Distributions 	        INM_REMREF(_inm);                                       \
304*aca3beaaSApple OSS Distributions 	}                                                               \
305*aca3beaaSApple OSS Distributions 	VERIFY(SLIST_EMPTY(_head));                                     \
306*aca3beaaSApple OSS Distributions }
307*aca3beaaSApple OSS Distributions 
308*aca3beaaSApple OSS Distributions static KALLOC_TYPE_DEFINE(igi_zone, struct igmp_ifinfo, NET_KT_DEFAULT);
309*aca3beaaSApple OSS Distributions 
310*aca3beaaSApple OSS Distributions /* Store IGMPv3 record count in the module private scratch space */
311*aca3beaaSApple OSS Distributions #define vt_nrecs        pkt_mpriv.__mpriv_u.__mpriv32[0].__mpriv32_u.__val16[0]
312*aca3beaaSApple OSS Distributions 
313*aca3beaaSApple OSS Distributions static __inline void
igmp_save_context(struct mbuf * m,struct ifnet * ifp)314*aca3beaaSApple OSS Distributions igmp_save_context(struct mbuf *m, struct ifnet *ifp)
315*aca3beaaSApple OSS Distributions {
316*aca3beaaSApple OSS Distributions 	m->m_pkthdr.rcvif = ifp;
317*aca3beaaSApple OSS Distributions }
318*aca3beaaSApple OSS Distributions 
319*aca3beaaSApple OSS Distributions static __inline void
igmp_scrub_context(struct mbuf * m)320*aca3beaaSApple OSS Distributions igmp_scrub_context(struct mbuf *m)
321*aca3beaaSApple OSS Distributions {
322*aca3beaaSApple OSS Distributions 	m->m_pkthdr.rcvif = NULL;
323*aca3beaaSApple OSS Distributions }
324*aca3beaaSApple OSS Distributions 
325*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
326*aca3beaaSApple OSS Distributions static __inline const char *
inet_ntop_haddr(in_addr_t haddr,char * buf,socklen_t size)327*aca3beaaSApple OSS Distributions inet_ntop_haddr(in_addr_t haddr, char *buf, socklen_t size)
328*aca3beaaSApple OSS Distributions {
329*aca3beaaSApple OSS Distributions 	struct in_addr ia;
330*aca3beaaSApple OSS Distributions 
331*aca3beaaSApple OSS Distributions 	ia.s_addr = htonl(haddr);
332*aca3beaaSApple OSS Distributions 	return inet_ntop(AF_INET, &ia, buf, size);
333*aca3beaaSApple OSS Distributions }
334*aca3beaaSApple OSS Distributions #endif
335*aca3beaaSApple OSS Distributions 
336*aca3beaaSApple OSS Distributions /*
337*aca3beaaSApple OSS Distributions  * Restore context from a queued IGMP output chain.
338*aca3beaaSApple OSS Distributions  * Return saved ifp.
339*aca3beaaSApple OSS Distributions  */
340*aca3beaaSApple OSS Distributions static __inline struct ifnet *
igmp_restore_context(struct mbuf * m)341*aca3beaaSApple OSS Distributions igmp_restore_context(struct mbuf *m)
342*aca3beaaSApple OSS Distributions {
343*aca3beaaSApple OSS Distributions 	return m->m_pkthdr.rcvif;
344*aca3beaaSApple OSS Distributions }
345*aca3beaaSApple OSS Distributions 
346*aca3beaaSApple OSS Distributions /*
347*aca3beaaSApple OSS Distributions  * Retrieve or set default IGMP version.
348*aca3beaaSApple OSS Distributions  */
349*aca3beaaSApple OSS Distributions static int
350*aca3beaaSApple OSS Distributions sysctl_igmp_default_version SYSCTL_HANDLER_ARGS
351*aca3beaaSApple OSS Distributions {
352*aca3beaaSApple OSS Distributions #pragma unused(oidp, arg2)
353*aca3beaaSApple OSS Distributions 	int      error;
354*aca3beaaSApple OSS Distributions 	int      new;
355*aca3beaaSApple OSS Distributions 
356*aca3beaaSApple OSS Distributions 	IGMP_LOCK();
357*aca3beaaSApple OSS Distributions 
358*aca3beaaSApple OSS Distributions 	error = SYSCTL_OUT(req, arg1, sizeof(int));
359*aca3beaaSApple OSS Distributions 	if (error || !req->newptr) {
360*aca3beaaSApple OSS Distributions 		goto out_locked;
361*aca3beaaSApple OSS Distributions 	}
362*aca3beaaSApple OSS Distributions 
363*aca3beaaSApple OSS Distributions 	new = igmp_default_version;
364*aca3beaaSApple OSS Distributions 
365*aca3beaaSApple OSS Distributions 	error = SYSCTL_IN(req, &new, sizeof(int));
366*aca3beaaSApple OSS Distributions 	if (error) {
367*aca3beaaSApple OSS Distributions 		goto out_locked;
368*aca3beaaSApple OSS Distributions 	}
369*aca3beaaSApple OSS Distributions 
370*aca3beaaSApple OSS Distributions 	if (new < IGMP_VERSION_1 || new > IGMP_VERSION_3) {
371*aca3beaaSApple OSS Distributions 		error = EINVAL;
372*aca3beaaSApple OSS Distributions 		goto out_locked;
373*aca3beaaSApple OSS Distributions 	}
374*aca3beaaSApple OSS Distributions 
375*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: change igmp_default_version from %d to %d\n",
376*aca3beaaSApple OSS Distributions 	    __func__, igmp_default_version, new));
377*aca3beaaSApple OSS Distributions 
378*aca3beaaSApple OSS Distributions 	igmp_default_version = new;
379*aca3beaaSApple OSS Distributions 
380*aca3beaaSApple OSS Distributions out_locked:
381*aca3beaaSApple OSS Distributions 	IGMP_UNLOCK();
382*aca3beaaSApple OSS Distributions 	return error;
383*aca3beaaSApple OSS Distributions }
384*aca3beaaSApple OSS Distributions 
385*aca3beaaSApple OSS Distributions /*
386*aca3beaaSApple OSS Distributions  * Retrieve or set threshold between group-source queries in seconds.
387*aca3beaaSApple OSS Distributions  *
388*aca3beaaSApple OSS Distributions  */
389*aca3beaaSApple OSS Distributions static int
390*aca3beaaSApple OSS Distributions sysctl_igmp_gsr SYSCTL_HANDLER_ARGS
391*aca3beaaSApple OSS Distributions {
392*aca3beaaSApple OSS Distributions #pragma unused(arg1, arg2)
393*aca3beaaSApple OSS Distributions 	int error;
394*aca3beaaSApple OSS Distributions 	int i;
395*aca3beaaSApple OSS Distributions 
396*aca3beaaSApple OSS Distributions 	IGMP_LOCK();
397*aca3beaaSApple OSS Distributions 
398*aca3beaaSApple OSS Distributions 	i = (int)igmp_gsrdelay.tv_sec;
399*aca3beaaSApple OSS Distributions 
400*aca3beaaSApple OSS Distributions 	error = sysctl_handle_int(oidp, &i, 0, req);
401*aca3beaaSApple OSS Distributions 	if (error || !req->newptr) {
402*aca3beaaSApple OSS Distributions 		goto out_locked;
403*aca3beaaSApple OSS Distributions 	}
404*aca3beaaSApple OSS Distributions 
405*aca3beaaSApple OSS Distributions 	if (i < -1 || i >= 60) {
406*aca3beaaSApple OSS Distributions 		error = EINVAL;
407*aca3beaaSApple OSS Distributions 		goto out_locked;
408*aca3beaaSApple OSS Distributions 	}
409*aca3beaaSApple OSS Distributions 
410*aca3beaaSApple OSS Distributions 	igmp_gsrdelay.tv_sec = i;
411*aca3beaaSApple OSS Distributions 
412*aca3beaaSApple OSS Distributions out_locked:
413*aca3beaaSApple OSS Distributions 	IGMP_UNLOCK();
414*aca3beaaSApple OSS Distributions 	return error;
415*aca3beaaSApple OSS Distributions }
416*aca3beaaSApple OSS Distributions 
417*aca3beaaSApple OSS Distributions /*
418*aca3beaaSApple OSS Distributions  * Expose struct igmp_ifinfo to userland, keyed by ifindex.
419*aca3beaaSApple OSS Distributions  * For use by ifmcstat(8).
420*aca3beaaSApple OSS Distributions  *
421*aca3beaaSApple OSS Distributions  */
422*aca3beaaSApple OSS Distributions static int
423*aca3beaaSApple OSS Distributions sysctl_igmp_ifinfo SYSCTL_HANDLER_ARGS
424*aca3beaaSApple OSS Distributions {
425*aca3beaaSApple OSS Distributions #pragma unused(oidp)
426*aca3beaaSApple OSS Distributions 	int                     *name;
427*aca3beaaSApple OSS Distributions 	int                      error;
428*aca3beaaSApple OSS Distributions 	u_int                    namelen;
429*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
430*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo      *igi;
431*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo_u    igi_u;
432*aca3beaaSApple OSS Distributions 
433*aca3beaaSApple OSS Distributions 	name = (int *)arg1;
434*aca3beaaSApple OSS Distributions 	namelen = arg2;
435*aca3beaaSApple OSS Distributions 
436*aca3beaaSApple OSS Distributions 	if (req->newptr != USER_ADDR_NULL) {
437*aca3beaaSApple OSS Distributions 		return EPERM;
438*aca3beaaSApple OSS Distributions 	}
439*aca3beaaSApple OSS Distributions 
440*aca3beaaSApple OSS Distributions 	if (namelen != 1) {
441*aca3beaaSApple OSS Distributions 		return EINVAL;
442*aca3beaaSApple OSS Distributions 	}
443*aca3beaaSApple OSS Distributions 
444*aca3beaaSApple OSS Distributions 	IGMP_LOCK();
445*aca3beaaSApple OSS Distributions 
446*aca3beaaSApple OSS Distributions 	if (name[0] <= 0 || name[0] > (u_int)if_index) {
447*aca3beaaSApple OSS Distributions 		error = ENOENT;
448*aca3beaaSApple OSS Distributions 		goto out_locked;
449*aca3beaaSApple OSS Distributions 	}
450*aca3beaaSApple OSS Distributions 
451*aca3beaaSApple OSS Distributions 	error = ENOENT;
452*aca3beaaSApple OSS Distributions 
453*aca3beaaSApple OSS Distributions 	ifnet_head_lock_shared();
454*aca3beaaSApple OSS Distributions 	ifp = ifindex2ifnet[name[0]];
455*aca3beaaSApple OSS Distributions 	ifnet_head_done();
456*aca3beaaSApple OSS Distributions 	if (ifp == NULL) {
457*aca3beaaSApple OSS Distributions 		goto out_locked;
458*aca3beaaSApple OSS Distributions 	}
459*aca3beaaSApple OSS Distributions 
460*aca3beaaSApple OSS Distributions 	bzero(&igi_u, sizeof(igi_u));
461*aca3beaaSApple OSS Distributions 
462*aca3beaaSApple OSS Distributions 	LIST_FOREACH(igi, &igi_head, igi_link) {
463*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
464*aca3beaaSApple OSS Distributions 		if (ifp != igi->igi_ifp) {
465*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
466*aca3beaaSApple OSS Distributions 			continue;
467*aca3beaaSApple OSS Distributions 		}
468*aca3beaaSApple OSS Distributions 		igi_u.igi_ifindex = igi->igi_ifp->if_index;
469*aca3beaaSApple OSS Distributions 		igi_u.igi_version = igi->igi_version;
470*aca3beaaSApple OSS Distributions 		igi_u.igi_v1_timer = igi->igi_v1_timer;
471*aca3beaaSApple OSS Distributions 		igi_u.igi_v2_timer = igi->igi_v2_timer;
472*aca3beaaSApple OSS Distributions 		igi_u.igi_v3_timer = igi->igi_v3_timer;
473*aca3beaaSApple OSS Distributions 		igi_u.igi_flags = igi->igi_flags;
474*aca3beaaSApple OSS Distributions 		igi_u.igi_rv = igi->igi_rv;
475*aca3beaaSApple OSS Distributions 		igi_u.igi_qi = igi->igi_qi;
476*aca3beaaSApple OSS Distributions 		igi_u.igi_qri = igi->igi_qri;
477*aca3beaaSApple OSS Distributions 		igi_u.igi_uri = igi->igi_uri;
478*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
479*aca3beaaSApple OSS Distributions 
480*aca3beaaSApple OSS Distributions 		error = SYSCTL_OUT(req, &igi_u, sizeof(igi_u));
481*aca3beaaSApple OSS Distributions 		break;
482*aca3beaaSApple OSS Distributions 	}
483*aca3beaaSApple OSS Distributions 
484*aca3beaaSApple OSS Distributions out_locked:
485*aca3beaaSApple OSS Distributions 	IGMP_UNLOCK();
486*aca3beaaSApple OSS Distributions 	return error;
487*aca3beaaSApple OSS Distributions }
488*aca3beaaSApple OSS Distributions 
489*aca3beaaSApple OSS Distributions /*
490*aca3beaaSApple OSS Distributions  * Dispatch an entire queue of pending packet chains
491*aca3beaaSApple OSS Distributions  *
492*aca3beaaSApple OSS Distributions  * Must not be called with inm_lock held.
493*aca3beaaSApple OSS Distributions  */
494*aca3beaaSApple OSS Distributions static void
igmp_dispatch_queue(struct igmp_ifinfo * igi,struct ifqueue * ifq,int limit,const int loop)495*aca3beaaSApple OSS Distributions igmp_dispatch_queue(struct igmp_ifinfo *igi, struct ifqueue *ifq, int limit,
496*aca3beaaSApple OSS Distributions     const int loop)
497*aca3beaaSApple OSS Distributions {
498*aca3beaaSApple OSS Distributions 	struct mbuf *m;
499*aca3beaaSApple OSS Distributions 	struct ip *ip;
500*aca3beaaSApple OSS Distributions 
501*aca3beaaSApple OSS Distributions 	if (igi != NULL) {
502*aca3beaaSApple OSS Distributions 		IGI_LOCK_ASSERT_HELD(igi);
503*aca3beaaSApple OSS Distributions 	}
504*aca3beaaSApple OSS Distributions 
505*aca3beaaSApple OSS Distributions #if SKYWALK
506*aca3beaaSApple OSS Distributions 	/*
507*aca3beaaSApple OSS Distributions 	 * Since this function is called holding the igi lock, we need to ensure we
508*aca3beaaSApple OSS Distributions 	 * don't enter the driver directly because a deadlock can happen if another
509*aca3beaaSApple OSS Distributions 	 * thread holding the workloop lock tries to acquire the igi lock at
510*aca3beaaSApple OSS Distributions 	 * the same time.
511*aca3beaaSApple OSS Distributions 	 */
512*aca3beaaSApple OSS Distributions 	sk_protect_t protect = sk_async_transmit_protect();
513*aca3beaaSApple OSS Distributions #endif /* SKYWALK */
514*aca3beaaSApple OSS Distributions 
515*aca3beaaSApple OSS Distributions 	for (;;) {
516*aca3beaaSApple OSS Distributions 		IF_DEQUEUE(ifq, m);
517*aca3beaaSApple OSS Distributions 		if (m == NULL) {
518*aca3beaaSApple OSS Distributions 			break;
519*aca3beaaSApple OSS Distributions 		}
520*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: dispatch 0x%llx from 0x%llx\n", __func__,
521*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifq),
522*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(m)));
523*aca3beaaSApple OSS Distributions 		ip = mtod(m, struct ip *);
524*aca3beaaSApple OSS Distributions 		if (loop) {
525*aca3beaaSApple OSS Distributions 			m->m_flags |= M_IGMP_LOOP;
526*aca3beaaSApple OSS Distributions 		}
527*aca3beaaSApple OSS Distributions 		if (igi != NULL) {
528*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
529*aca3beaaSApple OSS Distributions 		}
530*aca3beaaSApple OSS Distributions 		igmp_sendpkt(m);
531*aca3beaaSApple OSS Distributions 		if (igi != NULL) {
532*aca3beaaSApple OSS Distributions 			IGI_LOCK(igi);
533*aca3beaaSApple OSS Distributions 		}
534*aca3beaaSApple OSS Distributions 		if (--limit == 0) {
535*aca3beaaSApple OSS Distributions 			break;
536*aca3beaaSApple OSS Distributions 		}
537*aca3beaaSApple OSS Distributions 	}
538*aca3beaaSApple OSS Distributions 
539*aca3beaaSApple OSS Distributions #if SKYWALK
540*aca3beaaSApple OSS Distributions 	sk_async_transmit_unprotect(protect);
541*aca3beaaSApple OSS Distributions #endif /* SKYWALK */
542*aca3beaaSApple OSS Distributions 
543*aca3beaaSApple OSS Distributions 	if (igi != NULL) {
544*aca3beaaSApple OSS Distributions 		IGI_LOCK_ASSERT_HELD(igi);
545*aca3beaaSApple OSS Distributions 	}
546*aca3beaaSApple OSS Distributions }
547*aca3beaaSApple OSS Distributions 
548*aca3beaaSApple OSS Distributions /*
549*aca3beaaSApple OSS Distributions  * Filter outgoing IGMP report state by group.
550*aca3beaaSApple OSS Distributions  *
551*aca3beaaSApple OSS Distributions  * Reports are ALWAYS suppressed for ALL-HOSTS (224.0.0.1).
552*aca3beaaSApple OSS Distributions  * If the net.inet.igmp.sendlocal sysctl is 0, then IGMP reports are
553*aca3beaaSApple OSS Distributions  * disabled for all groups in the 224.0.0.0/24 link-local scope. However,
554*aca3beaaSApple OSS Distributions  * this may break certain IGMP snooping switches which rely on the old
555*aca3beaaSApple OSS Distributions  * report behaviour.
556*aca3beaaSApple OSS Distributions  *
557*aca3beaaSApple OSS Distributions  * Return zero if the given group is one for which IGMP reports
558*aca3beaaSApple OSS Distributions  * should be suppressed, or non-zero if reports should be issued.
559*aca3beaaSApple OSS Distributions  */
560*aca3beaaSApple OSS Distributions 
561*aca3beaaSApple OSS Distributions static __inline__
562*aca3beaaSApple OSS Distributions int
igmp_isgroupreported(const struct in_addr addr)563*aca3beaaSApple OSS Distributions igmp_isgroupreported(const struct in_addr addr)
564*aca3beaaSApple OSS Distributions {
565*aca3beaaSApple OSS Distributions 	if (in_allhosts(addr) ||
566*aca3beaaSApple OSS Distributions 	    ((!igmp_sendlocal && IN_LOCAL_GROUP(ntohl(addr.s_addr))))) {
567*aca3beaaSApple OSS Distributions 		return 0;
568*aca3beaaSApple OSS Distributions 	}
569*aca3beaaSApple OSS Distributions 
570*aca3beaaSApple OSS Distributions 	return 1;
571*aca3beaaSApple OSS Distributions }
572*aca3beaaSApple OSS Distributions 
573*aca3beaaSApple OSS Distributions /*
574*aca3beaaSApple OSS Distributions  * Construct a Router Alert option to use in outgoing packets.
575*aca3beaaSApple OSS Distributions  */
576*aca3beaaSApple OSS Distributions static struct mbuf *
igmp_ra_alloc(void)577*aca3beaaSApple OSS Distributions igmp_ra_alloc(void)
578*aca3beaaSApple OSS Distributions {
579*aca3beaaSApple OSS Distributions 	struct mbuf     *m;
580*aca3beaaSApple OSS Distributions 	struct ipoption *p;
581*aca3beaaSApple OSS Distributions 
582*aca3beaaSApple OSS Distributions 	MGET(m, M_WAITOK, MT_DATA);
583*aca3beaaSApple OSS Distributions 	p = mtod(m, struct ipoption *);
584*aca3beaaSApple OSS Distributions 	p->ipopt_dst.s_addr = INADDR_ANY;
585*aca3beaaSApple OSS Distributions 	p->ipopt_list[0] = (char)IPOPT_RA;      /* Router Alert Option */
586*aca3beaaSApple OSS Distributions 	p->ipopt_list[1] = 0x04;        /* 4 bytes long */
587*aca3beaaSApple OSS Distributions 	p->ipopt_list[2] = IPOPT_EOL;   /* End of IP option list */
588*aca3beaaSApple OSS Distributions 	p->ipopt_list[3] = 0x00;        /* pad byte */
589*aca3beaaSApple OSS Distributions 	m->m_len = sizeof(p->ipopt_dst) + p->ipopt_list[1];
590*aca3beaaSApple OSS Distributions 
591*aca3beaaSApple OSS Distributions 	return m;
592*aca3beaaSApple OSS Distributions }
593*aca3beaaSApple OSS Distributions 
594*aca3beaaSApple OSS Distributions /*
595*aca3beaaSApple OSS Distributions  * Attach IGMP when PF_INET is attached to an interface.
596*aca3beaaSApple OSS Distributions  */
597*aca3beaaSApple OSS Distributions struct igmp_ifinfo *
igmp_domifattach(struct ifnet * ifp,zalloc_flags_t how)598*aca3beaaSApple OSS Distributions igmp_domifattach(struct ifnet *ifp, zalloc_flags_t how)
599*aca3beaaSApple OSS Distributions {
600*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo *igi;
601*aca3beaaSApple OSS Distributions 
602*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: called for ifp 0x%llx(%s)\n",
603*aca3beaaSApple OSS Distributions 	    __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), ifp->if_name));
604*aca3beaaSApple OSS Distributions 
605*aca3beaaSApple OSS Distributions 	igi = igi_alloc(how);
606*aca3beaaSApple OSS Distributions 	if (igi == NULL) {
607*aca3beaaSApple OSS Distributions 		return NULL;
608*aca3beaaSApple OSS Distributions 	}
609*aca3beaaSApple OSS Distributions 
610*aca3beaaSApple OSS Distributions 	IGMP_LOCK();
611*aca3beaaSApple OSS Distributions 
612*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
613*aca3beaaSApple OSS Distributions 	igi_initvar(igi, ifp, 0);
614*aca3beaaSApple OSS Distributions 	igi->igi_debug |= IFD_ATTACHED;
615*aca3beaaSApple OSS Distributions 	IGI_ADDREF_LOCKED(igi); /* hold a reference for igi_head */
616*aca3beaaSApple OSS Distributions 	IGI_ADDREF_LOCKED(igi); /* hold a reference for caller */
617*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
618*aca3beaaSApple OSS Distributions 	ifnet_lock_shared(ifp);
619*aca3beaaSApple OSS Distributions 	igmp_initsilent(ifp, igi);
620*aca3beaaSApple OSS Distributions 	ifnet_lock_done(ifp);
621*aca3beaaSApple OSS Distributions 
622*aca3beaaSApple OSS Distributions 	LIST_INSERT_HEAD(&igi_head, igi, igi_link);
623*aca3beaaSApple OSS Distributions 
624*aca3beaaSApple OSS Distributions 	IGMP_UNLOCK();
625*aca3beaaSApple OSS Distributions 
626*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: allocate igmp_ifinfo for ifp 0x%llx(%s)\n", __func__,
627*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(ifp), ifp->if_name));
628*aca3beaaSApple OSS Distributions 
629*aca3beaaSApple OSS Distributions 	return igi;
630*aca3beaaSApple OSS Distributions }
631*aca3beaaSApple OSS Distributions 
632*aca3beaaSApple OSS Distributions /*
633*aca3beaaSApple OSS Distributions  * Attach IGMP when PF_INET is reattached to an interface.  Caller is
634*aca3beaaSApple OSS Distributions  * expected to have an outstanding reference to the igi.
635*aca3beaaSApple OSS Distributions  */
636*aca3beaaSApple OSS Distributions void
igmp_domifreattach(struct igmp_ifinfo * igi)637*aca3beaaSApple OSS Distributions igmp_domifreattach(struct igmp_ifinfo *igi)
638*aca3beaaSApple OSS Distributions {
639*aca3beaaSApple OSS Distributions 	struct ifnet *ifp;
640*aca3beaaSApple OSS Distributions 
641*aca3beaaSApple OSS Distributions 	IGMP_LOCK();
642*aca3beaaSApple OSS Distributions 
643*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
644*aca3beaaSApple OSS Distributions 	VERIFY(!(igi->igi_debug & IFD_ATTACHED));
645*aca3beaaSApple OSS Distributions 	ifp = igi->igi_ifp;
646*aca3beaaSApple OSS Distributions 	VERIFY(ifp != NULL);
647*aca3beaaSApple OSS Distributions 	igi_initvar(igi, ifp, 1);
648*aca3beaaSApple OSS Distributions 	igi->igi_debug |= IFD_ATTACHED;
649*aca3beaaSApple OSS Distributions 	IGI_ADDREF_LOCKED(igi); /* hold a reference for igi_head */
650*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
651*aca3beaaSApple OSS Distributions 	ifnet_lock_shared(ifp);
652*aca3beaaSApple OSS Distributions 	igmp_initsilent(ifp, igi);
653*aca3beaaSApple OSS Distributions 	ifnet_lock_done(ifp);
654*aca3beaaSApple OSS Distributions 
655*aca3beaaSApple OSS Distributions 	LIST_INSERT_HEAD(&igi_head, igi, igi_link);
656*aca3beaaSApple OSS Distributions 
657*aca3beaaSApple OSS Distributions 	IGMP_UNLOCK();
658*aca3beaaSApple OSS Distributions 
659*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: reattached igmp_ifinfo for ifp 0x%llx(%s)\n",
660*aca3beaaSApple OSS Distributions 	    __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), ifp->if_name));
661*aca3beaaSApple OSS Distributions }
662*aca3beaaSApple OSS Distributions 
663*aca3beaaSApple OSS Distributions /*
664*aca3beaaSApple OSS Distributions  * Hook for domifdetach.
665*aca3beaaSApple OSS Distributions  */
666*aca3beaaSApple OSS Distributions void
igmp_domifdetach(struct ifnet * ifp)667*aca3beaaSApple OSS Distributions igmp_domifdetach(struct ifnet *ifp)
668*aca3beaaSApple OSS Distributions {
669*aca3beaaSApple OSS Distributions 	SLIST_HEAD(, in_multi) inm_dthead;
670*aca3beaaSApple OSS Distributions 
671*aca3beaaSApple OSS Distributions 	SLIST_INIT(&inm_dthead);
672*aca3beaaSApple OSS Distributions 
673*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: called for ifp 0x%llx(%s%d)\n", __func__,
674*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(ifp), ifp->if_name, ifp->if_unit));
675*aca3beaaSApple OSS Distributions 
676*aca3beaaSApple OSS Distributions 	IGMP_LOCK();
677*aca3beaaSApple OSS Distributions 	igi_delete(ifp, (struct igmp_inm_relhead *)&inm_dthead);
678*aca3beaaSApple OSS Distributions 	IGMP_UNLOCK();
679*aca3beaaSApple OSS Distributions 
680*aca3beaaSApple OSS Distributions 	/* Now that we're dropped all locks, release detached records */
681*aca3beaaSApple OSS Distributions 	IGMP_REMOVE_DETACHED_INM(&inm_dthead);
682*aca3beaaSApple OSS Distributions }
683*aca3beaaSApple OSS Distributions 
684*aca3beaaSApple OSS Distributions /*
685*aca3beaaSApple OSS Distributions  * Called at interface detach time.  Note that we only flush all deferred
686*aca3beaaSApple OSS Distributions  * responses and record releases; all remaining inm records and their source
687*aca3beaaSApple OSS Distributions  * entries related to this interface are left intact, in order to handle
688*aca3beaaSApple OSS Distributions  * the reattach case.
689*aca3beaaSApple OSS Distributions  */
690*aca3beaaSApple OSS Distributions static void
igi_delete(const struct ifnet * ifp,struct igmp_inm_relhead * inm_dthead)691*aca3beaaSApple OSS Distributions igi_delete(const struct ifnet *ifp, struct igmp_inm_relhead *inm_dthead)
692*aca3beaaSApple OSS Distributions {
693*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo *igi, *tigi;
694*aca3beaaSApple OSS Distributions 
695*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_HELD();
696*aca3beaaSApple OSS Distributions 
697*aca3beaaSApple OSS Distributions 	LIST_FOREACH_SAFE(igi, &igi_head, igi_link, tigi) {
698*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
699*aca3beaaSApple OSS Distributions 		if (igi->igi_ifp == ifp) {
700*aca3beaaSApple OSS Distributions 			/*
701*aca3beaaSApple OSS Distributions 			 * Free deferred General Query responses.
702*aca3beaaSApple OSS Distributions 			 */
703*aca3beaaSApple OSS Distributions 			IF_DRAIN(&igi->igi_gq);
704*aca3beaaSApple OSS Distributions 			IF_DRAIN(&igi->igi_v2q);
705*aca3beaaSApple OSS Distributions 			igmp_flush_relq(igi, inm_dthead);
706*aca3beaaSApple OSS Distributions 			VERIFY(SLIST_EMPTY(&igi->igi_relinmhead));
707*aca3beaaSApple OSS Distributions 			igi->igi_debug &= ~IFD_ATTACHED;
708*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
709*aca3beaaSApple OSS Distributions 
710*aca3beaaSApple OSS Distributions 			LIST_REMOVE(igi, igi_link);
711*aca3beaaSApple OSS Distributions 			IGI_REMREF(igi); /* release igi_head reference */
712*aca3beaaSApple OSS Distributions 			return;
713*aca3beaaSApple OSS Distributions 		}
714*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
715*aca3beaaSApple OSS Distributions 	}
716*aca3beaaSApple OSS Distributions 	panic("%s: igmp_ifinfo not found for ifp %p(%s)", __func__,
717*aca3beaaSApple OSS Distributions 	    ifp, ifp->if_xname);
718*aca3beaaSApple OSS Distributions }
719*aca3beaaSApple OSS Distributions 
720*aca3beaaSApple OSS Distributions __private_extern__ void
igmp_initsilent(struct ifnet * ifp,struct igmp_ifinfo * igi)721*aca3beaaSApple OSS Distributions igmp_initsilent(struct ifnet *ifp, struct igmp_ifinfo *igi)
722*aca3beaaSApple OSS Distributions {
723*aca3beaaSApple OSS Distributions 	ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_OWNED);
724*aca3beaaSApple OSS Distributions 
725*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_NOTHELD(igi);
726*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
727*aca3beaaSApple OSS Distributions 	if (!(ifp->if_flags & IFF_MULTICAST)) {
728*aca3beaaSApple OSS Distributions 		igi->igi_flags |= IGIF_SILENT;
729*aca3beaaSApple OSS Distributions 	} else {
730*aca3beaaSApple OSS Distributions 		igi->igi_flags &= ~IGIF_SILENT;
731*aca3beaaSApple OSS Distributions 	}
732*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
733*aca3beaaSApple OSS Distributions }
734*aca3beaaSApple OSS Distributions 
735*aca3beaaSApple OSS Distributions static void
igi_initvar(struct igmp_ifinfo * igi,struct ifnet * ifp,int reattach)736*aca3beaaSApple OSS Distributions igi_initvar(struct igmp_ifinfo *igi, struct ifnet *ifp, int reattach)
737*aca3beaaSApple OSS Distributions {
738*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
739*aca3beaaSApple OSS Distributions 
740*aca3beaaSApple OSS Distributions 	igi->igi_ifp = ifp;
741*aca3beaaSApple OSS Distributions 	igi->igi_version = igmp_default_version;
742*aca3beaaSApple OSS Distributions 	igi->igi_flags = 0;
743*aca3beaaSApple OSS Distributions 	igi->igi_rv = IGMP_RV_INIT;
744*aca3beaaSApple OSS Distributions 	igi->igi_qi = IGMP_QI_INIT;
745*aca3beaaSApple OSS Distributions 	igi->igi_qri = IGMP_QRI_INIT;
746*aca3beaaSApple OSS Distributions 	igi->igi_uri = IGMP_URI_INIT;
747*aca3beaaSApple OSS Distributions 
748*aca3beaaSApple OSS Distributions 	if (!reattach) {
749*aca3beaaSApple OSS Distributions 		SLIST_INIT(&igi->igi_relinmhead);
750*aca3beaaSApple OSS Distributions 	}
751*aca3beaaSApple OSS Distributions 
752*aca3beaaSApple OSS Distributions 	/*
753*aca3beaaSApple OSS Distributions 	 * Responses to general queries are subject to bounds.
754*aca3beaaSApple OSS Distributions 	 */
755*aca3beaaSApple OSS Distributions 	igi->igi_gq.ifq_maxlen =  IGMP_MAX_RESPONSE_PACKETS;
756*aca3beaaSApple OSS Distributions 	igi->igi_v2q.ifq_maxlen = IGMP_MAX_RESPONSE_PACKETS;
757*aca3beaaSApple OSS Distributions }
758*aca3beaaSApple OSS Distributions 
759*aca3beaaSApple OSS Distributions static struct igmp_ifinfo *
igi_alloc(zalloc_flags_t how)760*aca3beaaSApple OSS Distributions igi_alloc(zalloc_flags_t how)
761*aca3beaaSApple OSS Distributions {
762*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo *igi = zalloc_flags(igi_zone, how | Z_ZERO);
763*aca3beaaSApple OSS Distributions 	if (igi != NULL) {
764*aca3beaaSApple OSS Distributions 		lck_mtx_init(&igi->igi_lock, &igmp_mtx_grp, &igmp_mtx_attr);
765*aca3beaaSApple OSS Distributions 		igi->igi_debug |= IFD_ALLOC;
766*aca3beaaSApple OSS Distributions 	}
767*aca3beaaSApple OSS Distributions 	return igi;
768*aca3beaaSApple OSS Distributions }
769*aca3beaaSApple OSS Distributions 
770*aca3beaaSApple OSS Distributions static void
igi_free(struct igmp_ifinfo * igi)771*aca3beaaSApple OSS Distributions igi_free(struct igmp_ifinfo *igi)
772*aca3beaaSApple OSS Distributions {
773*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
774*aca3beaaSApple OSS Distributions 	if (igi->igi_debug & IFD_ATTACHED) {
775*aca3beaaSApple OSS Distributions 		panic("%s: attached igi=%p is being freed", __func__, igi);
776*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
777*aca3beaaSApple OSS Distributions 	} else if (igi->igi_ifp != NULL) {
778*aca3beaaSApple OSS Distributions 		panic("%s: ifp not NULL for igi=%p", __func__, igi);
779*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
780*aca3beaaSApple OSS Distributions 	} else if (!(igi->igi_debug & IFD_ALLOC)) {
781*aca3beaaSApple OSS Distributions 		panic("%s: igi %p cannot be freed", __func__, igi);
782*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
783*aca3beaaSApple OSS Distributions 	} else if (igi->igi_refcnt != 0) {
784*aca3beaaSApple OSS Distributions 		panic("%s: non-zero refcnt igi=%p", __func__, igi);
785*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
786*aca3beaaSApple OSS Distributions 	}
787*aca3beaaSApple OSS Distributions 	igi->igi_debug &= ~IFD_ALLOC;
788*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
789*aca3beaaSApple OSS Distributions 
790*aca3beaaSApple OSS Distributions 	lck_mtx_destroy(&igi->igi_lock, &igmp_mtx_grp);
791*aca3beaaSApple OSS Distributions 	zfree(igi_zone, igi);
792*aca3beaaSApple OSS Distributions }
793*aca3beaaSApple OSS Distributions 
794*aca3beaaSApple OSS Distributions void
igi_addref(struct igmp_ifinfo * igi,int locked)795*aca3beaaSApple OSS Distributions igi_addref(struct igmp_ifinfo *igi, int locked)
796*aca3beaaSApple OSS Distributions {
797*aca3beaaSApple OSS Distributions 	if (!locked) {
798*aca3beaaSApple OSS Distributions 		IGI_LOCK_SPIN(igi);
799*aca3beaaSApple OSS Distributions 	} else {
800*aca3beaaSApple OSS Distributions 		IGI_LOCK_ASSERT_HELD(igi);
801*aca3beaaSApple OSS Distributions 	}
802*aca3beaaSApple OSS Distributions 
803*aca3beaaSApple OSS Distributions 	if (++igi->igi_refcnt == 0) {
804*aca3beaaSApple OSS Distributions 		panic("%s: igi=%p wraparound refcnt", __func__, igi);
805*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
806*aca3beaaSApple OSS Distributions 	}
807*aca3beaaSApple OSS Distributions 	if (!locked) {
808*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
809*aca3beaaSApple OSS Distributions 	}
810*aca3beaaSApple OSS Distributions }
811*aca3beaaSApple OSS Distributions 
812*aca3beaaSApple OSS Distributions void
igi_remref(struct igmp_ifinfo * igi)813*aca3beaaSApple OSS Distributions igi_remref(struct igmp_ifinfo *igi)
814*aca3beaaSApple OSS Distributions {
815*aca3beaaSApple OSS Distributions 	SLIST_HEAD(, in_multi) inm_dthead;
816*aca3beaaSApple OSS Distributions 	struct ifnet *ifp;
817*aca3beaaSApple OSS Distributions 
818*aca3beaaSApple OSS Distributions 	IGI_LOCK_SPIN(igi);
819*aca3beaaSApple OSS Distributions 
820*aca3beaaSApple OSS Distributions 	if (igi->igi_refcnt == 0) {
821*aca3beaaSApple OSS Distributions 		panic("%s: igi=%p negative refcnt", __func__, igi);
822*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
823*aca3beaaSApple OSS Distributions 	}
824*aca3beaaSApple OSS Distributions 
825*aca3beaaSApple OSS Distributions 	--igi->igi_refcnt;
826*aca3beaaSApple OSS Distributions 	if (igi->igi_refcnt > 0) {
827*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
828*aca3beaaSApple OSS Distributions 		return;
829*aca3beaaSApple OSS Distributions 	}
830*aca3beaaSApple OSS Distributions 
831*aca3beaaSApple OSS Distributions 	ifp = igi->igi_ifp;
832*aca3beaaSApple OSS Distributions 	igi->igi_ifp = NULL;
833*aca3beaaSApple OSS Distributions 	IF_DRAIN(&igi->igi_gq);
834*aca3beaaSApple OSS Distributions 	IF_DRAIN(&igi->igi_v2q);
835*aca3beaaSApple OSS Distributions 	SLIST_INIT(&inm_dthead);
836*aca3beaaSApple OSS Distributions 	igmp_flush_relq(igi, (struct igmp_inm_relhead *)&inm_dthead);
837*aca3beaaSApple OSS Distributions 	VERIFY(SLIST_EMPTY(&igi->igi_relinmhead));
838*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
839*aca3beaaSApple OSS Distributions 
840*aca3beaaSApple OSS Distributions 	/* Now that we're dropped all locks, release detached records */
841*aca3beaaSApple OSS Distributions 	IGMP_REMOVE_DETACHED_INM(&inm_dthead);
842*aca3beaaSApple OSS Distributions 
843*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: freeing igmp_ifinfo for ifp 0x%llx(%s)\n",
844*aca3beaaSApple OSS Distributions 	    __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
845*aca3beaaSApple OSS Distributions 
846*aca3beaaSApple OSS Distributions 	igi_free(igi);
847*aca3beaaSApple OSS Distributions }
848*aca3beaaSApple OSS Distributions 
849*aca3beaaSApple OSS Distributions /*
850*aca3beaaSApple OSS Distributions  * Process a received IGMPv1 query.
851*aca3beaaSApple OSS Distributions  * Return non-zero if the message should be dropped.
852*aca3beaaSApple OSS Distributions  */
853*aca3beaaSApple OSS Distributions static int
igmp_input_v1_query(struct ifnet * ifp,const struct ip * ip,const struct igmp * igmp)854*aca3beaaSApple OSS Distributions igmp_input_v1_query(struct ifnet *ifp, const struct ip *ip,
855*aca3beaaSApple OSS Distributions     const struct igmp *igmp)
856*aca3beaaSApple OSS Distributions {
857*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo      *igi;
858*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
859*aca3beaaSApple OSS Distributions 	struct in_multistep     step;
860*aca3beaaSApple OSS Distributions 	struct igmp_tparams     itp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
861*aca3beaaSApple OSS Distributions 
862*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_NOTHELD();
863*aca3beaaSApple OSS Distributions 
864*aca3beaaSApple OSS Distributions 	/*
865*aca3beaaSApple OSS Distributions 	 * IGMPv1 Host Membership Queries SHOULD always be addressed to
866*aca3beaaSApple OSS Distributions 	 * 224.0.0.1. They are always treated as General Queries.
867*aca3beaaSApple OSS Distributions 	 * igmp_group is always ignored. Do not drop it as a userland
868*aca3beaaSApple OSS Distributions 	 * daemon may wish to see it.
869*aca3beaaSApple OSS Distributions 	 */
870*aca3beaaSApple OSS Distributions 	if (!in_allhosts(ip->ip_dst) || !in_nullhost(igmp->igmp_group)) {
871*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_badqueries);
872*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_badqueries);
873*aca3beaaSApple OSS Distributions 		goto done;
874*aca3beaaSApple OSS Distributions 	}
875*aca3beaaSApple OSS Distributions 	IGMPSTAT_INC(igps_rcv_gen_queries);
876*aca3beaaSApple OSS Distributions 
877*aca3beaaSApple OSS Distributions 	igi = IGMP_IFINFO(ifp);
878*aca3beaaSApple OSS Distributions 	VERIFY(igi != NULL);
879*aca3beaaSApple OSS Distributions 
880*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
881*aca3beaaSApple OSS Distributions 	if (igi->igi_flags & IGIF_LOOPBACK) {
882*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ignore v1 query on IGIF_LOOPBACK "
883*aca3beaaSApple OSS Distributions 		    "ifp 0x%llx(%s)\n", __func__,
884*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
885*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
886*aca3beaaSApple OSS Distributions 		goto done;
887*aca3beaaSApple OSS Distributions 	}
888*aca3beaaSApple OSS Distributions 	/*
889*aca3beaaSApple OSS Distributions 	 * Switch to IGMPv1 host compatibility mode.
890*aca3beaaSApple OSS Distributions 	 */
891*aca3beaaSApple OSS Distributions 	itp.qpt = igmp_set_version(igi, IGMP_VERSION_1);
892*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
893*aca3beaaSApple OSS Distributions 
894*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: process v1 query on ifp 0x%llx(%s)\n", __func__,
895*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
896*aca3beaaSApple OSS Distributions 
897*aca3beaaSApple OSS Distributions 	/*
898*aca3beaaSApple OSS Distributions 	 * Start the timers in all of our group records
899*aca3beaaSApple OSS Distributions 	 * for the interface on which the query arrived,
900*aca3beaaSApple OSS Distributions 	 * except those which are already running.
901*aca3beaaSApple OSS Distributions 	 */
902*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
903*aca3beaaSApple OSS Distributions 	IN_FIRST_MULTI(step, inm);
904*aca3beaaSApple OSS Distributions 	while (inm != NULL) {
905*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
906*aca3beaaSApple OSS Distributions 		if (inm->inm_ifp != ifp || inm->inm_timer != 0) {
907*aca3beaaSApple OSS Distributions 			goto next;
908*aca3beaaSApple OSS Distributions 		}
909*aca3beaaSApple OSS Distributions 
910*aca3beaaSApple OSS Distributions 		switch (inm->inm_state) {
911*aca3beaaSApple OSS Distributions 		case IGMP_NOT_MEMBER:
912*aca3beaaSApple OSS Distributions 		case IGMP_SILENT_MEMBER:
913*aca3beaaSApple OSS Distributions 			break;
914*aca3beaaSApple OSS Distributions 		case IGMP_G_QUERY_PENDING_MEMBER:
915*aca3beaaSApple OSS Distributions 		case IGMP_SG_QUERY_PENDING_MEMBER:
916*aca3beaaSApple OSS Distributions 		case IGMP_REPORTING_MEMBER:
917*aca3beaaSApple OSS Distributions 		case IGMP_IDLE_MEMBER:
918*aca3beaaSApple OSS Distributions 		case IGMP_LAZY_MEMBER:
919*aca3beaaSApple OSS Distributions 		case IGMP_SLEEPING_MEMBER:
920*aca3beaaSApple OSS Distributions 		case IGMP_AWAKENING_MEMBER:
921*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_REPORTING_MEMBER;
922*aca3beaaSApple OSS Distributions 			inm->inm_timer = IGMP_RANDOM_DELAY(IGMP_V1V2_MAX_RI);
923*aca3beaaSApple OSS Distributions 			itp.cst = 1;
924*aca3beaaSApple OSS Distributions 			break;
925*aca3beaaSApple OSS Distributions 		case IGMP_LEAVING_MEMBER:
926*aca3beaaSApple OSS Distributions 			break;
927*aca3beaaSApple OSS Distributions 		}
928*aca3beaaSApple OSS Distributions next:
929*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
930*aca3beaaSApple OSS Distributions 		IN_NEXT_MULTI(step, inm);
931*aca3beaaSApple OSS Distributions 	}
932*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
933*aca3beaaSApple OSS Distributions done:
934*aca3beaaSApple OSS Distributions 	igmp_set_timeout(&itp);
935*aca3beaaSApple OSS Distributions 
936*aca3beaaSApple OSS Distributions 	return 0;
937*aca3beaaSApple OSS Distributions }
938*aca3beaaSApple OSS Distributions 
939*aca3beaaSApple OSS Distributions /*
940*aca3beaaSApple OSS Distributions  * Process a received IGMPv2 general or group-specific query.
941*aca3beaaSApple OSS Distributions  */
942*aca3beaaSApple OSS Distributions static int
igmp_input_v2_query(struct ifnet * ifp,const struct ip * ip,const struct igmp * igmp)943*aca3beaaSApple OSS Distributions igmp_input_v2_query(struct ifnet *ifp, const struct ip *ip,
944*aca3beaaSApple OSS Distributions     const struct igmp *igmp)
945*aca3beaaSApple OSS Distributions {
946*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo      *igi;
947*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
948*aca3beaaSApple OSS Distributions 	int                      is_general_query;
949*aca3beaaSApple OSS Distributions 	uint16_t                 timer;
950*aca3beaaSApple OSS Distributions 	struct igmp_tparams      itp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
951*aca3beaaSApple OSS Distributions 
952*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_NOTHELD();
953*aca3beaaSApple OSS Distributions 
954*aca3beaaSApple OSS Distributions 	is_general_query = 0;
955*aca3beaaSApple OSS Distributions 
956*aca3beaaSApple OSS Distributions 	/*
957*aca3beaaSApple OSS Distributions 	 * Validate address fields upfront.
958*aca3beaaSApple OSS Distributions 	 */
959*aca3beaaSApple OSS Distributions 	if (in_nullhost(igmp->igmp_group)) {
960*aca3beaaSApple OSS Distributions 		/*
961*aca3beaaSApple OSS Distributions 		 * IGMPv2 General Query.
962*aca3beaaSApple OSS Distributions 		 * If this was not sent to the all-hosts group, ignore it.
963*aca3beaaSApple OSS Distributions 		 */
964*aca3beaaSApple OSS Distributions 		if (!in_allhosts(ip->ip_dst)) {
965*aca3beaaSApple OSS Distributions 			goto done;
966*aca3beaaSApple OSS Distributions 		}
967*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_gen_queries);
968*aca3beaaSApple OSS Distributions 		is_general_query = 1;
969*aca3beaaSApple OSS Distributions 	} else {
970*aca3beaaSApple OSS Distributions 		/* IGMPv2 Group-Specific Query. */
971*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_group_queries);
972*aca3beaaSApple OSS Distributions 	}
973*aca3beaaSApple OSS Distributions 
974*aca3beaaSApple OSS Distributions 	igi = IGMP_IFINFO(ifp);
975*aca3beaaSApple OSS Distributions 	VERIFY(igi != NULL);
976*aca3beaaSApple OSS Distributions 
977*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
978*aca3beaaSApple OSS Distributions 	if (igi->igi_flags & IGIF_LOOPBACK) {
979*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ignore v2 query on IGIF_LOOPBACK "
980*aca3beaaSApple OSS Distributions 		    "ifp 0x%llx(%s)\n", __func__,
981*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
982*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
983*aca3beaaSApple OSS Distributions 		goto done;
984*aca3beaaSApple OSS Distributions 	}
985*aca3beaaSApple OSS Distributions 	/*
986*aca3beaaSApple OSS Distributions 	 * Ignore v2 query if in v1 Compatibility Mode.
987*aca3beaaSApple OSS Distributions 	 */
988*aca3beaaSApple OSS Distributions 	if (igi->igi_version == IGMP_VERSION_1) {
989*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
990*aca3beaaSApple OSS Distributions 		goto done;
991*aca3beaaSApple OSS Distributions 	}
992*aca3beaaSApple OSS Distributions 	itp.qpt = igmp_set_version(igi, IGMP_VERSION_2);
993*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
994*aca3beaaSApple OSS Distributions 
995*aca3beaaSApple OSS Distributions 	timer = igmp->igmp_code / IGMP_TIMER_SCALE;
996*aca3beaaSApple OSS Distributions 	if (timer == 0) {
997*aca3beaaSApple OSS Distributions 		timer = 1;
998*aca3beaaSApple OSS Distributions 	}
999*aca3beaaSApple OSS Distributions 
1000*aca3beaaSApple OSS Distributions 	if (is_general_query) {
1001*aca3beaaSApple OSS Distributions 		struct in_multistep step;
1002*aca3beaaSApple OSS Distributions 
1003*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: process v2 general query on ifp 0x%llx(%s)\n",
1004*aca3beaaSApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1005*aca3beaaSApple OSS Distributions 		/*
1006*aca3beaaSApple OSS Distributions 		 * For each reporting group joined on this
1007*aca3beaaSApple OSS Distributions 		 * interface, kick the report timer.
1008*aca3beaaSApple OSS Distributions 		 */
1009*aca3beaaSApple OSS Distributions 		in_multihead_lock_shared();
1010*aca3beaaSApple OSS Distributions 		IN_FIRST_MULTI(step, inm);
1011*aca3beaaSApple OSS Distributions 		while (inm != NULL) {
1012*aca3beaaSApple OSS Distributions 			INM_LOCK(inm);
1013*aca3beaaSApple OSS Distributions 			if (inm->inm_ifp == ifp) {
1014*aca3beaaSApple OSS Distributions 				itp.cst += igmp_v2_update_group(inm, timer);
1015*aca3beaaSApple OSS Distributions 			}
1016*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
1017*aca3beaaSApple OSS Distributions 			IN_NEXT_MULTI(step, inm);
1018*aca3beaaSApple OSS Distributions 		}
1019*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
1020*aca3beaaSApple OSS Distributions 	} else {
1021*aca3beaaSApple OSS Distributions 		/*
1022*aca3beaaSApple OSS Distributions 		 * Group-specific IGMPv2 query, we need only
1023*aca3beaaSApple OSS Distributions 		 * look up the single group to process it.
1024*aca3beaaSApple OSS Distributions 		 */
1025*aca3beaaSApple OSS Distributions 		in_multihead_lock_shared();
1026*aca3beaaSApple OSS Distributions 		IN_LOOKUP_MULTI(&igmp->igmp_group, ifp, inm);
1027*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
1028*aca3beaaSApple OSS Distributions 		if (inm != NULL) {
1029*aca3beaaSApple OSS Distributions 			INM_LOCK(inm);
1030*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(igmp->igmp_group,
1031*aca3beaaSApple OSS Distributions 			    ("process v2 query %s on ifp 0x%llx(%s)\n",
1032*aca3beaaSApple OSS Distributions 			    _igmp_inet_buf,
1033*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1034*aca3beaaSApple OSS Distributions 			itp.cst = igmp_v2_update_group(inm, timer);
1035*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
1036*aca3beaaSApple OSS Distributions 			INM_REMREF(inm); /* from IN_LOOKUP_MULTI */
1037*aca3beaaSApple OSS Distributions 		}
1038*aca3beaaSApple OSS Distributions 	}
1039*aca3beaaSApple OSS Distributions done:
1040*aca3beaaSApple OSS Distributions 	igmp_set_timeout(&itp);
1041*aca3beaaSApple OSS Distributions 
1042*aca3beaaSApple OSS Distributions 	return 0;
1043*aca3beaaSApple OSS Distributions }
1044*aca3beaaSApple OSS Distributions 
1045*aca3beaaSApple OSS Distributions /*
1046*aca3beaaSApple OSS Distributions  * Update the report timer on a group in response to an IGMPv2 query.
1047*aca3beaaSApple OSS Distributions  *
1048*aca3beaaSApple OSS Distributions  * If we are becoming the reporting member for this group, start the timer.
1049*aca3beaaSApple OSS Distributions  * If we already are the reporting member for this group, and timer is
1050*aca3beaaSApple OSS Distributions  * below the threshold, reset it.
1051*aca3beaaSApple OSS Distributions  *
1052*aca3beaaSApple OSS Distributions  * We may be updating the group for the first time since we switched
1053*aca3beaaSApple OSS Distributions  * to IGMPv3. If we are, then we must clear any recorded source lists,
1054*aca3beaaSApple OSS Distributions  * and transition to REPORTING state; the group timer is overloaded
1055*aca3beaaSApple OSS Distributions  * for group and group-source query responses.
1056*aca3beaaSApple OSS Distributions  *
1057*aca3beaaSApple OSS Distributions  * Unlike IGMPv3, the delay per group should be jittered
1058*aca3beaaSApple OSS Distributions  * to avoid bursts of IGMPv2 reports.
1059*aca3beaaSApple OSS Distributions  */
1060*aca3beaaSApple OSS Distributions static uint32_t
igmp_v2_update_group(struct in_multi * inm,const int timer)1061*aca3beaaSApple OSS Distributions igmp_v2_update_group(struct in_multi *inm, const int timer)
1062*aca3beaaSApple OSS Distributions {
1063*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(inm->inm_addr, ("%s: %s/%s timer=%d\n",
1064*aca3beaaSApple OSS Distributions 	    __func__, _igmp_inet_buf, if_name(inm->inm_ifp),
1065*aca3beaaSApple OSS Distributions 	    timer));
1066*aca3beaaSApple OSS Distributions 
1067*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
1068*aca3beaaSApple OSS Distributions 
1069*aca3beaaSApple OSS Distributions 	switch (inm->inm_state) {
1070*aca3beaaSApple OSS Distributions 	case IGMP_NOT_MEMBER:
1071*aca3beaaSApple OSS Distributions 	case IGMP_SILENT_MEMBER:
1072*aca3beaaSApple OSS Distributions 		break;
1073*aca3beaaSApple OSS Distributions 	case IGMP_REPORTING_MEMBER:
1074*aca3beaaSApple OSS Distributions 		if (inm->inm_timer != 0 &&
1075*aca3beaaSApple OSS Distributions 		    inm->inm_timer <= timer) {
1076*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: REPORTING and timer running, "
1077*aca3beaaSApple OSS Distributions 			    "skipping.\n", __func__));
1078*aca3beaaSApple OSS Distributions 			break;
1079*aca3beaaSApple OSS Distributions 		}
1080*aca3beaaSApple OSS Distributions 		OS_FALLTHROUGH;
1081*aca3beaaSApple OSS Distributions 	case IGMP_SG_QUERY_PENDING_MEMBER:
1082*aca3beaaSApple OSS Distributions 	case IGMP_G_QUERY_PENDING_MEMBER:
1083*aca3beaaSApple OSS Distributions 	case IGMP_IDLE_MEMBER:
1084*aca3beaaSApple OSS Distributions 	case IGMP_LAZY_MEMBER:
1085*aca3beaaSApple OSS Distributions 	case IGMP_AWAKENING_MEMBER:
1086*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ->REPORTING\n", __func__));
1087*aca3beaaSApple OSS Distributions 		inm->inm_state = IGMP_REPORTING_MEMBER;
1088*aca3beaaSApple OSS Distributions 		inm->inm_timer = IGMP_RANDOM_DELAY(timer);
1089*aca3beaaSApple OSS Distributions 		break;
1090*aca3beaaSApple OSS Distributions 	case IGMP_SLEEPING_MEMBER:
1091*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ->AWAKENING\n", __func__));
1092*aca3beaaSApple OSS Distributions 		inm->inm_state = IGMP_AWAKENING_MEMBER;
1093*aca3beaaSApple OSS Distributions 		break;
1094*aca3beaaSApple OSS Distributions 	case IGMP_LEAVING_MEMBER:
1095*aca3beaaSApple OSS Distributions 		break;
1096*aca3beaaSApple OSS Distributions 	}
1097*aca3beaaSApple OSS Distributions 
1098*aca3beaaSApple OSS Distributions 	return inm->inm_timer;
1099*aca3beaaSApple OSS Distributions }
1100*aca3beaaSApple OSS Distributions 
1101*aca3beaaSApple OSS Distributions /*
1102*aca3beaaSApple OSS Distributions  * Process a received IGMPv3 general, group-specific or
1103*aca3beaaSApple OSS Distributions  * group-and-source-specific query.
1104*aca3beaaSApple OSS Distributions  * Assumes m has already been pulled up to the full IGMP message length.
1105*aca3beaaSApple OSS Distributions  * Return 0 if successful, otherwise an appropriate error code is returned.
1106*aca3beaaSApple OSS Distributions  */
1107*aca3beaaSApple OSS Distributions static int
igmp_input_v3_query(struct ifnet * ifp,const struct ip * ip,struct igmpv3 * igmpv3)1108*aca3beaaSApple OSS Distributions igmp_input_v3_query(struct ifnet *ifp, const struct ip *ip,
1109*aca3beaaSApple OSS Distributions     /*const*/ struct igmpv3 *igmpv3)
1110*aca3beaaSApple OSS Distributions {
1111*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo      *igi;
1112*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
1113*aca3beaaSApple OSS Distributions 	int                      is_general_query;
1114*aca3beaaSApple OSS Distributions 	uint32_t                 maxresp, nsrc, qqi;
1115*aca3beaaSApple OSS Distributions 	uint32_t                 timer;
1116*aca3beaaSApple OSS Distributions 	uint8_t                  qrv;
1117*aca3beaaSApple OSS Distributions 	struct igmp_tparams      itp = { .qpt = 0, .it = 0, .cst = 0, .sct = 0 };
1118*aca3beaaSApple OSS Distributions 
1119*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_NOTHELD();
1120*aca3beaaSApple OSS Distributions 
1121*aca3beaaSApple OSS Distributions 	is_general_query = 0;
1122*aca3beaaSApple OSS Distributions 
1123*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: process v3 query on ifp 0x%llx(%s)\n", __func__,
1124*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1125*aca3beaaSApple OSS Distributions 
1126*aca3beaaSApple OSS Distributions 	maxresp = igmpv3->igmp_code;    /* in 1/10ths of a second */
1127*aca3beaaSApple OSS Distributions 	if (maxresp >= 128) {
1128*aca3beaaSApple OSS Distributions 		maxresp = IGMP_MANT(igmpv3->igmp_code) <<
1129*aca3beaaSApple OSS Distributions 		    (IGMP_EXP(igmpv3->igmp_code) + 3);
1130*aca3beaaSApple OSS Distributions 	}
1131*aca3beaaSApple OSS Distributions 
1132*aca3beaaSApple OSS Distributions 	/*
1133*aca3beaaSApple OSS Distributions 	 * Robustness must never be less than 2 for on-wire IGMPv3.
1134*aca3beaaSApple OSS Distributions 	 * FUTURE: Check if ifp has IGIF_LOOPBACK set, as we will make
1135*aca3beaaSApple OSS Distributions 	 * an exception for interfaces whose IGMPv3 state changes
1136*aca3beaaSApple OSS Distributions 	 * are redirected to loopback (e.g. MANET).
1137*aca3beaaSApple OSS Distributions 	 */
1138*aca3beaaSApple OSS Distributions 	qrv = IGMP_QRV(igmpv3->igmp_misc);
1139*aca3beaaSApple OSS Distributions 	if (qrv < 2) {
1140*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: clamping qrv %d to %d\n", __func__,
1141*aca3beaaSApple OSS Distributions 		    qrv, IGMP_RV_INIT));
1142*aca3beaaSApple OSS Distributions 		qrv = IGMP_RV_INIT;
1143*aca3beaaSApple OSS Distributions 	}
1144*aca3beaaSApple OSS Distributions 
1145*aca3beaaSApple OSS Distributions 	qqi = igmpv3->igmp_qqi;
1146*aca3beaaSApple OSS Distributions 	if (qqi >= 128) {
1147*aca3beaaSApple OSS Distributions 		qqi = IGMP_MANT(igmpv3->igmp_qqi) <<
1148*aca3beaaSApple OSS Distributions 		    (IGMP_EXP(igmpv3->igmp_qqi) + 3);
1149*aca3beaaSApple OSS Distributions 	}
1150*aca3beaaSApple OSS Distributions 
1151*aca3beaaSApple OSS Distributions 	timer = maxresp / IGMP_TIMER_SCALE;
1152*aca3beaaSApple OSS Distributions 	if (timer == 0) {
1153*aca3beaaSApple OSS Distributions 		timer = 1;
1154*aca3beaaSApple OSS Distributions 	}
1155*aca3beaaSApple OSS Distributions 
1156*aca3beaaSApple OSS Distributions 	nsrc = ntohs(igmpv3->igmp_numsrc);
1157*aca3beaaSApple OSS Distributions 
1158*aca3beaaSApple OSS Distributions 	/*
1159*aca3beaaSApple OSS Distributions 	 * Validate address fields and versions upfront before
1160*aca3beaaSApple OSS Distributions 	 * accepting v3 query.
1161*aca3beaaSApple OSS Distributions 	 */
1162*aca3beaaSApple OSS Distributions 	if (in_nullhost(igmpv3->igmp_group)) {
1163*aca3beaaSApple OSS Distributions 		/*
1164*aca3beaaSApple OSS Distributions 		 * IGMPv3 General Query.
1165*aca3beaaSApple OSS Distributions 		 *
1166*aca3beaaSApple OSS Distributions 		 * General Queries SHOULD be directed to 224.0.0.1.
1167*aca3beaaSApple OSS Distributions 		 * A general query with a source list has undefined
1168*aca3beaaSApple OSS Distributions 		 * behaviour; discard it.
1169*aca3beaaSApple OSS Distributions 		 */
1170*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_gen_queries);
1171*aca3beaaSApple OSS Distributions 		if (!in_allhosts(ip->ip_dst) || nsrc > 0) {
1172*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_badqueries);
1173*aca3beaaSApple OSS Distributions 			OIGMPSTAT_INC(igps_rcv_badqueries);
1174*aca3beaaSApple OSS Distributions 			goto done;
1175*aca3beaaSApple OSS Distributions 		}
1176*aca3beaaSApple OSS Distributions 		is_general_query = 1;
1177*aca3beaaSApple OSS Distributions 	} else {
1178*aca3beaaSApple OSS Distributions 		/* Group or group-source specific query. */
1179*aca3beaaSApple OSS Distributions 		if (nsrc == 0) {
1180*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_group_queries);
1181*aca3beaaSApple OSS Distributions 		} else {
1182*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_gsr_queries);
1183*aca3beaaSApple OSS Distributions 		}
1184*aca3beaaSApple OSS Distributions 	}
1185*aca3beaaSApple OSS Distributions 
1186*aca3beaaSApple OSS Distributions 	igi = IGMP_IFINFO(ifp);
1187*aca3beaaSApple OSS Distributions 	VERIFY(igi != NULL);
1188*aca3beaaSApple OSS Distributions 
1189*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
1190*aca3beaaSApple OSS Distributions 	if (igi->igi_flags & IGIF_LOOPBACK) {
1191*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ignore v3 query on IGIF_LOOPBACK "
1192*aca3beaaSApple OSS Distributions 		    "ifp 0x%llx(%s)\n", __func__,
1193*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1194*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
1195*aca3beaaSApple OSS Distributions 		goto done;
1196*aca3beaaSApple OSS Distributions 	}
1197*aca3beaaSApple OSS Distributions 
1198*aca3beaaSApple OSS Distributions 	/*
1199*aca3beaaSApple OSS Distributions 	 * Discard the v3 query if we're in Compatibility Mode.
1200*aca3beaaSApple OSS Distributions 	 * The RFC is not obviously worded that hosts need to stay in
1201*aca3beaaSApple OSS Distributions 	 * compatibility mode until the Old Version Querier Present
1202*aca3beaaSApple OSS Distributions 	 * timer expires.
1203*aca3beaaSApple OSS Distributions 	 */
1204*aca3beaaSApple OSS Distributions 	if (igi->igi_version != IGMP_VERSION_3) {
1205*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ignore v3 query in v%d mode on "
1206*aca3beaaSApple OSS Distributions 		    "ifp 0x%llx(%s)\n", __func__, igi->igi_version,
1207*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1208*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
1209*aca3beaaSApple OSS Distributions 		goto done;
1210*aca3beaaSApple OSS Distributions 	}
1211*aca3beaaSApple OSS Distributions 
1212*aca3beaaSApple OSS Distributions 	itp.qpt = igmp_set_version(igi, IGMP_VERSION_3);
1213*aca3beaaSApple OSS Distributions 	igi->igi_rv = qrv;
1214*aca3beaaSApple OSS Distributions 	igi->igi_qi = qqi;
1215*aca3beaaSApple OSS Distributions 	igi->igi_qri = MAX(timer, IGMP_QRI_MIN);
1216*aca3beaaSApple OSS Distributions 
1217*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: qrv %d qi %d qri %d\n", __func__, igi->igi_rv,
1218*aca3beaaSApple OSS Distributions 	    igi->igi_qi, igi->igi_qri));
1219*aca3beaaSApple OSS Distributions 
1220*aca3beaaSApple OSS Distributions 	if (is_general_query) {
1221*aca3beaaSApple OSS Distributions 		/*
1222*aca3beaaSApple OSS Distributions 		 * Schedule a current-state report on this ifp for
1223*aca3beaaSApple OSS Distributions 		 * all groups, possibly containing source lists.
1224*aca3beaaSApple OSS Distributions 		 * If there is a pending General Query response
1225*aca3beaaSApple OSS Distributions 		 * scheduled earlier than the selected delay, do
1226*aca3beaaSApple OSS Distributions 		 * not schedule any other reports.
1227*aca3beaaSApple OSS Distributions 		 * Otherwise, reset the interface timer.
1228*aca3beaaSApple OSS Distributions 		 */
1229*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: process v3 general query on ifp 0x%llx(%s)\n",
1230*aca3beaaSApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1231*aca3beaaSApple OSS Distributions 		if (igi->igi_v3_timer == 0 || igi->igi_v3_timer >= timer) {
1232*aca3beaaSApple OSS Distributions 			itp.it = igi->igi_v3_timer = IGMP_RANDOM_DELAY(timer);
1233*aca3beaaSApple OSS Distributions 		}
1234*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
1235*aca3beaaSApple OSS Distributions 	} else {
1236*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
1237*aca3beaaSApple OSS Distributions 		/*
1238*aca3beaaSApple OSS Distributions 		 * Group-source-specific queries are throttled on
1239*aca3beaaSApple OSS Distributions 		 * a per-group basis to defeat denial-of-service attempts.
1240*aca3beaaSApple OSS Distributions 		 * Queries for groups we are not a member of on this
1241*aca3beaaSApple OSS Distributions 		 * link are simply ignored.
1242*aca3beaaSApple OSS Distributions 		 */
1243*aca3beaaSApple OSS Distributions 		in_multihead_lock_shared();
1244*aca3beaaSApple OSS Distributions 		IN_LOOKUP_MULTI(&igmpv3->igmp_group, ifp, inm);
1245*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
1246*aca3beaaSApple OSS Distributions 		if (inm == NULL) {
1247*aca3beaaSApple OSS Distributions 			goto done;
1248*aca3beaaSApple OSS Distributions 		}
1249*aca3beaaSApple OSS Distributions 
1250*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
1251*aca3beaaSApple OSS Distributions 		if (nsrc > 0) {
1252*aca3beaaSApple OSS Distributions 			if (!ratecheck(&inm->inm_lastgsrtv,
1253*aca3beaaSApple OSS Distributions 			    &igmp_gsrdelay)) {
1254*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: GS query throttled.\n",
1255*aca3beaaSApple OSS Distributions 				    __func__));
1256*aca3beaaSApple OSS Distributions 				IGMPSTAT_INC(igps_drop_gsr_queries);
1257*aca3beaaSApple OSS Distributions 				INM_UNLOCK(inm);
1258*aca3beaaSApple OSS Distributions 				INM_REMREF(inm); /* from IN_LOOKUP_MULTI */
1259*aca3beaaSApple OSS Distributions 				goto done;
1260*aca3beaaSApple OSS Distributions 			}
1261*aca3beaaSApple OSS Distributions 		}
1262*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(igmpv3->igmp_group,
1263*aca3beaaSApple OSS Distributions 		    ("process v3 %s query on ifp 0x%llx(%s)\n", _igmp_inet_buf,
1264*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1265*aca3beaaSApple OSS Distributions 		/*
1266*aca3beaaSApple OSS Distributions 		 * If there is a pending General Query response
1267*aca3beaaSApple OSS Distributions 		 * scheduled sooner than the selected delay, no
1268*aca3beaaSApple OSS Distributions 		 * further report need be scheduled.
1269*aca3beaaSApple OSS Distributions 		 * Otherwise, prepare to respond to the
1270*aca3beaaSApple OSS Distributions 		 * group-specific or group-and-source query.
1271*aca3beaaSApple OSS Distributions 		 */
1272*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
1273*aca3beaaSApple OSS Distributions 		itp.it = igi->igi_v3_timer;
1274*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
1275*aca3beaaSApple OSS Distributions 		if (itp.it == 0 || itp.it >= timer) {
1276*aca3beaaSApple OSS Distributions 			(void) igmp_input_v3_group_query(inm, timer, igmpv3);
1277*aca3beaaSApple OSS Distributions 			itp.cst = inm->inm_timer;
1278*aca3beaaSApple OSS Distributions 		}
1279*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
1280*aca3beaaSApple OSS Distributions 		INM_REMREF(inm); /* from IN_LOOKUP_MULTI */
1281*aca3beaaSApple OSS Distributions 	}
1282*aca3beaaSApple OSS Distributions done:
1283*aca3beaaSApple OSS Distributions 	if (itp.it > 0) {
1284*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: v3 general query response scheduled in "
1285*aca3beaaSApple OSS Distributions 		    "T+%d seconds on ifp 0x%llx(%s)\n", __func__, itp.it,
1286*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1287*aca3beaaSApple OSS Distributions 	}
1288*aca3beaaSApple OSS Distributions 	igmp_set_timeout(&itp);
1289*aca3beaaSApple OSS Distributions 
1290*aca3beaaSApple OSS Distributions 	return 0;
1291*aca3beaaSApple OSS Distributions }
1292*aca3beaaSApple OSS Distributions 
1293*aca3beaaSApple OSS Distributions /*
1294*aca3beaaSApple OSS Distributions  * Process a recieved IGMPv3 group-specific or group-and-source-specific
1295*aca3beaaSApple OSS Distributions  * query.
1296*aca3beaaSApple OSS Distributions  * Return <0 if any error occured. Currently this is ignored.
1297*aca3beaaSApple OSS Distributions  */
1298*aca3beaaSApple OSS Distributions static int
igmp_input_v3_group_query(struct in_multi * inm,int timer,struct igmpv3 * igmpv3)1299*aca3beaaSApple OSS Distributions igmp_input_v3_group_query(struct in_multi *inm,
1300*aca3beaaSApple OSS Distributions     int timer, /*const*/ struct igmpv3 *igmpv3)
1301*aca3beaaSApple OSS Distributions {
1302*aca3beaaSApple OSS Distributions 	int                      retval;
1303*aca3beaaSApple OSS Distributions 	uint16_t                 nsrc;
1304*aca3beaaSApple OSS Distributions 
1305*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
1306*aca3beaaSApple OSS Distributions 
1307*aca3beaaSApple OSS Distributions 	retval = 0;
1308*aca3beaaSApple OSS Distributions 
1309*aca3beaaSApple OSS Distributions 	switch (inm->inm_state) {
1310*aca3beaaSApple OSS Distributions 	case IGMP_NOT_MEMBER:
1311*aca3beaaSApple OSS Distributions 	case IGMP_SILENT_MEMBER:
1312*aca3beaaSApple OSS Distributions 	case IGMP_SLEEPING_MEMBER:
1313*aca3beaaSApple OSS Distributions 	case IGMP_LAZY_MEMBER:
1314*aca3beaaSApple OSS Distributions 	case IGMP_AWAKENING_MEMBER:
1315*aca3beaaSApple OSS Distributions 	case IGMP_IDLE_MEMBER:
1316*aca3beaaSApple OSS Distributions 	case IGMP_LEAVING_MEMBER:
1317*aca3beaaSApple OSS Distributions 		return retval;
1318*aca3beaaSApple OSS Distributions 	case IGMP_REPORTING_MEMBER:
1319*aca3beaaSApple OSS Distributions 	case IGMP_G_QUERY_PENDING_MEMBER:
1320*aca3beaaSApple OSS Distributions 	case IGMP_SG_QUERY_PENDING_MEMBER:
1321*aca3beaaSApple OSS Distributions 		break;
1322*aca3beaaSApple OSS Distributions 	}
1323*aca3beaaSApple OSS Distributions 
1324*aca3beaaSApple OSS Distributions 	nsrc = ntohs(igmpv3->igmp_numsrc);
1325*aca3beaaSApple OSS Distributions 
1326*aca3beaaSApple OSS Distributions 	/*
1327*aca3beaaSApple OSS Distributions 	 * Deal with group-specific queries upfront.
1328*aca3beaaSApple OSS Distributions 	 * If any group query is already pending, purge any recorded
1329*aca3beaaSApple OSS Distributions 	 * source-list state if it exists, and schedule a query response
1330*aca3beaaSApple OSS Distributions 	 * for this group-specific query.
1331*aca3beaaSApple OSS Distributions 	 */
1332*aca3beaaSApple OSS Distributions 	if (nsrc == 0) {
1333*aca3beaaSApple OSS Distributions 		if (inm->inm_state == IGMP_G_QUERY_PENDING_MEMBER ||
1334*aca3beaaSApple OSS Distributions 		    inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER) {
1335*aca3beaaSApple OSS Distributions 			inm_clear_recorded(inm);
1336*aca3beaaSApple OSS Distributions 			timer = min(inm->inm_timer, timer);
1337*aca3beaaSApple OSS Distributions 		}
1338*aca3beaaSApple OSS Distributions 		inm->inm_state = IGMP_G_QUERY_PENDING_MEMBER;
1339*aca3beaaSApple OSS Distributions 		inm->inm_timer = IGMP_RANDOM_DELAY(timer);
1340*aca3beaaSApple OSS Distributions 		return retval;
1341*aca3beaaSApple OSS Distributions 	}
1342*aca3beaaSApple OSS Distributions 
1343*aca3beaaSApple OSS Distributions 	/*
1344*aca3beaaSApple OSS Distributions 	 * Deal with the case where a group-and-source-specific query has
1345*aca3beaaSApple OSS Distributions 	 * been received but a group-specific query is already pending.
1346*aca3beaaSApple OSS Distributions 	 */
1347*aca3beaaSApple OSS Distributions 	if (inm->inm_state == IGMP_G_QUERY_PENDING_MEMBER) {
1348*aca3beaaSApple OSS Distributions 		timer = min(inm->inm_timer, timer);
1349*aca3beaaSApple OSS Distributions 		inm->inm_timer = IGMP_RANDOM_DELAY(timer);
1350*aca3beaaSApple OSS Distributions 		return retval;
1351*aca3beaaSApple OSS Distributions 	}
1352*aca3beaaSApple OSS Distributions 
1353*aca3beaaSApple OSS Distributions 	/*
1354*aca3beaaSApple OSS Distributions 	 * Finally, deal with the case where a group-and-source-specific
1355*aca3beaaSApple OSS Distributions 	 * query has been received, where a response to a previous g-s-r
1356*aca3beaaSApple OSS Distributions 	 * query exists, or none exists.
1357*aca3beaaSApple OSS Distributions 	 * In this case, we need to parse the source-list which the Querier
1358*aca3beaaSApple OSS Distributions 	 * has provided us with and check if we have any source list filter
1359*aca3beaaSApple OSS Distributions 	 * entries at T1 for these sources. If we do not, there is no need
1360*aca3beaaSApple OSS Distributions 	 * schedule a report and the query may be dropped.
1361*aca3beaaSApple OSS Distributions 	 * If we do, we must record them and schedule a current-state
1362*aca3beaaSApple OSS Distributions 	 * report for those sources.
1363*aca3beaaSApple OSS Distributions 	 * FIXME: Handling source lists larger than 1 mbuf requires that
1364*aca3beaaSApple OSS Distributions 	 * we pass the mbuf chain pointer down to this function, and use
1365*aca3beaaSApple OSS Distributions 	 * m_getptr() to walk the chain.
1366*aca3beaaSApple OSS Distributions 	 */
1367*aca3beaaSApple OSS Distributions 	if (inm->inm_nsrc > 0) {
1368*aca3beaaSApple OSS Distributions 		const struct in_addr    *ap;
1369*aca3beaaSApple OSS Distributions 		int                      i, nrecorded;
1370*aca3beaaSApple OSS Distributions 
1371*aca3beaaSApple OSS Distributions 		ap = (const struct in_addr *)(igmpv3 + 1);
1372*aca3beaaSApple OSS Distributions 		nrecorded = 0;
1373*aca3beaaSApple OSS Distributions 		for (i = 0; i < nsrc; i++, ap++) {
1374*aca3beaaSApple OSS Distributions 			retval = inm_record_source(inm, ap->s_addr);
1375*aca3beaaSApple OSS Distributions 			if (retval < 0) {
1376*aca3beaaSApple OSS Distributions 				break;
1377*aca3beaaSApple OSS Distributions 			}
1378*aca3beaaSApple OSS Distributions 			nrecorded += retval;
1379*aca3beaaSApple OSS Distributions 		}
1380*aca3beaaSApple OSS Distributions 		if (nrecorded > 0) {
1381*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: schedule response to SG query\n",
1382*aca3beaaSApple OSS Distributions 			    __func__));
1383*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_SG_QUERY_PENDING_MEMBER;
1384*aca3beaaSApple OSS Distributions 			inm->inm_timer = IGMP_RANDOM_DELAY(timer);
1385*aca3beaaSApple OSS Distributions 		}
1386*aca3beaaSApple OSS Distributions 	}
1387*aca3beaaSApple OSS Distributions 
1388*aca3beaaSApple OSS Distributions 	return retval;
1389*aca3beaaSApple OSS Distributions }
1390*aca3beaaSApple OSS Distributions 
1391*aca3beaaSApple OSS Distributions /*
1392*aca3beaaSApple OSS Distributions  * Process a received IGMPv1 host membership report.
1393*aca3beaaSApple OSS Distributions  *
1394*aca3beaaSApple OSS Distributions  * NOTE: 0.0.0.0 workaround breaks const correctness.
1395*aca3beaaSApple OSS Distributions  */
1396*aca3beaaSApple OSS Distributions static int
igmp_input_v1_report(struct ifnet * ifp,struct mbuf * m,struct ip * ip,struct igmp * igmp)1397*aca3beaaSApple OSS Distributions igmp_input_v1_report(struct ifnet *ifp, struct mbuf *m, /*const*/ struct ip *ip,
1398*aca3beaaSApple OSS Distributions     /*const*/ struct igmp *igmp)
1399*aca3beaaSApple OSS Distributions {
1400*aca3beaaSApple OSS Distributions 	struct in_ifaddr *ia;
1401*aca3beaaSApple OSS Distributions 	struct in_multi *inm;
1402*aca3beaaSApple OSS Distributions 
1403*aca3beaaSApple OSS Distributions 	IGMPSTAT_INC(igps_rcv_reports);
1404*aca3beaaSApple OSS Distributions 	OIGMPSTAT_INC(igps_rcv_reports);
1405*aca3beaaSApple OSS Distributions 
1406*aca3beaaSApple OSS Distributions 	if ((ifp->if_flags & IFF_LOOPBACK) ||
1407*aca3beaaSApple OSS Distributions 	    (m->m_pkthdr.pkt_flags & PKTF_LOOP)) {
1408*aca3beaaSApple OSS Distributions 		return 0;
1409*aca3beaaSApple OSS Distributions 	}
1410*aca3beaaSApple OSS Distributions 
1411*aca3beaaSApple OSS Distributions 	if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr) ||
1412*aca3beaaSApple OSS Distributions 	    !in_hosteq(igmp->igmp_group, ip->ip_dst))) {
1413*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_badreports);
1414*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_badreports);
1415*aca3beaaSApple OSS Distributions 		return EINVAL;
1416*aca3beaaSApple OSS Distributions 	}
1417*aca3beaaSApple OSS Distributions 
1418*aca3beaaSApple OSS Distributions 	/*
1419*aca3beaaSApple OSS Distributions 	 * RFC 3376, Section 4.2.13, 9.2, 9.3:
1420*aca3beaaSApple OSS Distributions 	 * Booting clients may use the source address 0.0.0.0. Some
1421*aca3beaaSApple OSS Distributions 	 * IGMP daemons may not know how to use IP_RECVIF to determine
1422*aca3beaaSApple OSS Distributions 	 * the interface upon which this message was received.
1423*aca3beaaSApple OSS Distributions 	 * Replace 0.0.0.0 with the subnet address if told to do so.
1424*aca3beaaSApple OSS Distributions 	 */
1425*aca3beaaSApple OSS Distributions 	if (igmp_recvifkludge && in_nullhost(ip->ip_src)) {
1426*aca3beaaSApple OSS Distributions 		IFP_TO_IA(ifp, ia);
1427*aca3beaaSApple OSS Distributions 		if (ia != NULL) {
1428*aca3beaaSApple OSS Distributions 			IFA_LOCK(&ia->ia_ifa);
1429*aca3beaaSApple OSS Distributions 			ip->ip_src.s_addr = htonl(ia->ia_subnet);
1430*aca3beaaSApple OSS Distributions 			IFA_UNLOCK(&ia->ia_ifa);
1431*aca3beaaSApple OSS Distributions 			IFA_REMREF(&ia->ia_ifa);
1432*aca3beaaSApple OSS Distributions 		}
1433*aca3beaaSApple OSS Distributions 	}
1434*aca3beaaSApple OSS Distributions 
1435*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(igmp->igmp_group,
1436*aca3beaaSApple OSS Distributions 	    ("process v1 report %s on ifp 0x%llx(%s)\n", _igmp_inet_buf,
1437*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1438*aca3beaaSApple OSS Distributions 
1439*aca3beaaSApple OSS Distributions 	/*
1440*aca3beaaSApple OSS Distributions 	 * IGMPv1 report suppression.
1441*aca3beaaSApple OSS Distributions 	 * If we are a member of this group, and our membership should be
1442*aca3beaaSApple OSS Distributions 	 * reported, stop our group timer and transition to the 'lazy' state.
1443*aca3beaaSApple OSS Distributions 	 */
1444*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
1445*aca3beaaSApple OSS Distributions 	IN_LOOKUP_MULTI(&igmp->igmp_group, ifp, inm);
1446*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
1447*aca3beaaSApple OSS Distributions 	if (inm != NULL) {
1448*aca3beaaSApple OSS Distributions 		struct igmp_ifinfo *igi;
1449*aca3beaaSApple OSS Distributions 
1450*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
1451*aca3beaaSApple OSS Distributions 
1452*aca3beaaSApple OSS Distributions 		igi = inm->inm_igi;
1453*aca3beaaSApple OSS Distributions 		VERIFY(igi != NULL);
1454*aca3beaaSApple OSS Distributions 
1455*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_ourreports);
1456*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_ourreports);
1457*aca3beaaSApple OSS Distributions 
1458*aca3beaaSApple OSS Distributions 		/*
1459*aca3beaaSApple OSS Distributions 		 * If we are in IGMPv3 host mode, do not allow the
1460*aca3beaaSApple OSS Distributions 		 * other host's IGMPv1 report to suppress our reports
1461*aca3beaaSApple OSS Distributions 		 * unless explicitly configured to do so.
1462*aca3beaaSApple OSS Distributions 		 */
1463*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
1464*aca3beaaSApple OSS Distributions 		if (igi->igi_version == IGMP_VERSION_3) {
1465*aca3beaaSApple OSS Distributions 			if (igmp_legacysupp) {
1466*aca3beaaSApple OSS Distributions 				igmp_v3_suppress_group_record(inm);
1467*aca3beaaSApple OSS Distributions 			}
1468*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
1469*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
1470*aca3beaaSApple OSS Distributions 			INM_REMREF(inm); /* from IN_LOOKUP_MULTI */
1471*aca3beaaSApple OSS Distributions 			return 0;
1472*aca3beaaSApple OSS Distributions 		}
1473*aca3beaaSApple OSS Distributions 
1474*aca3beaaSApple OSS Distributions 		INM_LOCK_ASSERT_HELD(inm);
1475*aca3beaaSApple OSS Distributions 		inm->inm_timer = 0;
1476*aca3beaaSApple OSS Distributions 
1477*aca3beaaSApple OSS Distributions 		switch (inm->inm_state) {
1478*aca3beaaSApple OSS Distributions 		case IGMP_NOT_MEMBER:
1479*aca3beaaSApple OSS Distributions 		case IGMP_SILENT_MEMBER:
1480*aca3beaaSApple OSS Distributions 			break;
1481*aca3beaaSApple OSS Distributions 		case IGMP_IDLE_MEMBER:
1482*aca3beaaSApple OSS Distributions 		case IGMP_LAZY_MEMBER:
1483*aca3beaaSApple OSS Distributions 		case IGMP_AWAKENING_MEMBER:
1484*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(igmp->igmp_group,
1485*aca3beaaSApple OSS Distributions 			    ("report suppressed for %s on ifp 0x%llx(%s)\n",
1486*aca3beaaSApple OSS Distributions 			    _igmp_inet_buf,
1487*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1488*aca3beaaSApple OSS Distributions 			OS_FALLTHROUGH;
1489*aca3beaaSApple OSS Distributions 		case IGMP_SLEEPING_MEMBER:
1490*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_SLEEPING_MEMBER;
1491*aca3beaaSApple OSS Distributions 			break;
1492*aca3beaaSApple OSS Distributions 		case IGMP_REPORTING_MEMBER:
1493*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(igmp->igmp_group,
1494*aca3beaaSApple OSS Distributions 			    ("report suppressed for %s on ifp 0x%llx(%s)\n",
1495*aca3beaaSApple OSS Distributions 			    _igmp_inet_buf,
1496*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1497*aca3beaaSApple OSS Distributions 			if (igi->igi_version == IGMP_VERSION_1) {
1498*aca3beaaSApple OSS Distributions 				inm->inm_state = IGMP_LAZY_MEMBER;
1499*aca3beaaSApple OSS Distributions 			} else if (igi->igi_version == IGMP_VERSION_2) {
1500*aca3beaaSApple OSS Distributions 				inm->inm_state = IGMP_SLEEPING_MEMBER;
1501*aca3beaaSApple OSS Distributions 			}
1502*aca3beaaSApple OSS Distributions 			break;
1503*aca3beaaSApple OSS Distributions 		case IGMP_G_QUERY_PENDING_MEMBER:
1504*aca3beaaSApple OSS Distributions 		case IGMP_SG_QUERY_PENDING_MEMBER:
1505*aca3beaaSApple OSS Distributions 		case IGMP_LEAVING_MEMBER:
1506*aca3beaaSApple OSS Distributions 			break;
1507*aca3beaaSApple OSS Distributions 		}
1508*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
1509*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
1510*aca3beaaSApple OSS Distributions 		INM_REMREF(inm); /* from IN_LOOKUP_MULTI */
1511*aca3beaaSApple OSS Distributions 	}
1512*aca3beaaSApple OSS Distributions 
1513*aca3beaaSApple OSS Distributions 	return 0;
1514*aca3beaaSApple OSS Distributions }
1515*aca3beaaSApple OSS Distributions 
1516*aca3beaaSApple OSS Distributions /*
1517*aca3beaaSApple OSS Distributions  * Process a received IGMPv2 host membership report.
1518*aca3beaaSApple OSS Distributions  *
1519*aca3beaaSApple OSS Distributions  * NOTE: 0.0.0.0 workaround breaks const correctness.
1520*aca3beaaSApple OSS Distributions  */
1521*aca3beaaSApple OSS Distributions static int
igmp_input_v2_report(struct ifnet * ifp,struct mbuf * m,struct ip * ip,struct igmp * igmp)1522*aca3beaaSApple OSS Distributions igmp_input_v2_report(struct ifnet *ifp, struct mbuf *m, /*const*/ struct ip *ip,
1523*aca3beaaSApple OSS Distributions     /*const*/ struct igmp *igmp)
1524*aca3beaaSApple OSS Distributions {
1525*aca3beaaSApple OSS Distributions 	struct in_ifaddr *ia;
1526*aca3beaaSApple OSS Distributions 	struct in_multi *inm;
1527*aca3beaaSApple OSS Distributions 
1528*aca3beaaSApple OSS Distributions 	/*
1529*aca3beaaSApple OSS Distributions 	 * Make sure we don't hear our own membership report.  Fast
1530*aca3beaaSApple OSS Distributions 	 * leave requires knowing that we are the only member of a
1531*aca3beaaSApple OSS Distributions 	 * group.
1532*aca3beaaSApple OSS Distributions 	 */
1533*aca3beaaSApple OSS Distributions 	IFP_TO_IA(ifp, ia);
1534*aca3beaaSApple OSS Distributions 	if (ia != NULL) {
1535*aca3beaaSApple OSS Distributions 		IFA_LOCK(&ia->ia_ifa);
1536*aca3beaaSApple OSS Distributions 		if (in_hosteq(ip->ip_src, IA_SIN(ia)->sin_addr)) {
1537*aca3beaaSApple OSS Distributions 			IFA_UNLOCK(&ia->ia_ifa);
1538*aca3beaaSApple OSS Distributions 			IFA_REMREF(&ia->ia_ifa);
1539*aca3beaaSApple OSS Distributions 			return 0;
1540*aca3beaaSApple OSS Distributions 		}
1541*aca3beaaSApple OSS Distributions 		IFA_UNLOCK(&ia->ia_ifa);
1542*aca3beaaSApple OSS Distributions 	}
1543*aca3beaaSApple OSS Distributions 
1544*aca3beaaSApple OSS Distributions 	IGMPSTAT_INC(igps_rcv_reports);
1545*aca3beaaSApple OSS Distributions 	OIGMPSTAT_INC(igps_rcv_reports);
1546*aca3beaaSApple OSS Distributions 
1547*aca3beaaSApple OSS Distributions 	if ((ifp->if_flags & IFF_LOOPBACK) ||
1548*aca3beaaSApple OSS Distributions 	    (m->m_pkthdr.pkt_flags & PKTF_LOOP)) {
1549*aca3beaaSApple OSS Distributions 		if (ia != NULL) {
1550*aca3beaaSApple OSS Distributions 			IFA_REMREF(&ia->ia_ifa);
1551*aca3beaaSApple OSS Distributions 		}
1552*aca3beaaSApple OSS Distributions 		return 0;
1553*aca3beaaSApple OSS Distributions 	}
1554*aca3beaaSApple OSS Distributions 
1555*aca3beaaSApple OSS Distributions 	if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
1556*aca3beaaSApple OSS Distributions 	    !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
1557*aca3beaaSApple OSS Distributions 		if (ia != NULL) {
1558*aca3beaaSApple OSS Distributions 			IFA_REMREF(&ia->ia_ifa);
1559*aca3beaaSApple OSS Distributions 		}
1560*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_badreports);
1561*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_badreports);
1562*aca3beaaSApple OSS Distributions 		return EINVAL;
1563*aca3beaaSApple OSS Distributions 	}
1564*aca3beaaSApple OSS Distributions 
1565*aca3beaaSApple OSS Distributions 	/*
1566*aca3beaaSApple OSS Distributions 	 * RFC 3376, Section 4.2.13, 9.2, 9.3:
1567*aca3beaaSApple OSS Distributions 	 * Booting clients may use the source address 0.0.0.0. Some
1568*aca3beaaSApple OSS Distributions 	 * IGMP daemons may not know how to use IP_RECVIF to determine
1569*aca3beaaSApple OSS Distributions 	 * the interface upon which this message was received.
1570*aca3beaaSApple OSS Distributions 	 * Replace 0.0.0.0 with the subnet address if told to do so.
1571*aca3beaaSApple OSS Distributions 	 */
1572*aca3beaaSApple OSS Distributions 	if (igmp_recvifkludge && in_nullhost(ip->ip_src)) {
1573*aca3beaaSApple OSS Distributions 		if (ia != NULL) {
1574*aca3beaaSApple OSS Distributions 			IFA_LOCK(&ia->ia_ifa);
1575*aca3beaaSApple OSS Distributions 			ip->ip_src.s_addr = htonl(ia->ia_subnet);
1576*aca3beaaSApple OSS Distributions 			IFA_UNLOCK(&ia->ia_ifa);
1577*aca3beaaSApple OSS Distributions 		}
1578*aca3beaaSApple OSS Distributions 	}
1579*aca3beaaSApple OSS Distributions 	if (ia != NULL) {
1580*aca3beaaSApple OSS Distributions 		IFA_REMREF(&ia->ia_ifa);
1581*aca3beaaSApple OSS Distributions 	}
1582*aca3beaaSApple OSS Distributions 
1583*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(igmp->igmp_group,
1584*aca3beaaSApple OSS Distributions 	    ("process v2 report %s on ifp 0x%llx(%s)\n", _igmp_inet_buf,
1585*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1586*aca3beaaSApple OSS Distributions 
1587*aca3beaaSApple OSS Distributions 	/*
1588*aca3beaaSApple OSS Distributions 	 * IGMPv2 report suppression.
1589*aca3beaaSApple OSS Distributions 	 * If we are a member of this group, and our membership should be
1590*aca3beaaSApple OSS Distributions 	 * reported, and our group timer is pending or about to be reset,
1591*aca3beaaSApple OSS Distributions 	 * stop our group timer by transitioning to the 'lazy' state.
1592*aca3beaaSApple OSS Distributions 	 */
1593*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
1594*aca3beaaSApple OSS Distributions 	IN_LOOKUP_MULTI(&igmp->igmp_group, ifp, inm);
1595*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
1596*aca3beaaSApple OSS Distributions 	if (inm != NULL) {
1597*aca3beaaSApple OSS Distributions 		struct igmp_ifinfo *igi;
1598*aca3beaaSApple OSS Distributions 
1599*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
1600*aca3beaaSApple OSS Distributions 		igi = inm->inm_igi;
1601*aca3beaaSApple OSS Distributions 		VERIFY(igi != NULL);
1602*aca3beaaSApple OSS Distributions 
1603*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_ourreports);
1604*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_ourreports);
1605*aca3beaaSApple OSS Distributions 
1606*aca3beaaSApple OSS Distributions 		/*
1607*aca3beaaSApple OSS Distributions 		 * If we are in IGMPv3 host mode, do not allow the
1608*aca3beaaSApple OSS Distributions 		 * other host's IGMPv1 report to suppress our reports
1609*aca3beaaSApple OSS Distributions 		 * unless explicitly configured to do so.
1610*aca3beaaSApple OSS Distributions 		 */
1611*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
1612*aca3beaaSApple OSS Distributions 		if (igi->igi_version == IGMP_VERSION_3) {
1613*aca3beaaSApple OSS Distributions 			if (igmp_legacysupp) {
1614*aca3beaaSApple OSS Distributions 				igmp_v3_suppress_group_record(inm);
1615*aca3beaaSApple OSS Distributions 			}
1616*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
1617*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
1618*aca3beaaSApple OSS Distributions 			INM_REMREF(inm);
1619*aca3beaaSApple OSS Distributions 			return 0;
1620*aca3beaaSApple OSS Distributions 		}
1621*aca3beaaSApple OSS Distributions 
1622*aca3beaaSApple OSS Distributions 		inm->inm_timer = 0;
1623*aca3beaaSApple OSS Distributions 
1624*aca3beaaSApple OSS Distributions 		switch (inm->inm_state) {
1625*aca3beaaSApple OSS Distributions 		case IGMP_NOT_MEMBER:
1626*aca3beaaSApple OSS Distributions 		case IGMP_SILENT_MEMBER:
1627*aca3beaaSApple OSS Distributions 		case IGMP_SLEEPING_MEMBER:
1628*aca3beaaSApple OSS Distributions 			break;
1629*aca3beaaSApple OSS Distributions 		case IGMP_REPORTING_MEMBER:
1630*aca3beaaSApple OSS Distributions 		case IGMP_IDLE_MEMBER:
1631*aca3beaaSApple OSS Distributions 		case IGMP_AWAKENING_MEMBER:
1632*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(igmp->igmp_group,
1633*aca3beaaSApple OSS Distributions 			    ("report suppressed for %s on ifp 0x%llx(%s)\n",
1634*aca3beaaSApple OSS Distributions 			    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp),
1635*aca3beaaSApple OSS Distributions 			    if_name(ifp)));
1636*aca3beaaSApple OSS Distributions 			OS_FALLTHROUGH;
1637*aca3beaaSApple OSS Distributions 		case IGMP_LAZY_MEMBER:
1638*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_LAZY_MEMBER;
1639*aca3beaaSApple OSS Distributions 			break;
1640*aca3beaaSApple OSS Distributions 		case IGMP_G_QUERY_PENDING_MEMBER:
1641*aca3beaaSApple OSS Distributions 		case IGMP_SG_QUERY_PENDING_MEMBER:
1642*aca3beaaSApple OSS Distributions 		case IGMP_LEAVING_MEMBER:
1643*aca3beaaSApple OSS Distributions 			break;
1644*aca3beaaSApple OSS Distributions 		}
1645*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
1646*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
1647*aca3beaaSApple OSS Distributions 		INM_REMREF(inm);
1648*aca3beaaSApple OSS Distributions 	}
1649*aca3beaaSApple OSS Distributions 
1650*aca3beaaSApple OSS Distributions 	return 0;
1651*aca3beaaSApple OSS Distributions }
1652*aca3beaaSApple OSS Distributions 
1653*aca3beaaSApple OSS Distributions void
igmp_input(struct mbuf * m,int off)1654*aca3beaaSApple OSS Distributions igmp_input(struct mbuf *m, int off)
1655*aca3beaaSApple OSS Distributions {
1656*aca3beaaSApple OSS Distributions 	int iphlen;
1657*aca3beaaSApple OSS Distributions 	struct ifnet *ifp;
1658*aca3beaaSApple OSS Distributions 	struct igmp *igmp;
1659*aca3beaaSApple OSS Distributions 	struct ip *ip;
1660*aca3beaaSApple OSS Distributions 	int igmplen;
1661*aca3beaaSApple OSS Distributions 	int minlen;
1662*aca3beaaSApple OSS Distributions 	int queryver;
1663*aca3beaaSApple OSS Distributions 
1664*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: called w/mbuf (0x%llx,%d)\n", __func__,
1665*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(m), off));
1666*aca3beaaSApple OSS Distributions 
1667*aca3beaaSApple OSS Distributions 	ifp = m->m_pkthdr.rcvif;
1668*aca3beaaSApple OSS Distributions 
1669*aca3beaaSApple OSS Distributions 	IGMPSTAT_INC(igps_rcv_total);
1670*aca3beaaSApple OSS Distributions 	OIGMPSTAT_INC(igps_rcv_total);
1671*aca3beaaSApple OSS Distributions 
1672*aca3beaaSApple OSS Distributions 	/* Expect 32-bit aligned data pointer on strict-align platforms */
1673*aca3beaaSApple OSS Distributions 	MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
1674*aca3beaaSApple OSS Distributions 
1675*aca3beaaSApple OSS Distributions 	ip = mtod(m, struct ip *);
1676*aca3beaaSApple OSS Distributions 	iphlen = off;
1677*aca3beaaSApple OSS Distributions 
1678*aca3beaaSApple OSS Distributions 	/* By now, ip_len no longer contains the length of IP header */
1679*aca3beaaSApple OSS Distributions 	igmplen = ip->ip_len;
1680*aca3beaaSApple OSS Distributions 
1681*aca3beaaSApple OSS Distributions 	/*
1682*aca3beaaSApple OSS Distributions 	 * Validate lengths.
1683*aca3beaaSApple OSS Distributions 	 */
1684*aca3beaaSApple OSS Distributions 	if (igmplen < IGMP_MINLEN) {
1685*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_tooshort);
1686*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_tooshort);
1687*aca3beaaSApple OSS Distributions 		m_freem(m);
1688*aca3beaaSApple OSS Distributions 		return;
1689*aca3beaaSApple OSS Distributions 	}
1690*aca3beaaSApple OSS Distributions 
1691*aca3beaaSApple OSS Distributions 	/*
1692*aca3beaaSApple OSS Distributions 	 * Always pullup to the minimum size for v1/v2 or v3
1693*aca3beaaSApple OSS Distributions 	 * to amortize calls to m_pulldown().
1694*aca3beaaSApple OSS Distributions 	 */
1695*aca3beaaSApple OSS Distributions 	if (igmplen >= IGMP_V3_QUERY_MINLEN) {
1696*aca3beaaSApple OSS Distributions 		minlen = IGMP_V3_QUERY_MINLEN;
1697*aca3beaaSApple OSS Distributions 	} else {
1698*aca3beaaSApple OSS Distributions 		minlen = IGMP_MINLEN;
1699*aca3beaaSApple OSS Distributions 	}
1700*aca3beaaSApple OSS Distributions 
1701*aca3beaaSApple OSS Distributions 	/* A bit more expensive than M_STRUCT_GET, but ensures alignment */
1702*aca3beaaSApple OSS Distributions 	M_STRUCT_GET0(igmp, struct igmp *, m, off, minlen);
1703*aca3beaaSApple OSS Distributions 	if (igmp == NULL) {
1704*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_tooshort);
1705*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_tooshort);
1706*aca3beaaSApple OSS Distributions 		return;
1707*aca3beaaSApple OSS Distributions 	}
1708*aca3beaaSApple OSS Distributions 	/* N.B.: we assume the packet was correctly aligned in ip_input. */
1709*aca3beaaSApple OSS Distributions 
1710*aca3beaaSApple OSS Distributions 	/*
1711*aca3beaaSApple OSS Distributions 	 * Validate checksum.
1712*aca3beaaSApple OSS Distributions 	 */
1713*aca3beaaSApple OSS Distributions 	m->m_data += iphlen;
1714*aca3beaaSApple OSS Distributions 	m->m_len -= iphlen;
1715*aca3beaaSApple OSS Distributions 	if (in_cksum(m, igmplen)) {
1716*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_badsum);
1717*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_badsum);
1718*aca3beaaSApple OSS Distributions 		m_freem(m);
1719*aca3beaaSApple OSS Distributions 		return;
1720*aca3beaaSApple OSS Distributions 	}
1721*aca3beaaSApple OSS Distributions 	m->m_data -= iphlen;
1722*aca3beaaSApple OSS Distributions 	m->m_len += iphlen;
1723*aca3beaaSApple OSS Distributions 
1724*aca3beaaSApple OSS Distributions 	/*
1725*aca3beaaSApple OSS Distributions 	 * IGMP control traffic is link-scope, and must have a TTL of 1.
1726*aca3beaaSApple OSS Distributions 	 * DVMRP traffic (e.g. mrinfo, mtrace) is an exception;
1727*aca3beaaSApple OSS Distributions 	 * probe packets may come from beyond the LAN.
1728*aca3beaaSApple OSS Distributions 	 */
1729*aca3beaaSApple OSS Distributions 	if (igmp->igmp_type != IGMP_DVMRP && ip->ip_ttl != 1) {
1730*aca3beaaSApple OSS Distributions 		IGMPSTAT_INC(igps_rcv_badttl);
1731*aca3beaaSApple OSS Distributions 		m_freem(m);
1732*aca3beaaSApple OSS Distributions 		return;
1733*aca3beaaSApple OSS Distributions 	}
1734*aca3beaaSApple OSS Distributions 
1735*aca3beaaSApple OSS Distributions 	switch (igmp->igmp_type) {
1736*aca3beaaSApple OSS Distributions 	case IGMP_HOST_MEMBERSHIP_QUERY:
1737*aca3beaaSApple OSS Distributions 		if (igmplen == IGMP_MINLEN) {
1738*aca3beaaSApple OSS Distributions 			if (igmp->igmp_code == 0) {
1739*aca3beaaSApple OSS Distributions 				queryver = IGMP_VERSION_1;
1740*aca3beaaSApple OSS Distributions 			} else {
1741*aca3beaaSApple OSS Distributions 				queryver = IGMP_VERSION_2;
1742*aca3beaaSApple OSS Distributions 			}
1743*aca3beaaSApple OSS Distributions 		} else if (igmplen >= IGMP_V3_QUERY_MINLEN) {
1744*aca3beaaSApple OSS Distributions 			queryver = IGMP_VERSION_3;
1745*aca3beaaSApple OSS Distributions 		} else {
1746*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_tooshort);
1747*aca3beaaSApple OSS Distributions 			OIGMPSTAT_INC(igps_rcv_tooshort);
1748*aca3beaaSApple OSS Distributions 			m_freem(m);
1749*aca3beaaSApple OSS Distributions 			return;
1750*aca3beaaSApple OSS Distributions 		}
1751*aca3beaaSApple OSS Distributions 
1752*aca3beaaSApple OSS Distributions 		OIGMPSTAT_INC(igps_rcv_queries);
1753*aca3beaaSApple OSS Distributions 
1754*aca3beaaSApple OSS Distributions 		switch (queryver) {
1755*aca3beaaSApple OSS Distributions 		case IGMP_VERSION_1:
1756*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_v1v2_queries);
1757*aca3beaaSApple OSS Distributions 			if (!igmp_v1enable) {
1758*aca3beaaSApple OSS Distributions 				break;
1759*aca3beaaSApple OSS Distributions 			}
1760*aca3beaaSApple OSS Distributions 			if (igmp_input_v1_query(ifp, ip, igmp) != 0) {
1761*aca3beaaSApple OSS Distributions 				m_freem(m);
1762*aca3beaaSApple OSS Distributions 				return;
1763*aca3beaaSApple OSS Distributions 			}
1764*aca3beaaSApple OSS Distributions 			break;
1765*aca3beaaSApple OSS Distributions 
1766*aca3beaaSApple OSS Distributions 		case IGMP_VERSION_2:
1767*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_v1v2_queries);
1768*aca3beaaSApple OSS Distributions 			if (!igmp_v2enable) {
1769*aca3beaaSApple OSS Distributions 				break;
1770*aca3beaaSApple OSS Distributions 			}
1771*aca3beaaSApple OSS Distributions 			if (igmp_input_v2_query(ifp, ip, igmp) != 0) {
1772*aca3beaaSApple OSS Distributions 				m_freem(m);
1773*aca3beaaSApple OSS Distributions 				return;
1774*aca3beaaSApple OSS Distributions 			}
1775*aca3beaaSApple OSS Distributions 			break;
1776*aca3beaaSApple OSS Distributions 
1777*aca3beaaSApple OSS Distributions 		case IGMP_VERSION_3: {
1778*aca3beaaSApple OSS Distributions 			struct igmpv3 *igmpv3;
1779*aca3beaaSApple OSS Distributions 			uint16_t igmpv3len;
1780*aca3beaaSApple OSS Distributions 			uint16_t srclen;
1781*aca3beaaSApple OSS Distributions 			int nsrc;
1782*aca3beaaSApple OSS Distributions 
1783*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_v3_queries);
1784*aca3beaaSApple OSS Distributions 			igmpv3 = (struct igmpv3 *)igmp;
1785*aca3beaaSApple OSS Distributions 			/*
1786*aca3beaaSApple OSS Distributions 			 * Validate length based on source count.
1787*aca3beaaSApple OSS Distributions 			 */
1788*aca3beaaSApple OSS Distributions 			nsrc = ntohs(igmpv3->igmp_numsrc);
1789*aca3beaaSApple OSS Distributions 			/*
1790*aca3beaaSApple OSS Distributions 			 * The max vaue of nsrc is limited by the
1791*aca3beaaSApple OSS Distributions 			 * MTU of the network on which the datagram
1792*aca3beaaSApple OSS Distributions 			 * is received
1793*aca3beaaSApple OSS Distributions 			 */
1794*aca3beaaSApple OSS Distributions 			if (nsrc < 0 || nsrc > IGMP_V3_QUERY_MAX_SRCS) {
1795*aca3beaaSApple OSS Distributions 				IGMPSTAT_INC(igps_rcv_tooshort);
1796*aca3beaaSApple OSS Distributions 				OIGMPSTAT_INC(igps_rcv_tooshort);
1797*aca3beaaSApple OSS Distributions 				m_freem(m);
1798*aca3beaaSApple OSS Distributions 				return;
1799*aca3beaaSApple OSS Distributions 			}
1800*aca3beaaSApple OSS Distributions 			srclen = sizeof(struct in_addr) * (uint16_t)nsrc;
1801*aca3beaaSApple OSS Distributions 			if (igmplen < (IGMP_V3_QUERY_MINLEN + srclen)) {
1802*aca3beaaSApple OSS Distributions 				IGMPSTAT_INC(igps_rcv_tooshort);
1803*aca3beaaSApple OSS Distributions 				OIGMPSTAT_INC(igps_rcv_tooshort);
1804*aca3beaaSApple OSS Distributions 				m_freem(m);
1805*aca3beaaSApple OSS Distributions 				return;
1806*aca3beaaSApple OSS Distributions 			}
1807*aca3beaaSApple OSS Distributions 			igmpv3len = IGMP_V3_QUERY_MINLEN + srclen;
1808*aca3beaaSApple OSS Distributions 			/*
1809*aca3beaaSApple OSS Distributions 			 * A bit more expensive than M_STRUCT_GET,
1810*aca3beaaSApple OSS Distributions 			 * but ensures alignment.
1811*aca3beaaSApple OSS Distributions 			 */
1812*aca3beaaSApple OSS Distributions 			M_STRUCT_GET0(igmpv3, struct igmpv3 *, m,
1813*aca3beaaSApple OSS Distributions 			    off, igmpv3len);
1814*aca3beaaSApple OSS Distributions 			if (igmpv3 == NULL) {
1815*aca3beaaSApple OSS Distributions 				IGMPSTAT_INC(igps_rcv_tooshort);
1816*aca3beaaSApple OSS Distributions 				OIGMPSTAT_INC(igps_rcv_tooshort);
1817*aca3beaaSApple OSS Distributions 				return;
1818*aca3beaaSApple OSS Distributions 			}
1819*aca3beaaSApple OSS Distributions 			/*
1820*aca3beaaSApple OSS Distributions 			 * N.B.: we assume the packet was correctly
1821*aca3beaaSApple OSS Distributions 			 * aligned in ip_input.
1822*aca3beaaSApple OSS Distributions 			 */
1823*aca3beaaSApple OSS Distributions 			if (igmp_input_v3_query(ifp, ip, igmpv3) != 0) {
1824*aca3beaaSApple OSS Distributions 				m_freem(m);
1825*aca3beaaSApple OSS Distributions 				return;
1826*aca3beaaSApple OSS Distributions 			}
1827*aca3beaaSApple OSS Distributions 		}
1828*aca3beaaSApple OSS Distributions 		break;
1829*aca3beaaSApple OSS Distributions 		}
1830*aca3beaaSApple OSS Distributions 		break;
1831*aca3beaaSApple OSS Distributions 
1832*aca3beaaSApple OSS Distributions 	case IGMP_v1_HOST_MEMBERSHIP_REPORT:
1833*aca3beaaSApple OSS Distributions 		if (!igmp_v1enable) {
1834*aca3beaaSApple OSS Distributions 			break;
1835*aca3beaaSApple OSS Distributions 		}
1836*aca3beaaSApple OSS Distributions 		if (igmp_input_v1_report(ifp, m, ip, igmp) != 0) {
1837*aca3beaaSApple OSS Distributions 			m_freem(m);
1838*aca3beaaSApple OSS Distributions 			return;
1839*aca3beaaSApple OSS Distributions 		}
1840*aca3beaaSApple OSS Distributions 		break;
1841*aca3beaaSApple OSS Distributions 
1842*aca3beaaSApple OSS Distributions 	case IGMP_v2_HOST_MEMBERSHIP_REPORT:
1843*aca3beaaSApple OSS Distributions 		if (!igmp_v2enable) {
1844*aca3beaaSApple OSS Distributions 			break;
1845*aca3beaaSApple OSS Distributions 		}
1846*aca3beaaSApple OSS Distributions 		if (!ip_checkrouteralert(m)) {
1847*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_nora);
1848*aca3beaaSApple OSS Distributions 		}
1849*aca3beaaSApple OSS Distributions 		if (igmp_input_v2_report(ifp, m, ip, igmp) != 0) {
1850*aca3beaaSApple OSS Distributions 			m_freem(m);
1851*aca3beaaSApple OSS Distributions 			return;
1852*aca3beaaSApple OSS Distributions 		}
1853*aca3beaaSApple OSS Distributions 		break;
1854*aca3beaaSApple OSS Distributions 
1855*aca3beaaSApple OSS Distributions 	case IGMP_v3_HOST_MEMBERSHIP_REPORT:
1856*aca3beaaSApple OSS Distributions 		/*
1857*aca3beaaSApple OSS Distributions 		 * Hosts do not need to process IGMPv3 membership reports,
1858*aca3beaaSApple OSS Distributions 		 * as report suppression is no longer required.
1859*aca3beaaSApple OSS Distributions 		 */
1860*aca3beaaSApple OSS Distributions 		if (!ip_checkrouteralert(m)) {
1861*aca3beaaSApple OSS Distributions 			IGMPSTAT_INC(igps_rcv_nora);
1862*aca3beaaSApple OSS Distributions 		}
1863*aca3beaaSApple OSS Distributions 		break;
1864*aca3beaaSApple OSS Distributions 
1865*aca3beaaSApple OSS Distributions 	default:
1866*aca3beaaSApple OSS Distributions 		break;
1867*aca3beaaSApple OSS Distributions 	}
1868*aca3beaaSApple OSS Distributions 
1869*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_NOTHELD();
1870*aca3beaaSApple OSS Distributions 	/*
1871*aca3beaaSApple OSS Distributions 	 * Pass all valid IGMP packets up to any process(es) listening on a
1872*aca3beaaSApple OSS Distributions 	 * raw IGMP socket.
1873*aca3beaaSApple OSS Distributions 	 */
1874*aca3beaaSApple OSS Distributions 	rip_input(m, off);
1875*aca3beaaSApple OSS Distributions }
1876*aca3beaaSApple OSS Distributions 
1877*aca3beaaSApple OSS Distributions /*
1878*aca3beaaSApple OSS Distributions  * Schedule IGMP timer based on various parameters; caller must ensure that
1879*aca3beaaSApple OSS Distributions  * lock ordering is maintained as this routine acquires IGMP global lock.
1880*aca3beaaSApple OSS Distributions  */
1881*aca3beaaSApple OSS Distributions void
igmp_set_timeout(struct igmp_tparams * itp)1882*aca3beaaSApple OSS Distributions igmp_set_timeout(struct igmp_tparams *itp)
1883*aca3beaaSApple OSS Distributions {
1884*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_NOTHELD();
1885*aca3beaaSApple OSS Distributions 	VERIFY(itp != NULL);
1886*aca3beaaSApple OSS Distributions 
1887*aca3beaaSApple OSS Distributions 	if (itp->qpt != 0 || itp->it != 0 || itp->cst != 0 || itp->sct != 0) {
1888*aca3beaaSApple OSS Distributions 		IGMP_LOCK();
1889*aca3beaaSApple OSS Distributions 		if (itp->qpt != 0) {
1890*aca3beaaSApple OSS Distributions 			querier_present_timers_running = 1;
1891*aca3beaaSApple OSS Distributions 		}
1892*aca3beaaSApple OSS Distributions 		if (itp->it != 0) {
1893*aca3beaaSApple OSS Distributions 			interface_timers_running = 1;
1894*aca3beaaSApple OSS Distributions 		}
1895*aca3beaaSApple OSS Distributions 		if (itp->cst != 0) {
1896*aca3beaaSApple OSS Distributions 			current_state_timers_running = 1;
1897*aca3beaaSApple OSS Distributions 		}
1898*aca3beaaSApple OSS Distributions 		if (itp->sct != 0) {
1899*aca3beaaSApple OSS Distributions 			state_change_timers_running = 1;
1900*aca3beaaSApple OSS Distributions 		}
1901*aca3beaaSApple OSS Distributions 		igmp_sched_timeout(itp->fast);
1902*aca3beaaSApple OSS Distributions 		IGMP_UNLOCK();
1903*aca3beaaSApple OSS Distributions 	}
1904*aca3beaaSApple OSS Distributions }
1905*aca3beaaSApple OSS Distributions 
1906*aca3beaaSApple OSS Distributions void
igmp_set_fast_timeout(struct igmp_tparams * itp)1907*aca3beaaSApple OSS Distributions igmp_set_fast_timeout(struct igmp_tparams *itp)
1908*aca3beaaSApple OSS Distributions {
1909*aca3beaaSApple OSS Distributions 	VERIFY(itp != NULL);
1910*aca3beaaSApple OSS Distributions 	itp->fast = true;
1911*aca3beaaSApple OSS Distributions 	igmp_set_timeout(itp);
1912*aca3beaaSApple OSS Distributions }
1913*aca3beaaSApple OSS Distributions 
1914*aca3beaaSApple OSS Distributions /*
1915*aca3beaaSApple OSS Distributions  * IGMP timer handler (per 1 second).
1916*aca3beaaSApple OSS Distributions  */
1917*aca3beaaSApple OSS Distributions static void
igmp_timeout(void * arg)1918*aca3beaaSApple OSS Distributions igmp_timeout(void *arg)
1919*aca3beaaSApple OSS Distributions {
1920*aca3beaaSApple OSS Distributions 	struct ifqueue           scq;   /* State-change packets */
1921*aca3beaaSApple OSS Distributions 	struct ifqueue           qrq;   /* Query response packets */
1922*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
1923*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo      *igi;
1924*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
1925*aca3beaaSApple OSS Distributions 	unsigned int             loop = 0, uri_sec = 0;
1926*aca3beaaSApple OSS Distributions 	SLIST_HEAD(, in_multi)  inm_dthead;
1927*aca3beaaSApple OSS Distributions 	bool                     fast = arg != NULL;
1928*aca3beaaSApple OSS Distributions 
1929*aca3beaaSApple OSS Distributions 	SLIST_INIT(&inm_dthead);
1930*aca3beaaSApple OSS Distributions 
1931*aca3beaaSApple OSS Distributions 	/*
1932*aca3beaaSApple OSS Distributions 	 * Update coarse-grained networking timestamp (in sec.); the idea
1933*aca3beaaSApple OSS Distributions 	 * is to piggy-back on the timeout callout to update the counter
1934*aca3beaaSApple OSS Distributions 	 * returnable via net_uptime().
1935*aca3beaaSApple OSS Distributions 	 */
1936*aca3beaaSApple OSS Distributions 	net_update_uptime();
1937*aca3beaaSApple OSS Distributions 
1938*aca3beaaSApple OSS Distributions 	IGMP_LOCK();
1939*aca3beaaSApple OSS Distributions 
1940*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: qpt %d, it %d, cst %d, sct %d, fast %d\n", __func__,
1941*aca3beaaSApple OSS Distributions 	    querier_present_timers_running, interface_timers_running,
1942*aca3beaaSApple OSS Distributions 	    current_state_timers_running, state_change_timers_running,
1943*aca3beaaSApple OSS Distributions 	    fast));
1944*aca3beaaSApple OSS Distributions 
1945*aca3beaaSApple OSS Distributions 	if (fast) {
1946*aca3beaaSApple OSS Distributions 		/*
1947*aca3beaaSApple OSS Distributions 		 * When running the fast timer, skip processing
1948*aca3beaaSApple OSS Distributions 		 * of "querier present" timers since they are
1949*aca3beaaSApple OSS Distributions 		 * based on 1-second intervals.
1950*aca3beaaSApple OSS Distributions 		 */
1951*aca3beaaSApple OSS Distributions 		goto skip_query_timers;
1952*aca3beaaSApple OSS Distributions 	}
1953*aca3beaaSApple OSS Distributions 	/*
1954*aca3beaaSApple OSS Distributions 	 * IGMPv1/v2 querier present timer processing.
1955*aca3beaaSApple OSS Distributions 	 */
1956*aca3beaaSApple OSS Distributions 	if (querier_present_timers_running) {
1957*aca3beaaSApple OSS Distributions 		querier_present_timers_running = 0;
1958*aca3beaaSApple OSS Distributions 		LIST_FOREACH(igi, &igi_head, igi_link) {
1959*aca3beaaSApple OSS Distributions 			IGI_LOCK(igi);
1960*aca3beaaSApple OSS Distributions 			igmp_v1v2_process_querier_timers(igi);
1961*aca3beaaSApple OSS Distributions 			if (igi->igi_v1_timer > 0 || igi->igi_v2_timer > 0) {
1962*aca3beaaSApple OSS Distributions 				querier_present_timers_running = 1;
1963*aca3beaaSApple OSS Distributions 			}
1964*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
1965*aca3beaaSApple OSS Distributions 		}
1966*aca3beaaSApple OSS Distributions 	}
1967*aca3beaaSApple OSS Distributions 
1968*aca3beaaSApple OSS Distributions 	/*
1969*aca3beaaSApple OSS Distributions 	 * IGMPv3 General Query response timer processing.
1970*aca3beaaSApple OSS Distributions 	 */
1971*aca3beaaSApple OSS Distributions 	if (interface_timers_running) {
1972*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: interface timers running\n", __func__));
1973*aca3beaaSApple OSS Distributions 		interface_timers_running = 0;
1974*aca3beaaSApple OSS Distributions 		LIST_FOREACH(igi, &igi_head, igi_link) {
1975*aca3beaaSApple OSS Distributions 			IGI_LOCK(igi);
1976*aca3beaaSApple OSS Distributions 			if (igi->igi_version != IGMP_VERSION_3) {
1977*aca3beaaSApple OSS Distributions 				IGI_UNLOCK(igi);
1978*aca3beaaSApple OSS Distributions 				continue;
1979*aca3beaaSApple OSS Distributions 			}
1980*aca3beaaSApple OSS Distributions 			if (igi->igi_v3_timer == 0) {
1981*aca3beaaSApple OSS Distributions 				/* Do nothing. */
1982*aca3beaaSApple OSS Distributions 			} else if (--igi->igi_v3_timer == 0) {
1983*aca3beaaSApple OSS Distributions 				if (igmp_v3_dispatch_general_query(igi) > 0) {
1984*aca3beaaSApple OSS Distributions 					interface_timers_running = 1;
1985*aca3beaaSApple OSS Distributions 				}
1986*aca3beaaSApple OSS Distributions 			} else {
1987*aca3beaaSApple OSS Distributions 				interface_timers_running = 1;
1988*aca3beaaSApple OSS Distributions 			}
1989*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
1990*aca3beaaSApple OSS Distributions 		}
1991*aca3beaaSApple OSS Distributions 	}
1992*aca3beaaSApple OSS Distributions 
1993*aca3beaaSApple OSS Distributions skip_query_timers:
1994*aca3beaaSApple OSS Distributions 	if (!current_state_timers_running &&
1995*aca3beaaSApple OSS Distributions 	    !state_change_timers_running) {
1996*aca3beaaSApple OSS Distributions 		goto out_locked;
1997*aca3beaaSApple OSS Distributions 	}
1998*aca3beaaSApple OSS Distributions 
1999*aca3beaaSApple OSS Distributions 	current_state_timers_running = 0;
2000*aca3beaaSApple OSS Distributions 	state_change_timers_running = 0;
2001*aca3beaaSApple OSS Distributions 
2002*aca3beaaSApple OSS Distributions 	memset(&qrq, 0, sizeof(struct ifqueue));
2003*aca3beaaSApple OSS Distributions 	qrq.ifq_maxlen = IGMP_MAX_G_GS_PACKETS;
2004*aca3beaaSApple OSS Distributions 
2005*aca3beaaSApple OSS Distributions 	memset(&scq, 0, sizeof(struct ifqueue));
2006*aca3beaaSApple OSS Distributions 	scq.ifq_maxlen =  IGMP_MAX_STATE_CHANGE_PACKETS;
2007*aca3beaaSApple OSS Distributions 
2008*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: state change timers running\n", __func__));
2009*aca3beaaSApple OSS Distributions 
2010*aca3beaaSApple OSS Distributions 	/*
2011*aca3beaaSApple OSS Distributions 	 * IGMPv1/v2/v3 host report and state-change timer processing.
2012*aca3beaaSApple OSS Distributions 	 * Note: Processing a v3 group timer may remove a node.
2013*aca3beaaSApple OSS Distributions 	 */
2014*aca3beaaSApple OSS Distributions 	LIST_FOREACH(igi, &igi_head, igi_link) {
2015*aca3beaaSApple OSS Distributions 		struct in_multistep step;
2016*aca3beaaSApple OSS Distributions 
2017*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
2018*aca3beaaSApple OSS Distributions 		ifp = igi->igi_ifp;
2019*aca3beaaSApple OSS Distributions 		loop = (igi->igi_flags & IGIF_LOOPBACK) ? 1 : 0;
2020*aca3beaaSApple OSS Distributions 		uri_sec = IGMP_RANDOM_DELAY(igi->igi_uri);
2021*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
2022*aca3beaaSApple OSS Distributions 
2023*aca3beaaSApple OSS Distributions 		in_multihead_lock_shared();
2024*aca3beaaSApple OSS Distributions 		IN_FIRST_MULTI(step, inm);
2025*aca3beaaSApple OSS Distributions 		while (inm != NULL) {
2026*aca3beaaSApple OSS Distributions 			INM_LOCK(inm);
2027*aca3beaaSApple OSS Distributions 			if (inm->inm_ifp != ifp) {
2028*aca3beaaSApple OSS Distributions 				goto next;
2029*aca3beaaSApple OSS Distributions 			}
2030*aca3beaaSApple OSS Distributions 
2031*aca3beaaSApple OSS Distributions 			IGI_LOCK(igi);
2032*aca3beaaSApple OSS Distributions 			switch (igi->igi_version) {
2033*aca3beaaSApple OSS Distributions 			case IGMP_VERSION_1:
2034*aca3beaaSApple OSS Distributions 			case IGMP_VERSION_2:
2035*aca3beaaSApple OSS Distributions 				igmp_v1v2_process_group_timer(inm,
2036*aca3beaaSApple OSS Distributions 				    igi->igi_version);
2037*aca3beaaSApple OSS Distributions 				break;
2038*aca3beaaSApple OSS Distributions 			case IGMP_VERSION_3:
2039*aca3beaaSApple OSS Distributions 				igmp_v3_process_group_timers(igi, &qrq,
2040*aca3beaaSApple OSS Distributions 				    &scq, inm, uri_sec);
2041*aca3beaaSApple OSS Distributions 				break;
2042*aca3beaaSApple OSS Distributions 			}
2043*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
2044*aca3beaaSApple OSS Distributions next:
2045*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
2046*aca3beaaSApple OSS Distributions 			IN_NEXT_MULTI(step, inm);
2047*aca3beaaSApple OSS Distributions 		}
2048*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
2049*aca3beaaSApple OSS Distributions 
2050*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
2051*aca3beaaSApple OSS Distributions 		if (igi->igi_version == IGMP_VERSION_1 ||
2052*aca3beaaSApple OSS Distributions 		    igi->igi_version == IGMP_VERSION_2) {
2053*aca3beaaSApple OSS Distributions 			igmp_dispatch_queue(igi, &igi->igi_v2q, 0, loop);
2054*aca3beaaSApple OSS Distributions 		} else if (igi->igi_version == IGMP_VERSION_3) {
2055*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
2056*aca3beaaSApple OSS Distributions 			igmp_dispatch_queue(NULL, &qrq, 0, loop);
2057*aca3beaaSApple OSS Distributions 			igmp_dispatch_queue(NULL, &scq, 0, loop);
2058*aca3beaaSApple OSS Distributions 			VERIFY(qrq.ifq_len == 0);
2059*aca3beaaSApple OSS Distributions 			VERIFY(scq.ifq_len == 0);
2060*aca3beaaSApple OSS Distributions 			IGI_LOCK(igi);
2061*aca3beaaSApple OSS Distributions 		}
2062*aca3beaaSApple OSS Distributions 		/*
2063*aca3beaaSApple OSS Distributions 		 * In case there are still any pending membership reports
2064*aca3beaaSApple OSS Distributions 		 * which didn't get drained at version change time.
2065*aca3beaaSApple OSS Distributions 		 */
2066*aca3beaaSApple OSS Distributions 		IF_DRAIN(&igi->igi_v2q);
2067*aca3beaaSApple OSS Distributions 		/*
2068*aca3beaaSApple OSS Distributions 		 * Release all deferred inm records, and drain any locally
2069*aca3beaaSApple OSS Distributions 		 * enqueued packets; do it even if the current IGMP version
2070*aca3beaaSApple OSS Distributions 		 * for the link is no longer IGMPv3, in order to handle the
2071*aca3beaaSApple OSS Distributions 		 * version change case.
2072*aca3beaaSApple OSS Distributions 		 */
2073*aca3beaaSApple OSS Distributions 		igmp_flush_relq(igi, (struct igmp_inm_relhead *)&inm_dthead);
2074*aca3beaaSApple OSS Distributions 		VERIFY(SLIST_EMPTY(&igi->igi_relinmhead));
2075*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
2076*aca3beaaSApple OSS Distributions 
2077*aca3beaaSApple OSS Distributions 		IF_DRAIN(&qrq);
2078*aca3beaaSApple OSS Distributions 		IF_DRAIN(&scq);
2079*aca3beaaSApple OSS Distributions 	}
2080*aca3beaaSApple OSS Distributions 
2081*aca3beaaSApple OSS Distributions out_locked:
2082*aca3beaaSApple OSS Distributions 	/* re-arm the timer if there's work to do */
2083*aca3beaaSApple OSS Distributions 	igmp_timeout_run = 0;
2084*aca3beaaSApple OSS Distributions 	igmp_sched_timeout(false);
2085*aca3beaaSApple OSS Distributions 	IGMP_UNLOCK();
2086*aca3beaaSApple OSS Distributions 
2087*aca3beaaSApple OSS Distributions 	/* Now that we're dropped all locks, release detached records */
2088*aca3beaaSApple OSS Distributions 	IGMP_REMOVE_DETACHED_INM(&inm_dthead);
2089*aca3beaaSApple OSS Distributions }
2090*aca3beaaSApple OSS Distributions 
2091*aca3beaaSApple OSS Distributions static void
igmp_sched_timeout(bool fast)2092*aca3beaaSApple OSS Distributions igmp_sched_timeout(bool fast)
2093*aca3beaaSApple OSS Distributions {
2094*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_HELD();
2095*aca3beaaSApple OSS Distributions 
2096*aca3beaaSApple OSS Distributions 	if (!igmp_timeout_run &&
2097*aca3beaaSApple OSS Distributions 	    (querier_present_timers_running || current_state_timers_running ||
2098*aca3beaaSApple OSS Distributions 	    interface_timers_running || state_change_timers_running)) {
2099*aca3beaaSApple OSS Distributions 		igmp_timeout_run = 1;
2100*aca3beaaSApple OSS Distributions 		int sched_hz = fast ? 0 : hz;
2101*aca3beaaSApple OSS Distributions 		void *arg = fast ? (void *)igmp_sched_timeout : NULL;
2102*aca3beaaSApple OSS Distributions 		timeout(igmp_timeout, arg, sched_hz);
2103*aca3beaaSApple OSS Distributions 	}
2104*aca3beaaSApple OSS Distributions }
2105*aca3beaaSApple OSS Distributions 
2106*aca3beaaSApple OSS Distributions /*
2107*aca3beaaSApple OSS Distributions  * Free the in_multi reference(s) for this IGMP lifecycle.
2108*aca3beaaSApple OSS Distributions  *
2109*aca3beaaSApple OSS Distributions  * Caller must be holding igi_lock.
2110*aca3beaaSApple OSS Distributions  */
2111*aca3beaaSApple OSS Distributions static void
igmp_flush_relq(struct igmp_ifinfo * igi,struct igmp_inm_relhead * inm_dthead)2112*aca3beaaSApple OSS Distributions igmp_flush_relq(struct igmp_ifinfo *igi, struct igmp_inm_relhead *inm_dthead)
2113*aca3beaaSApple OSS Distributions {
2114*aca3beaaSApple OSS Distributions 	struct in_multi *inm;
2115*aca3beaaSApple OSS Distributions 
2116*aca3beaaSApple OSS Distributions again:
2117*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
2118*aca3beaaSApple OSS Distributions 	inm = SLIST_FIRST(&igi->igi_relinmhead);
2119*aca3beaaSApple OSS Distributions 	if (inm != NULL) {
2120*aca3beaaSApple OSS Distributions 		int lastref;
2121*aca3beaaSApple OSS Distributions 
2122*aca3beaaSApple OSS Distributions 		SLIST_REMOVE_HEAD(&igi->igi_relinmhead, inm_nrele);
2123*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
2124*aca3beaaSApple OSS Distributions 
2125*aca3beaaSApple OSS Distributions 		in_multihead_lock_exclusive();
2126*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
2127*aca3beaaSApple OSS Distributions 		VERIFY(inm->inm_nrelecnt != 0);
2128*aca3beaaSApple OSS Distributions 		inm->inm_nrelecnt--;
2129*aca3beaaSApple OSS Distributions 		lastref = in_multi_detach(inm);
2130*aca3beaaSApple OSS Distributions 		VERIFY(!lastref || (!(inm->inm_debug & IFD_ATTACHED) &&
2131*aca3beaaSApple OSS Distributions 		    inm->inm_reqcnt == 0));
2132*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
2133*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
2134*aca3beaaSApple OSS Distributions 		/* from igi_relinmhead */
2135*aca3beaaSApple OSS Distributions 		INM_REMREF(inm);
2136*aca3beaaSApple OSS Distributions 		/* from in_multihead list */
2137*aca3beaaSApple OSS Distributions 		if (lastref) {
2138*aca3beaaSApple OSS Distributions 			/*
2139*aca3beaaSApple OSS Distributions 			 * Defer releasing our final reference, as we
2140*aca3beaaSApple OSS Distributions 			 * are holding the IGMP lock at this point, and
2141*aca3beaaSApple OSS Distributions 			 * we could end up with locking issues later on
2142*aca3beaaSApple OSS Distributions 			 * (while issuing SIOCDELMULTI) when this is the
2143*aca3beaaSApple OSS Distributions 			 * final reference count.  Let the caller do it
2144*aca3beaaSApple OSS Distributions 			 * when it is safe.
2145*aca3beaaSApple OSS Distributions 			 */
2146*aca3beaaSApple OSS Distributions 			IGMP_ADD_DETACHED_INM(inm_dthead, inm);
2147*aca3beaaSApple OSS Distributions 		}
2148*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
2149*aca3beaaSApple OSS Distributions 		goto again;
2150*aca3beaaSApple OSS Distributions 	}
2151*aca3beaaSApple OSS Distributions }
2152*aca3beaaSApple OSS Distributions 
2153*aca3beaaSApple OSS Distributions /*
2154*aca3beaaSApple OSS Distributions  * Update host report group timer for IGMPv1/v2.
2155*aca3beaaSApple OSS Distributions  * Will update the global pending timer flags.
2156*aca3beaaSApple OSS Distributions  */
2157*aca3beaaSApple OSS Distributions static void
igmp_v1v2_process_group_timer(struct in_multi * inm,const int igmp_version)2158*aca3beaaSApple OSS Distributions igmp_v1v2_process_group_timer(struct in_multi *inm, const int igmp_version)
2159*aca3beaaSApple OSS Distributions {
2160*aca3beaaSApple OSS Distributions 	int report_timer_expired;
2161*aca3beaaSApple OSS Distributions 
2162*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_HELD();
2163*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2164*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(inm->inm_igi);
2165*aca3beaaSApple OSS Distributions 
2166*aca3beaaSApple OSS Distributions 	if (inm->inm_timer == 0) {
2167*aca3beaaSApple OSS Distributions 		report_timer_expired = 0;
2168*aca3beaaSApple OSS Distributions 	} else if (--inm->inm_timer == 0) {
2169*aca3beaaSApple OSS Distributions 		report_timer_expired = 1;
2170*aca3beaaSApple OSS Distributions 	} else {
2171*aca3beaaSApple OSS Distributions 		current_state_timers_running = 1;
2172*aca3beaaSApple OSS Distributions 		/* caller will schedule timer */
2173*aca3beaaSApple OSS Distributions 		return;
2174*aca3beaaSApple OSS Distributions 	}
2175*aca3beaaSApple OSS Distributions 
2176*aca3beaaSApple OSS Distributions 	switch (inm->inm_state) {
2177*aca3beaaSApple OSS Distributions 	case IGMP_NOT_MEMBER:
2178*aca3beaaSApple OSS Distributions 	case IGMP_SILENT_MEMBER:
2179*aca3beaaSApple OSS Distributions 	case IGMP_IDLE_MEMBER:
2180*aca3beaaSApple OSS Distributions 	case IGMP_LAZY_MEMBER:
2181*aca3beaaSApple OSS Distributions 	case IGMP_SLEEPING_MEMBER:
2182*aca3beaaSApple OSS Distributions 	case IGMP_AWAKENING_MEMBER:
2183*aca3beaaSApple OSS Distributions 		break;
2184*aca3beaaSApple OSS Distributions 	case IGMP_REPORTING_MEMBER:
2185*aca3beaaSApple OSS Distributions 		if (report_timer_expired) {
2186*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_IDLE_MEMBER;
2187*aca3beaaSApple OSS Distributions 			(void) igmp_v1v2_queue_report(inm,
2188*aca3beaaSApple OSS Distributions 			    (igmp_version == IGMP_VERSION_2) ?
2189*aca3beaaSApple OSS Distributions 			    IGMP_v2_HOST_MEMBERSHIP_REPORT :
2190*aca3beaaSApple OSS Distributions 			    IGMP_v1_HOST_MEMBERSHIP_REPORT);
2191*aca3beaaSApple OSS Distributions 			INM_LOCK_ASSERT_HELD(inm);
2192*aca3beaaSApple OSS Distributions 			IGI_LOCK_ASSERT_HELD(inm->inm_igi);
2193*aca3beaaSApple OSS Distributions 		}
2194*aca3beaaSApple OSS Distributions 		break;
2195*aca3beaaSApple OSS Distributions 	case IGMP_G_QUERY_PENDING_MEMBER:
2196*aca3beaaSApple OSS Distributions 	case IGMP_SG_QUERY_PENDING_MEMBER:
2197*aca3beaaSApple OSS Distributions 	case IGMP_LEAVING_MEMBER:
2198*aca3beaaSApple OSS Distributions 		break;
2199*aca3beaaSApple OSS Distributions 	}
2200*aca3beaaSApple OSS Distributions }
2201*aca3beaaSApple OSS Distributions 
2202*aca3beaaSApple OSS Distributions /*
2203*aca3beaaSApple OSS Distributions  * Update a group's timers for IGMPv3.
2204*aca3beaaSApple OSS Distributions  * Will update the global pending timer flags.
2205*aca3beaaSApple OSS Distributions  * Note: Unlocked read from igi.
2206*aca3beaaSApple OSS Distributions  */
2207*aca3beaaSApple OSS Distributions static void
igmp_v3_process_group_timers(struct igmp_ifinfo * igi,struct ifqueue * qrq,struct ifqueue * scq,struct in_multi * inm,const unsigned int uri_sec)2208*aca3beaaSApple OSS Distributions igmp_v3_process_group_timers(struct igmp_ifinfo *igi,
2209*aca3beaaSApple OSS Distributions     struct ifqueue *qrq, struct ifqueue *scq,
2210*aca3beaaSApple OSS Distributions     struct in_multi *inm, const unsigned int uri_sec)
2211*aca3beaaSApple OSS Distributions {
2212*aca3beaaSApple OSS Distributions 	int query_response_timer_expired;
2213*aca3beaaSApple OSS Distributions 	int state_change_retransmit_timer_expired;
2214*aca3beaaSApple OSS Distributions 
2215*aca3beaaSApple OSS Distributions 	IGMP_LOCK_ASSERT_HELD();
2216*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2217*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
2218*aca3beaaSApple OSS Distributions 	VERIFY(igi == inm->inm_igi);
2219*aca3beaaSApple OSS Distributions 
2220*aca3beaaSApple OSS Distributions 	query_response_timer_expired = 0;
2221*aca3beaaSApple OSS Distributions 	state_change_retransmit_timer_expired = 0;
2222*aca3beaaSApple OSS Distributions 
2223*aca3beaaSApple OSS Distributions 	/*
2224*aca3beaaSApple OSS Distributions 	 * During a transition from v1/v2 compatibility mode back to v3,
2225*aca3beaaSApple OSS Distributions 	 * a group record in REPORTING state may still have its group
2226*aca3beaaSApple OSS Distributions 	 * timer active. This is a no-op in this function; it is easier
2227*aca3beaaSApple OSS Distributions 	 * to deal with it here than to complicate the timeout path.
2228*aca3beaaSApple OSS Distributions 	 */
2229*aca3beaaSApple OSS Distributions 	if (inm->inm_timer == 0) {
2230*aca3beaaSApple OSS Distributions 		query_response_timer_expired = 0;
2231*aca3beaaSApple OSS Distributions 	} else if (--inm->inm_timer == 0) {
2232*aca3beaaSApple OSS Distributions 		query_response_timer_expired = 1;
2233*aca3beaaSApple OSS Distributions 	} else {
2234*aca3beaaSApple OSS Distributions 		current_state_timers_running = 1;
2235*aca3beaaSApple OSS Distributions 		/* caller will schedule timer */
2236*aca3beaaSApple OSS Distributions 	}
2237*aca3beaaSApple OSS Distributions 
2238*aca3beaaSApple OSS Distributions 	if (inm->inm_sctimer == 0) {
2239*aca3beaaSApple OSS Distributions 		state_change_retransmit_timer_expired = 0;
2240*aca3beaaSApple OSS Distributions 	} else if (--inm->inm_sctimer == 0) {
2241*aca3beaaSApple OSS Distributions 		state_change_retransmit_timer_expired = 1;
2242*aca3beaaSApple OSS Distributions 	} else {
2243*aca3beaaSApple OSS Distributions 		state_change_timers_running = 1;
2244*aca3beaaSApple OSS Distributions 		/* caller will schedule timer */
2245*aca3beaaSApple OSS Distributions 	}
2246*aca3beaaSApple OSS Distributions 
2247*aca3beaaSApple OSS Distributions 	/* We are in timer callback, so be quick about it. */
2248*aca3beaaSApple OSS Distributions 	if (!state_change_retransmit_timer_expired &&
2249*aca3beaaSApple OSS Distributions 	    !query_response_timer_expired) {
2250*aca3beaaSApple OSS Distributions 		return;
2251*aca3beaaSApple OSS Distributions 	}
2252*aca3beaaSApple OSS Distributions 
2253*aca3beaaSApple OSS Distributions 	switch (inm->inm_state) {
2254*aca3beaaSApple OSS Distributions 	case IGMP_NOT_MEMBER:
2255*aca3beaaSApple OSS Distributions 	case IGMP_SILENT_MEMBER:
2256*aca3beaaSApple OSS Distributions 	case IGMP_SLEEPING_MEMBER:
2257*aca3beaaSApple OSS Distributions 	case IGMP_LAZY_MEMBER:
2258*aca3beaaSApple OSS Distributions 	case IGMP_AWAKENING_MEMBER:
2259*aca3beaaSApple OSS Distributions 	case IGMP_IDLE_MEMBER:
2260*aca3beaaSApple OSS Distributions 		break;
2261*aca3beaaSApple OSS Distributions 	case IGMP_G_QUERY_PENDING_MEMBER:
2262*aca3beaaSApple OSS Distributions 	case IGMP_SG_QUERY_PENDING_MEMBER:
2263*aca3beaaSApple OSS Distributions 		/*
2264*aca3beaaSApple OSS Distributions 		 * Respond to a previously pending Group-Specific
2265*aca3beaaSApple OSS Distributions 		 * or Group-and-Source-Specific query by enqueueing
2266*aca3beaaSApple OSS Distributions 		 * the appropriate Current-State report for
2267*aca3beaaSApple OSS Distributions 		 * immediate transmission.
2268*aca3beaaSApple OSS Distributions 		 */
2269*aca3beaaSApple OSS Distributions 		if (query_response_timer_expired) {
2270*aca3beaaSApple OSS Distributions 			int retval;
2271*aca3beaaSApple OSS Distributions 
2272*aca3beaaSApple OSS Distributions 			retval = igmp_v3_enqueue_group_record(qrq, inm, 0, 1,
2273*aca3beaaSApple OSS Distributions 			    (inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER));
2274*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: enqueue record = %d\n",
2275*aca3beaaSApple OSS Distributions 			    __func__, retval));
2276*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_REPORTING_MEMBER;
2277*aca3beaaSApple OSS Distributions 			/* XXX Clear recorded sources for next time. */
2278*aca3beaaSApple OSS Distributions 			inm_clear_recorded(inm);
2279*aca3beaaSApple OSS Distributions 		}
2280*aca3beaaSApple OSS Distributions 		OS_FALLTHROUGH;
2281*aca3beaaSApple OSS Distributions 	case IGMP_REPORTING_MEMBER:
2282*aca3beaaSApple OSS Distributions 	case IGMP_LEAVING_MEMBER:
2283*aca3beaaSApple OSS Distributions 		if (state_change_retransmit_timer_expired) {
2284*aca3beaaSApple OSS Distributions 			/*
2285*aca3beaaSApple OSS Distributions 			 * State-change retransmission timer fired.
2286*aca3beaaSApple OSS Distributions 			 * If there are any further pending retransmissions,
2287*aca3beaaSApple OSS Distributions 			 * set the global pending state-change flag, and
2288*aca3beaaSApple OSS Distributions 			 * reset the timer.
2289*aca3beaaSApple OSS Distributions 			 */
2290*aca3beaaSApple OSS Distributions 			if (--inm->inm_scrv > 0) {
2291*aca3beaaSApple OSS Distributions 				inm->inm_sctimer = (uint16_t)uri_sec;
2292*aca3beaaSApple OSS Distributions 				state_change_timers_running = 1;
2293*aca3beaaSApple OSS Distributions 				/* caller will schedule timer */
2294*aca3beaaSApple OSS Distributions 			}
2295*aca3beaaSApple OSS Distributions 			/*
2296*aca3beaaSApple OSS Distributions 			 * Retransmit the previously computed state-change
2297*aca3beaaSApple OSS Distributions 			 * report. If there are no further pending
2298*aca3beaaSApple OSS Distributions 			 * retransmissions, the mbuf queue will be consumed.
2299*aca3beaaSApple OSS Distributions 			 * Update T0 state to T1 as we have now sent
2300*aca3beaaSApple OSS Distributions 			 * a state-change.
2301*aca3beaaSApple OSS Distributions 			 */
2302*aca3beaaSApple OSS Distributions 			(void) igmp_v3_merge_state_changes(inm, scq);
2303*aca3beaaSApple OSS Distributions 
2304*aca3beaaSApple OSS Distributions 			inm_commit(inm);
2305*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(inm->inm_addr,
2306*aca3beaaSApple OSS Distributions 			    ("%s: T1 -> T0 for %s/%s\n", __func__,
2307*aca3beaaSApple OSS Distributions 			    _igmp_inet_buf, if_name(inm->inm_ifp)));
2308*aca3beaaSApple OSS Distributions 
2309*aca3beaaSApple OSS Distributions 			/*
2310*aca3beaaSApple OSS Distributions 			 * If we are leaving the group for good, make sure
2311*aca3beaaSApple OSS Distributions 			 * we release IGMP's reference to it.
2312*aca3beaaSApple OSS Distributions 			 * This release must be deferred using a SLIST,
2313*aca3beaaSApple OSS Distributions 			 * as we are called from a loop which traverses
2314*aca3beaaSApple OSS Distributions 			 * the in_multihead list.
2315*aca3beaaSApple OSS Distributions 			 */
2316*aca3beaaSApple OSS Distributions 			if (inm->inm_state == IGMP_LEAVING_MEMBER &&
2317*aca3beaaSApple OSS Distributions 			    inm->inm_scrv == 0) {
2318*aca3beaaSApple OSS Distributions 				inm->inm_state = IGMP_NOT_MEMBER;
2319*aca3beaaSApple OSS Distributions 				/*
2320*aca3beaaSApple OSS Distributions 				 * A reference has already been held in
2321*aca3beaaSApple OSS Distributions 				 * igmp_final_leave() for this inm, so
2322*aca3beaaSApple OSS Distributions 				 * no need to hold another one.  We also
2323*aca3beaaSApple OSS Distributions 				 * bumped up its request count then, so
2324*aca3beaaSApple OSS Distributions 				 * that it stays in in_multihead.  Both
2325*aca3beaaSApple OSS Distributions 				 * of them will be released when it is
2326*aca3beaaSApple OSS Distributions 				 * dequeued later on.
2327*aca3beaaSApple OSS Distributions 				 */
2328*aca3beaaSApple OSS Distributions 				VERIFY(inm->inm_nrelecnt != 0);
2329*aca3beaaSApple OSS Distributions 				SLIST_INSERT_HEAD(&igi->igi_relinmhead,
2330*aca3beaaSApple OSS Distributions 				    inm, inm_nrele);
2331*aca3beaaSApple OSS Distributions 			}
2332*aca3beaaSApple OSS Distributions 		}
2333*aca3beaaSApple OSS Distributions 		break;
2334*aca3beaaSApple OSS Distributions 	}
2335*aca3beaaSApple OSS Distributions }
2336*aca3beaaSApple OSS Distributions 
2337*aca3beaaSApple OSS Distributions /*
2338*aca3beaaSApple OSS Distributions  * Suppress a group's pending response to a group or source/group query.
2339*aca3beaaSApple OSS Distributions  *
2340*aca3beaaSApple OSS Distributions  * Do NOT suppress state changes. This leads to IGMPv3 inconsistency.
2341*aca3beaaSApple OSS Distributions  * Do NOT update ST1/ST0 as this operation merely suppresses
2342*aca3beaaSApple OSS Distributions  * the currently pending group record.
2343*aca3beaaSApple OSS Distributions  * Do NOT suppress the response to a general query. It is possible but
2344*aca3beaaSApple OSS Distributions  * it would require adding another state or flag.
2345*aca3beaaSApple OSS Distributions  */
2346*aca3beaaSApple OSS Distributions static void
igmp_v3_suppress_group_record(struct in_multi * inm)2347*aca3beaaSApple OSS Distributions igmp_v3_suppress_group_record(struct in_multi *inm)
2348*aca3beaaSApple OSS Distributions {
2349*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2350*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(inm->inm_igi);
2351*aca3beaaSApple OSS Distributions 
2352*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_igi->igi_version == IGMP_VERSION_3);
2353*aca3beaaSApple OSS Distributions 
2354*aca3beaaSApple OSS Distributions 	if (inm->inm_state != IGMP_G_QUERY_PENDING_MEMBER ||
2355*aca3beaaSApple OSS Distributions 	    inm->inm_state != IGMP_SG_QUERY_PENDING_MEMBER) {
2356*aca3beaaSApple OSS Distributions 		return;
2357*aca3beaaSApple OSS Distributions 	}
2358*aca3beaaSApple OSS Distributions 
2359*aca3beaaSApple OSS Distributions 	if (inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER) {
2360*aca3beaaSApple OSS Distributions 		inm_clear_recorded(inm);
2361*aca3beaaSApple OSS Distributions 	}
2362*aca3beaaSApple OSS Distributions 
2363*aca3beaaSApple OSS Distributions 	inm->inm_timer = 0;
2364*aca3beaaSApple OSS Distributions 	inm->inm_state = IGMP_REPORTING_MEMBER;
2365*aca3beaaSApple OSS Distributions }
2366*aca3beaaSApple OSS Distributions 
2367*aca3beaaSApple OSS Distributions /*
2368*aca3beaaSApple OSS Distributions  * Switch to a different IGMP version on the given interface,
2369*aca3beaaSApple OSS Distributions  * as per Section 7.2.1.
2370*aca3beaaSApple OSS Distributions  */
2371*aca3beaaSApple OSS Distributions static uint32_t
igmp_set_version(struct igmp_ifinfo * igi,const int igmp_version)2372*aca3beaaSApple OSS Distributions igmp_set_version(struct igmp_ifinfo *igi, const int igmp_version)
2373*aca3beaaSApple OSS Distributions {
2374*aca3beaaSApple OSS Distributions 	int old_version_timer;
2375*aca3beaaSApple OSS Distributions 
2376*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
2377*aca3beaaSApple OSS Distributions 
2378*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: switching to v%d on ifp 0x%llx(%s)\n", __func__,
2379*aca3beaaSApple OSS Distributions 	    igmp_version, (uint64_t)VM_KERNEL_ADDRPERM(igi->igi_ifp),
2380*aca3beaaSApple OSS Distributions 	    if_name(igi->igi_ifp)));
2381*aca3beaaSApple OSS Distributions 
2382*aca3beaaSApple OSS Distributions 	if (igmp_version == IGMP_VERSION_1 || igmp_version == IGMP_VERSION_2) {
2383*aca3beaaSApple OSS Distributions 		/*
2384*aca3beaaSApple OSS Distributions 		 * Compute the "Older Version Querier Present" timer as per
2385*aca3beaaSApple OSS Distributions 		 * Section 8.12, in seconds.
2386*aca3beaaSApple OSS Distributions 		 */
2387*aca3beaaSApple OSS Distributions 		old_version_timer = igi->igi_rv * igi->igi_qi + igi->igi_qri;
2388*aca3beaaSApple OSS Distributions 
2389*aca3beaaSApple OSS Distributions 		if (igmp_version == IGMP_VERSION_1) {
2390*aca3beaaSApple OSS Distributions 			igi->igi_v1_timer = old_version_timer;
2391*aca3beaaSApple OSS Distributions 			igi->igi_v2_timer = 0;
2392*aca3beaaSApple OSS Distributions 		} else if (igmp_version == IGMP_VERSION_2) {
2393*aca3beaaSApple OSS Distributions 			igi->igi_v1_timer = 0;
2394*aca3beaaSApple OSS Distributions 			igi->igi_v2_timer = old_version_timer;
2395*aca3beaaSApple OSS Distributions 		}
2396*aca3beaaSApple OSS Distributions 	}
2397*aca3beaaSApple OSS Distributions 
2398*aca3beaaSApple OSS Distributions 	if (igi->igi_v1_timer == 0 && igi->igi_v2_timer > 0) {
2399*aca3beaaSApple OSS Distributions 		if (igi->igi_version != IGMP_VERSION_2) {
2400*aca3beaaSApple OSS Distributions 			igmp_v3_cancel_link_timers(igi);
2401*aca3beaaSApple OSS Distributions 			igi->igi_version = IGMP_VERSION_2;
2402*aca3beaaSApple OSS Distributions 		}
2403*aca3beaaSApple OSS Distributions 	} else if (igi->igi_v1_timer > 0) {
2404*aca3beaaSApple OSS Distributions 		if (igi->igi_version != IGMP_VERSION_1) {
2405*aca3beaaSApple OSS Distributions 			igmp_v3_cancel_link_timers(igi);
2406*aca3beaaSApple OSS Distributions 			igi->igi_version = IGMP_VERSION_1;
2407*aca3beaaSApple OSS Distributions 		}
2408*aca3beaaSApple OSS Distributions 	}
2409*aca3beaaSApple OSS Distributions 
2410*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
2411*aca3beaaSApple OSS Distributions 
2412*aca3beaaSApple OSS Distributions 	return MAX(igi->igi_v1_timer, igi->igi_v2_timer);
2413*aca3beaaSApple OSS Distributions }
2414*aca3beaaSApple OSS Distributions 
2415*aca3beaaSApple OSS Distributions /*
2416*aca3beaaSApple OSS Distributions  * Cancel pending IGMPv3 timers for the given link and all groups
2417*aca3beaaSApple OSS Distributions  * joined on it; state-change, general-query, and group-query timers.
2418*aca3beaaSApple OSS Distributions  *
2419*aca3beaaSApple OSS Distributions  * Only ever called on a transition from v3 to Compatibility mode. Kill
2420*aca3beaaSApple OSS Distributions  * the timers stone dead (this may be expensive for large N groups), they
2421*aca3beaaSApple OSS Distributions  * will be restarted if Compatibility Mode deems that they must be due to
2422*aca3beaaSApple OSS Distributions  * query processing.
2423*aca3beaaSApple OSS Distributions  */
2424*aca3beaaSApple OSS Distributions static void
igmp_v3_cancel_link_timers(struct igmp_ifinfo * igi)2425*aca3beaaSApple OSS Distributions igmp_v3_cancel_link_timers(struct igmp_ifinfo *igi)
2426*aca3beaaSApple OSS Distributions {
2427*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
2428*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
2429*aca3beaaSApple OSS Distributions 	struct in_multistep     step;
2430*aca3beaaSApple OSS Distributions 
2431*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
2432*aca3beaaSApple OSS Distributions 
2433*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: cancel v3 timers on ifp 0x%llx(%s)\n", __func__,
2434*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(igi->igi_ifp), if_name(igi->igi_ifp)));
2435*aca3beaaSApple OSS Distributions 
2436*aca3beaaSApple OSS Distributions 	/*
2437*aca3beaaSApple OSS Distributions 	 * Stop the v3 General Query Response on this link stone dead.
2438*aca3beaaSApple OSS Distributions 	 * If timer is woken up due to interface_timers_running,
2439*aca3beaaSApple OSS Distributions 	 * the flag will be cleared if there are no pending link timers.
2440*aca3beaaSApple OSS Distributions 	 */
2441*aca3beaaSApple OSS Distributions 	igi->igi_v3_timer = 0;
2442*aca3beaaSApple OSS Distributions 
2443*aca3beaaSApple OSS Distributions 	/*
2444*aca3beaaSApple OSS Distributions 	 * Now clear the current-state and state-change report timers
2445*aca3beaaSApple OSS Distributions 	 * for all memberships scoped to this link.
2446*aca3beaaSApple OSS Distributions 	 */
2447*aca3beaaSApple OSS Distributions 	ifp = igi->igi_ifp;
2448*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
2449*aca3beaaSApple OSS Distributions 
2450*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
2451*aca3beaaSApple OSS Distributions 	IN_FIRST_MULTI(step, inm);
2452*aca3beaaSApple OSS Distributions 	while (inm != NULL) {
2453*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
2454*aca3beaaSApple OSS Distributions 		if (inm->inm_ifp != ifp && inm->inm_igi != igi) {
2455*aca3beaaSApple OSS Distributions 			goto next;
2456*aca3beaaSApple OSS Distributions 		}
2457*aca3beaaSApple OSS Distributions 
2458*aca3beaaSApple OSS Distributions 		switch (inm->inm_state) {
2459*aca3beaaSApple OSS Distributions 		case IGMP_NOT_MEMBER:
2460*aca3beaaSApple OSS Distributions 		case IGMP_SILENT_MEMBER:
2461*aca3beaaSApple OSS Distributions 		case IGMP_IDLE_MEMBER:
2462*aca3beaaSApple OSS Distributions 		case IGMP_LAZY_MEMBER:
2463*aca3beaaSApple OSS Distributions 		case IGMP_SLEEPING_MEMBER:
2464*aca3beaaSApple OSS Distributions 		case IGMP_AWAKENING_MEMBER:
2465*aca3beaaSApple OSS Distributions 			/*
2466*aca3beaaSApple OSS Distributions 			 * These states are either not relevant in v3 mode,
2467*aca3beaaSApple OSS Distributions 			 * or are unreported. Do nothing.
2468*aca3beaaSApple OSS Distributions 			 */
2469*aca3beaaSApple OSS Distributions 			break;
2470*aca3beaaSApple OSS Distributions 		case IGMP_LEAVING_MEMBER:
2471*aca3beaaSApple OSS Distributions 			/*
2472*aca3beaaSApple OSS Distributions 			 * If we are leaving the group and switching to
2473*aca3beaaSApple OSS Distributions 			 * compatibility mode, we need to release the final
2474*aca3beaaSApple OSS Distributions 			 * reference held for issuing the INCLUDE {}, and
2475*aca3beaaSApple OSS Distributions 			 * transition to REPORTING to ensure the host leave
2476*aca3beaaSApple OSS Distributions 			 * message is sent upstream to the old querier --
2477*aca3beaaSApple OSS Distributions 			 * transition to NOT would lose the leave and race.
2478*aca3beaaSApple OSS Distributions 			 * During igmp_final_leave(), we bumped up both the
2479*aca3beaaSApple OSS Distributions 			 * request and reference counts.  Since we cannot
2480*aca3beaaSApple OSS Distributions 			 * call in_multi_detach() here, defer this task to
2481*aca3beaaSApple OSS Distributions 			 * the timer routine.
2482*aca3beaaSApple OSS Distributions 			 */
2483*aca3beaaSApple OSS Distributions 			VERIFY(inm->inm_nrelecnt != 0);
2484*aca3beaaSApple OSS Distributions 			IGI_LOCK(igi);
2485*aca3beaaSApple OSS Distributions 			SLIST_INSERT_HEAD(&igi->igi_relinmhead, inm, inm_nrele);
2486*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
2487*aca3beaaSApple OSS Distributions 			OS_FALLTHROUGH;
2488*aca3beaaSApple OSS Distributions 		case IGMP_G_QUERY_PENDING_MEMBER:
2489*aca3beaaSApple OSS Distributions 		case IGMP_SG_QUERY_PENDING_MEMBER:
2490*aca3beaaSApple OSS Distributions 			inm_clear_recorded(inm);
2491*aca3beaaSApple OSS Distributions 			OS_FALLTHROUGH;
2492*aca3beaaSApple OSS Distributions 		case IGMP_REPORTING_MEMBER:
2493*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_REPORTING_MEMBER;
2494*aca3beaaSApple OSS Distributions 			break;
2495*aca3beaaSApple OSS Distributions 		}
2496*aca3beaaSApple OSS Distributions 		/*
2497*aca3beaaSApple OSS Distributions 		 * Always clear state-change and group report timers.
2498*aca3beaaSApple OSS Distributions 		 * Free any pending IGMPv3 state-change records.
2499*aca3beaaSApple OSS Distributions 		 */
2500*aca3beaaSApple OSS Distributions 		inm->inm_sctimer = 0;
2501*aca3beaaSApple OSS Distributions 		inm->inm_timer = 0;
2502*aca3beaaSApple OSS Distributions 		IF_DRAIN(&inm->inm_scq);
2503*aca3beaaSApple OSS Distributions next:
2504*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
2505*aca3beaaSApple OSS Distributions 		IN_NEXT_MULTI(step, inm);
2506*aca3beaaSApple OSS Distributions 	}
2507*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
2508*aca3beaaSApple OSS Distributions 
2509*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
2510*aca3beaaSApple OSS Distributions }
2511*aca3beaaSApple OSS Distributions 
2512*aca3beaaSApple OSS Distributions /*
2513*aca3beaaSApple OSS Distributions  * Update the Older Version Querier Present timers for a link.
2514*aca3beaaSApple OSS Distributions  * See Section 7.2.1 of RFC 3376.
2515*aca3beaaSApple OSS Distributions  */
2516*aca3beaaSApple OSS Distributions static void
igmp_v1v2_process_querier_timers(struct igmp_ifinfo * igi)2517*aca3beaaSApple OSS Distributions igmp_v1v2_process_querier_timers(struct igmp_ifinfo *igi)
2518*aca3beaaSApple OSS Distributions {
2519*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
2520*aca3beaaSApple OSS Distributions 
2521*aca3beaaSApple OSS Distributions 	if (igi->igi_v1_timer == 0 && igi->igi_v2_timer == 0) {
2522*aca3beaaSApple OSS Distributions 		/*
2523*aca3beaaSApple OSS Distributions 		 * IGMPv1 and IGMPv2 Querier Present timers expired.
2524*aca3beaaSApple OSS Distributions 		 *
2525*aca3beaaSApple OSS Distributions 		 * Revert to IGMPv3.
2526*aca3beaaSApple OSS Distributions 		 */
2527*aca3beaaSApple OSS Distributions 		if (igi->igi_version != IGMP_VERSION_3) {
2528*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: transition from v%d -> v%d "
2529*aca3beaaSApple OSS Distributions 			    "on 0x%llx(%s)\n", __func__,
2530*aca3beaaSApple OSS Distributions 			    igi->igi_version, IGMP_VERSION_3,
2531*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(igi->igi_ifp),
2532*aca3beaaSApple OSS Distributions 			    if_name(igi->igi_ifp)));
2533*aca3beaaSApple OSS Distributions 			igi->igi_version = IGMP_VERSION_3;
2534*aca3beaaSApple OSS Distributions 			IF_DRAIN(&igi->igi_v2q);
2535*aca3beaaSApple OSS Distributions 		}
2536*aca3beaaSApple OSS Distributions 	} else if (igi->igi_v1_timer == 0 && igi->igi_v2_timer > 0) {
2537*aca3beaaSApple OSS Distributions 		/*
2538*aca3beaaSApple OSS Distributions 		 * IGMPv1 Querier Present timer expired,
2539*aca3beaaSApple OSS Distributions 		 * IGMPv2 Querier Present timer running.
2540*aca3beaaSApple OSS Distributions 		 * If IGMPv2 was disabled since last timeout,
2541*aca3beaaSApple OSS Distributions 		 * revert to IGMPv3.
2542*aca3beaaSApple OSS Distributions 		 * If IGMPv2 is enabled, revert to IGMPv2.
2543*aca3beaaSApple OSS Distributions 		 */
2544*aca3beaaSApple OSS Distributions 		if (!igmp_v2enable) {
2545*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: transition from v%d -> v%d "
2546*aca3beaaSApple OSS Distributions 			    "on 0x%llx(%s%d)\n", __func__,
2547*aca3beaaSApple OSS Distributions 			    igi->igi_version, IGMP_VERSION_3,
2548*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(igi->igi_ifp),
2549*aca3beaaSApple OSS Distributions 			    igi->igi_ifp->if_name, igi->igi_ifp->if_unit));
2550*aca3beaaSApple OSS Distributions 			igi->igi_v2_timer = 0;
2551*aca3beaaSApple OSS Distributions 			igi->igi_version = IGMP_VERSION_3;
2552*aca3beaaSApple OSS Distributions 			IF_DRAIN(&igi->igi_v2q);
2553*aca3beaaSApple OSS Distributions 		} else {
2554*aca3beaaSApple OSS Distributions 			--igi->igi_v2_timer;
2555*aca3beaaSApple OSS Distributions 			if (igi->igi_version != IGMP_VERSION_2) {
2556*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: transition from v%d -> v%d "
2557*aca3beaaSApple OSS Distributions 				    "on 0x%llx(%s)\n", __func__,
2558*aca3beaaSApple OSS Distributions 				    igi->igi_version, IGMP_VERSION_2,
2559*aca3beaaSApple OSS Distributions 				    (uint64_t)VM_KERNEL_ADDRPERM(igi->igi_ifp),
2560*aca3beaaSApple OSS Distributions 				    if_name(igi->igi_ifp)));
2561*aca3beaaSApple OSS Distributions 				IF_DRAIN(&igi->igi_gq);
2562*aca3beaaSApple OSS Distributions 				igmp_v3_cancel_link_timers(igi);
2563*aca3beaaSApple OSS Distributions 				igi->igi_version = IGMP_VERSION_2;
2564*aca3beaaSApple OSS Distributions 			}
2565*aca3beaaSApple OSS Distributions 		}
2566*aca3beaaSApple OSS Distributions 	} else if (igi->igi_v1_timer > 0) {
2567*aca3beaaSApple OSS Distributions 		/*
2568*aca3beaaSApple OSS Distributions 		 * IGMPv1 Querier Present timer running.
2569*aca3beaaSApple OSS Distributions 		 * Stop IGMPv2 timer if running.
2570*aca3beaaSApple OSS Distributions 		 *
2571*aca3beaaSApple OSS Distributions 		 * If IGMPv1 was disabled since last timeout,
2572*aca3beaaSApple OSS Distributions 		 * revert to IGMPv3.
2573*aca3beaaSApple OSS Distributions 		 * If IGMPv1 is enabled, reset IGMPv2 timer if running.
2574*aca3beaaSApple OSS Distributions 		 */
2575*aca3beaaSApple OSS Distributions 		if (!igmp_v1enable) {
2576*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: transition from v%d -> v%d "
2577*aca3beaaSApple OSS Distributions 			    "on 0x%llx(%s%d)\n", __func__,
2578*aca3beaaSApple OSS Distributions 			    igi->igi_version, IGMP_VERSION_3,
2579*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(igi->igi_ifp),
2580*aca3beaaSApple OSS Distributions 			    igi->igi_ifp->if_name, igi->igi_ifp->if_unit));
2581*aca3beaaSApple OSS Distributions 			igi->igi_v1_timer = 0;
2582*aca3beaaSApple OSS Distributions 			igi->igi_version = IGMP_VERSION_3;
2583*aca3beaaSApple OSS Distributions 			IF_DRAIN(&igi->igi_v2q);
2584*aca3beaaSApple OSS Distributions 		} else {
2585*aca3beaaSApple OSS Distributions 			--igi->igi_v1_timer;
2586*aca3beaaSApple OSS Distributions 		}
2587*aca3beaaSApple OSS Distributions 		if (igi->igi_v2_timer > 0) {
2588*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: cancel v2 timer on 0x%llx(%s%d)\n",
2589*aca3beaaSApple OSS Distributions 			    __func__,
2590*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(igi->igi_ifp),
2591*aca3beaaSApple OSS Distributions 			    igi->igi_ifp->if_name, igi->igi_ifp->if_unit));
2592*aca3beaaSApple OSS Distributions 			igi->igi_v2_timer = 0;
2593*aca3beaaSApple OSS Distributions 		}
2594*aca3beaaSApple OSS Distributions 	}
2595*aca3beaaSApple OSS Distributions }
2596*aca3beaaSApple OSS Distributions 
2597*aca3beaaSApple OSS Distributions /*
2598*aca3beaaSApple OSS Distributions  * Dispatch an IGMPv1/v2 host report or leave message.
2599*aca3beaaSApple OSS Distributions  * These are always small enough to fit inside a single mbuf.
2600*aca3beaaSApple OSS Distributions  */
2601*aca3beaaSApple OSS Distributions static int
igmp_v1v2_queue_report(struct in_multi * inm,const int type)2602*aca3beaaSApple OSS Distributions igmp_v1v2_queue_report(struct in_multi *inm, const int type)
2603*aca3beaaSApple OSS Distributions {
2604*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
2605*aca3beaaSApple OSS Distributions 	struct igmp             *igmp;
2606*aca3beaaSApple OSS Distributions 	struct ip               *ip;
2607*aca3beaaSApple OSS Distributions 	struct mbuf             *m;
2608*aca3beaaSApple OSS Distributions 	int                     error = 0;
2609*aca3beaaSApple OSS Distributions 
2610*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2611*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(inm->inm_igi);
2612*aca3beaaSApple OSS Distributions 
2613*aca3beaaSApple OSS Distributions 	ifp = inm->inm_ifp;
2614*aca3beaaSApple OSS Distributions 
2615*aca3beaaSApple OSS Distributions 	MGETHDR(m, M_DONTWAIT, MT_DATA);
2616*aca3beaaSApple OSS Distributions 	if (m == NULL) {
2617*aca3beaaSApple OSS Distributions 		return ENOMEM;
2618*aca3beaaSApple OSS Distributions 	}
2619*aca3beaaSApple OSS Distributions 	MH_ALIGN(m, sizeof(struct ip) + sizeof(struct igmp));
2620*aca3beaaSApple OSS Distributions 
2621*aca3beaaSApple OSS Distributions 	m->m_pkthdr.len = sizeof(struct ip) + sizeof(struct igmp);
2622*aca3beaaSApple OSS Distributions 
2623*aca3beaaSApple OSS Distributions 	m->m_data += sizeof(struct ip);
2624*aca3beaaSApple OSS Distributions 	m->m_len = sizeof(struct igmp);
2625*aca3beaaSApple OSS Distributions 
2626*aca3beaaSApple OSS Distributions 	igmp = mtod(m, struct igmp *);
2627*aca3beaaSApple OSS Distributions 	igmp->igmp_type = (u_char)type;
2628*aca3beaaSApple OSS Distributions 	igmp->igmp_code = 0;
2629*aca3beaaSApple OSS Distributions 	igmp->igmp_group = inm->inm_addr;
2630*aca3beaaSApple OSS Distributions 	igmp->igmp_cksum = 0;
2631*aca3beaaSApple OSS Distributions 	igmp->igmp_cksum = in_cksum(m, sizeof(struct igmp));
2632*aca3beaaSApple OSS Distributions 
2633*aca3beaaSApple OSS Distributions 	m->m_data -= sizeof(struct ip);
2634*aca3beaaSApple OSS Distributions 	m->m_len += sizeof(struct ip);
2635*aca3beaaSApple OSS Distributions 
2636*aca3beaaSApple OSS Distributions 	ip = mtod(m, struct ip *);
2637*aca3beaaSApple OSS Distributions 	ip->ip_tos = 0;
2638*aca3beaaSApple OSS Distributions 	ip->ip_len = sizeof(struct ip) + sizeof(struct igmp);
2639*aca3beaaSApple OSS Distributions 	ip->ip_off = 0;
2640*aca3beaaSApple OSS Distributions 	ip->ip_p = IPPROTO_IGMP;
2641*aca3beaaSApple OSS Distributions 	ip->ip_src.s_addr = INADDR_ANY;
2642*aca3beaaSApple OSS Distributions 
2643*aca3beaaSApple OSS Distributions 	if (type == IGMP_HOST_LEAVE_MESSAGE) {
2644*aca3beaaSApple OSS Distributions 		ip->ip_dst.s_addr = htonl(INADDR_ALLRTRS_GROUP);
2645*aca3beaaSApple OSS Distributions 	} else {
2646*aca3beaaSApple OSS Distributions 		ip->ip_dst = inm->inm_addr;
2647*aca3beaaSApple OSS Distributions 	}
2648*aca3beaaSApple OSS Distributions 
2649*aca3beaaSApple OSS Distributions 	igmp_save_context(m, ifp);
2650*aca3beaaSApple OSS Distributions 
2651*aca3beaaSApple OSS Distributions 	m->m_flags |= M_IGMPV2;
2652*aca3beaaSApple OSS Distributions 	if (inm->inm_igi->igi_flags & IGIF_LOOPBACK) {
2653*aca3beaaSApple OSS Distributions 		m->m_flags |= M_IGMP_LOOP;
2654*aca3beaaSApple OSS Distributions 	}
2655*aca3beaaSApple OSS Distributions 
2656*aca3beaaSApple OSS Distributions 	/*
2657*aca3beaaSApple OSS Distributions 	 * Due to the fact that at this point we are possibly holding
2658*aca3beaaSApple OSS Distributions 	 * in_multihead_lock in shared or exclusive mode, we can't call
2659*aca3beaaSApple OSS Distributions 	 * igmp_sendpkt() here since that will eventually call ip_output(),
2660*aca3beaaSApple OSS Distributions 	 * which will try to lock in_multihead_lock and cause a deadlock.
2661*aca3beaaSApple OSS Distributions 	 * Instead we defer the work to the igmp_timeout() thread, thus
2662*aca3beaaSApple OSS Distributions 	 * avoiding unlocking in_multihead_lock here.
2663*aca3beaaSApple OSS Distributions 	 */
2664*aca3beaaSApple OSS Distributions 	if (IF_QFULL(&inm->inm_igi->igi_v2q)) {
2665*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: v1/v2 outbound queue full\n", __func__));
2666*aca3beaaSApple OSS Distributions 		error = ENOMEM;
2667*aca3beaaSApple OSS Distributions 		m_freem(m);
2668*aca3beaaSApple OSS Distributions 	} else {
2669*aca3beaaSApple OSS Distributions 		IF_ENQUEUE(&inm->inm_igi->igi_v2q, m);
2670*aca3beaaSApple OSS Distributions 		VERIFY(error == 0);
2671*aca3beaaSApple OSS Distributions 	}
2672*aca3beaaSApple OSS Distributions 	return error;
2673*aca3beaaSApple OSS Distributions }
2674*aca3beaaSApple OSS Distributions 
2675*aca3beaaSApple OSS Distributions /*
2676*aca3beaaSApple OSS Distributions  * Process a state change from the upper layer for the given IPv4 group.
2677*aca3beaaSApple OSS Distributions  *
2678*aca3beaaSApple OSS Distributions  * Each socket holds a reference on the in_multi in its own ip_moptions.
2679*aca3beaaSApple OSS Distributions  * The socket layer will have made the necessary updates to the group
2680*aca3beaaSApple OSS Distributions  * state, it is now up to IGMP to issue a state change report if there
2681*aca3beaaSApple OSS Distributions  * has been any change between T0 (when the last state-change was issued)
2682*aca3beaaSApple OSS Distributions  * and T1 (now).
2683*aca3beaaSApple OSS Distributions  *
2684*aca3beaaSApple OSS Distributions  * We use the IGMPv3 state machine at group level. The IGMP module
2685*aca3beaaSApple OSS Distributions  * however makes the decision as to which IGMP protocol version to speak.
2686*aca3beaaSApple OSS Distributions  * A state change *from* INCLUDE {} always means an initial join.
2687*aca3beaaSApple OSS Distributions  * A state change *to* INCLUDE {} always means a final leave.
2688*aca3beaaSApple OSS Distributions  *
2689*aca3beaaSApple OSS Distributions  * FUTURE: If IGIF_V3LITE is enabled for this interface, then we can
2690*aca3beaaSApple OSS Distributions  * save ourselves a bunch of work; any exclusive mode groups need not
2691*aca3beaaSApple OSS Distributions  * compute source filter lists.
2692*aca3beaaSApple OSS Distributions  */
2693*aca3beaaSApple OSS Distributions int
igmp_change_state(struct in_multi * inm,struct igmp_tparams * itp)2694*aca3beaaSApple OSS Distributions igmp_change_state(struct in_multi *inm, struct igmp_tparams *itp)
2695*aca3beaaSApple OSS Distributions {
2696*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo *igi;
2697*aca3beaaSApple OSS Distributions 	struct ifnet *ifp;
2698*aca3beaaSApple OSS Distributions 	int error = 0;
2699*aca3beaaSApple OSS Distributions 
2700*aca3beaaSApple OSS Distributions 	VERIFY(itp != NULL);
2701*aca3beaaSApple OSS Distributions 	bzero(itp, sizeof(*itp));
2702*aca3beaaSApple OSS Distributions 
2703*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2704*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_igi != NULL);
2705*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_NOTHELD(inm->inm_igi);
2706*aca3beaaSApple OSS Distributions 
2707*aca3beaaSApple OSS Distributions 	/*
2708*aca3beaaSApple OSS Distributions 	 * Try to detect if the upper layer just asked us to change state
2709*aca3beaaSApple OSS Distributions 	 * for an interface which has now gone away.
2710*aca3beaaSApple OSS Distributions 	 */
2711*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_ifma != NULL);
2712*aca3beaaSApple OSS Distributions 	ifp = inm->inm_ifma->ifma_ifp;
2713*aca3beaaSApple OSS Distributions 	/*
2714*aca3beaaSApple OSS Distributions 	 * Sanity check that netinet's notion of ifp is the same as net's.
2715*aca3beaaSApple OSS Distributions 	 */
2716*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_ifp == ifp);
2717*aca3beaaSApple OSS Distributions 
2718*aca3beaaSApple OSS Distributions 	igi = IGMP_IFINFO(ifp);
2719*aca3beaaSApple OSS Distributions 	VERIFY(igi != NULL);
2720*aca3beaaSApple OSS Distributions 
2721*aca3beaaSApple OSS Distributions 	/*
2722*aca3beaaSApple OSS Distributions 	 * If we detect a state transition to or from MCAST_UNDEFINED
2723*aca3beaaSApple OSS Distributions 	 * for this group, then we are starting or finishing an IGMP
2724*aca3beaaSApple OSS Distributions 	 * life cycle for this group.
2725*aca3beaaSApple OSS Distributions 	 */
2726*aca3beaaSApple OSS Distributions 	if (inm->inm_st[1].iss_fmode != inm->inm_st[0].iss_fmode) {
2727*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: inm transition %d -> %d\n", __func__,
2728*aca3beaaSApple OSS Distributions 		    inm->inm_st[0].iss_fmode, inm->inm_st[1].iss_fmode));
2729*aca3beaaSApple OSS Distributions 		if (inm->inm_st[0].iss_fmode == MCAST_UNDEFINED) {
2730*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: initial join\n", __func__));
2731*aca3beaaSApple OSS Distributions 			error = igmp_initial_join(inm, igi, itp);
2732*aca3beaaSApple OSS Distributions 			goto out;
2733*aca3beaaSApple OSS Distributions 		} else if (inm->inm_st[1].iss_fmode == MCAST_UNDEFINED) {
2734*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: final leave\n", __func__));
2735*aca3beaaSApple OSS Distributions 			igmp_final_leave(inm, igi, itp);
2736*aca3beaaSApple OSS Distributions 			goto out;
2737*aca3beaaSApple OSS Distributions 		}
2738*aca3beaaSApple OSS Distributions 	} else {
2739*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: filter set change\n", __func__));
2740*aca3beaaSApple OSS Distributions 	}
2741*aca3beaaSApple OSS Distributions 
2742*aca3beaaSApple OSS Distributions 	error = igmp_handle_state_change(inm, igi, itp);
2743*aca3beaaSApple OSS Distributions out:
2744*aca3beaaSApple OSS Distributions 	return error;
2745*aca3beaaSApple OSS Distributions }
2746*aca3beaaSApple OSS Distributions 
2747*aca3beaaSApple OSS Distributions /*
2748*aca3beaaSApple OSS Distributions  * Perform the initial join for an IGMP group.
2749*aca3beaaSApple OSS Distributions  *
2750*aca3beaaSApple OSS Distributions  * When joining a group:
2751*aca3beaaSApple OSS Distributions  *  If the group should have its IGMP traffic suppressed, do nothing.
2752*aca3beaaSApple OSS Distributions  *  IGMPv1 starts sending IGMPv1 host membership reports.
2753*aca3beaaSApple OSS Distributions  *  IGMPv2 starts sending IGMPv2 host membership reports.
2754*aca3beaaSApple OSS Distributions  *  IGMPv3 will schedule an IGMPv3 state-change report containing the
2755*aca3beaaSApple OSS Distributions  *  initial state of the membership.
2756*aca3beaaSApple OSS Distributions  */
2757*aca3beaaSApple OSS Distributions static int
igmp_initial_join(struct in_multi * inm,struct igmp_ifinfo * igi,struct igmp_tparams * itp)2758*aca3beaaSApple OSS Distributions igmp_initial_join(struct in_multi *inm, struct igmp_ifinfo *igi,
2759*aca3beaaSApple OSS Distributions     struct igmp_tparams *itp)
2760*aca3beaaSApple OSS Distributions {
2761*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
2762*aca3beaaSApple OSS Distributions 	struct ifqueue          *ifq;
2763*aca3beaaSApple OSS Distributions 	int                      error, retval, syncstates;
2764*aca3beaaSApple OSS Distributions 
2765*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2766*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_NOTHELD(igi);
2767*aca3beaaSApple OSS Distributions 	VERIFY(itp != NULL);
2768*aca3beaaSApple OSS Distributions 
2769*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(inm->inm_addr,
2770*aca3beaaSApple OSS Distributions 	    ("%s: initial join %s on ifp 0x%llx(%s)\n", __func__,
2771*aca3beaaSApple OSS Distributions 	    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifp),
2772*aca3beaaSApple OSS Distributions 	    if_name(inm->inm_ifp)));
2773*aca3beaaSApple OSS Distributions 
2774*aca3beaaSApple OSS Distributions 	error = 0;
2775*aca3beaaSApple OSS Distributions 	syncstates = 1;
2776*aca3beaaSApple OSS Distributions 
2777*aca3beaaSApple OSS Distributions 	ifp = inm->inm_ifp;
2778*aca3beaaSApple OSS Distributions 
2779*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
2780*aca3beaaSApple OSS Distributions 	VERIFY(igi->igi_ifp == ifp);
2781*aca3beaaSApple OSS Distributions 
2782*aca3beaaSApple OSS Distributions 	/*
2783*aca3beaaSApple OSS Distributions 	 * Groups joined on loopback or marked as 'not reported',
2784*aca3beaaSApple OSS Distributions 	 * e.g. 224.0.0.1, enter the IGMP_SILENT_MEMBER state and
2785*aca3beaaSApple OSS Distributions 	 * are never reported in any IGMP protocol exchanges.
2786*aca3beaaSApple OSS Distributions 	 * All other groups enter the appropriate IGMP state machine
2787*aca3beaaSApple OSS Distributions 	 * for the version in use on this link.
2788*aca3beaaSApple OSS Distributions 	 * A link marked as IGIF_SILENT causes IGMP to be completely
2789*aca3beaaSApple OSS Distributions 	 * disabled for the link.
2790*aca3beaaSApple OSS Distributions 	 */
2791*aca3beaaSApple OSS Distributions 	if ((ifp->if_flags & IFF_LOOPBACK) ||
2792*aca3beaaSApple OSS Distributions 	    (igi->igi_flags & IGIF_SILENT) ||
2793*aca3beaaSApple OSS Distributions 	    !igmp_isgroupreported(inm->inm_addr)) {
2794*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: not kicking state machine for silent group\n",
2795*aca3beaaSApple OSS Distributions 		    __func__));
2796*aca3beaaSApple OSS Distributions 		inm->inm_state = IGMP_SILENT_MEMBER;
2797*aca3beaaSApple OSS Distributions 		inm->inm_timer = 0;
2798*aca3beaaSApple OSS Distributions 	} else {
2799*aca3beaaSApple OSS Distributions 		/*
2800*aca3beaaSApple OSS Distributions 		 * Deal with overlapping in_multi lifecycle.
2801*aca3beaaSApple OSS Distributions 		 * If this group was LEAVING, then make sure
2802*aca3beaaSApple OSS Distributions 		 * we drop the reference we picked up to keep the
2803*aca3beaaSApple OSS Distributions 		 * group around for the final INCLUDE {} enqueue.
2804*aca3beaaSApple OSS Distributions 		 * Since we cannot call in_multi_detach() here,
2805*aca3beaaSApple OSS Distributions 		 * defer this task to the timer routine.
2806*aca3beaaSApple OSS Distributions 		 */
2807*aca3beaaSApple OSS Distributions 		if (igi->igi_version == IGMP_VERSION_3 &&
2808*aca3beaaSApple OSS Distributions 		    inm->inm_state == IGMP_LEAVING_MEMBER) {
2809*aca3beaaSApple OSS Distributions 			VERIFY(inm->inm_nrelecnt != 0);
2810*aca3beaaSApple OSS Distributions 			SLIST_INSERT_HEAD(&igi->igi_relinmhead, inm, inm_nrele);
2811*aca3beaaSApple OSS Distributions 		}
2812*aca3beaaSApple OSS Distributions 
2813*aca3beaaSApple OSS Distributions 		inm->inm_state = IGMP_REPORTING_MEMBER;
2814*aca3beaaSApple OSS Distributions 
2815*aca3beaaSApple OSS Distributions 		switch (igi->igi_version) {
2816*aca3beaaSApple OSS Distributions 		case IGMP_VERSION_1:
2817*aca3beaaSApple OSS Distributions 		case IGMP_VERSION_2:
2818*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_IDLE_MEMBER;
2819*aca3beaaSApple OSS Distributions 			error = igmp_v1v2_queue_report(inm,
2820*aca3beaaSApple OSS Distributions 			    (igi->igi_version == IGMP_VERSION_2) ?
2821*aca3beaaSApple OSS Distributions 			    IGMP_v2_HOST_MEMBERSHIP_REPORT :
2822*aca3beaaSApple OSS Distributions 			    IGMP_v1_HOST_MEMBERSHIP_REPORT);
2823*aca3beaaSApple OSS Distributions 
2824*aca3beaaSApple OSS Distributions 			INM_LOCK_ASSERT_HELD(inm);
2825*aca3beaaSApple OSS Distributions 			IGI_LOCK_ASSERT_HELD(igi);
2826*aca3beaaSApple OSS Distributions 
2827*aca3beaaSApple OSS Distributions 			if (error == 0) {
2828*aca3beaaSApple OSS Distributions 				inm->inm_timer =
2829*aca3beaaSApple OSS Distributions 				    IGMP_RANDOM_DELAY(IGMP_V1V2_MAX_RI);
2830*aca3beaaSApple OSS Distributions 				itp->cst = 1;
2831*aca3beaaSApple OSS Distributions 			}
2832*aca3beaaSApple OSS Distributions 			break;
2833*aca3beaaSApple OSS Distributions 
2834*aca3beaaSApple OSS Distributions 		case IGMP_VERSION_3:
2835*aca3beaaSApple OSS Distributions 			/*
2836*aca3beaaSApple OSS Distributions 			 * Defer update of T0 to T1, until the first copy
2837*aca3beaaSApple OSS Distributions 			 * of the state change has been transmitted.
2838*aca3beaaSApple OSS Distributions 			 */
2839*aca3beaaSApple OSS Distributions 			syncstates = 0;
2840*aca3beaaSApple OSS Distributions 
2841*aca3beaaSApple OSS Distributions 			/*
2842*aca3beaaSApple OSS Distributions 			 * Immediately enqueue a State-Change Report for
2843*aca3beaaSApple OSS Distributions 			 * this interface, freeing any previous reports.
2844*aca3beaaSApple OSS Distributions 			 * Don't kick the timers if there is nothing to do,
2845*aca3beaaSApple OSS Distributions 			 * or if an error occurred.
2846*aca3beaaSApple OSS Distributions 			 */
2847*aca3beaaSApple OSS Distributions 			ifq = &inm->inm_scq;
2848*aca3beaaSApple OSS Distributions 			IF_DRAIN(ifq);
2849*aca3beaaSApple OSS Distributions 			retval = igmp_v3_enqueue_group_record(ifq, inm, 1,
2850*aca3beaaSApple OSS Distributions 			    0, 0);
2851*aca3beaaSApple OSS Distributions 			itp->cst = (ifq->ifq_len > 0);
2852*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: enqueue record = %d\n",
2853*aca3beaaSApple OSS Distributions 			    __func__, retval));
2854*aca3beaaSApple OSS Distributions 			if (retval <= 0) {
2855*aca3beaaSApple OSS Distributions 				error = retval * -1;
2856*aca3beaaSApple OSS Distributions 				break;
2857*aca3beaaSApple OSS Distributions 			}
2858*aca3beaaSApple OSS Distributions 
2859*aca3beaaSApple OSS Distributions 			/*
2860*aca3beaaSApple OSS Distributions 			 * Schedule transmission of pending state-change
2861*aca3beaaSApple OSS Distributions 			 * report up to RV times for this link. The timer
2862*aca3beaaSApple OSS Distributions 			 * will fire at the next igmp_timeout (1 second),
2863*aca3beaaSApple OSS Distributions 			 * giving us an opportunity to merge the reports.
2864*aca3beaaSApple OSS Distributions 			 */
2865*aca3beaaSApple OSS Distributions 			if (igi->igi_flags & IGIF_LOOPBACK) {
2866*aca3beaaSApple OSS Distributions 				inm->inm_scrv = 1;
2867*aca3beaaSApple OSS Distributions 			} else {
2868*aca3beaaSApple OSS Distributions 				VERIFY(igi->igi_rv > 1);
2869*aca3beaaSApple OSS Distributions 				inm->inm_scrv = (uint16_t)igi->igi_rv;
2870*aca3beaaSApple OSS Distributions 			}
2871*aca3beaaSApple OSS Distributions 			inm->inm_sctimer = 1;
2872*aca3beaaSApple OSS Distributions 			itp->sct = 1;
2873*aca3beaaSApple OSS Distributions 
2874*aca3beaaSApple OSS Distributions 			error = 0;
2875*aca3beaaSApple OSS Distributions 			break;
2876*aca3beaaSApple OSS Distributions 		}
2877*aca3beaaSApple OSS Distributions 	}
2878*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
2879*aca3beaaSApple OSS Distributions 
2880*aca3beaaSApple OSS Distributions 	/*
2881*aca3beaaSApple OSS Distributions 	 * Only update the T0 state if state change is atomic,
2882*aca3beaaSApple OSS Distributions 	 * i.e. we don't need to wait for a timer to fire before we
2883*aca3beaaSApple OSS Distributions 	 * can consider the state change to have been communicated.
2884*aca3beaaSApple OSS Distributions 	 */
2885*aca3beaaSApple OSS Distributions 	if (syncstates) {
2886*aca3beaaSApple OSS Distributions 		inm_commit(inm);
2887*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(inm->inm_addr,
2888*aca3beaaSApple OSS Distributions 		    ("%s: T1 -> T0 for %s/%s\n", __func__,
2889*aca3beaaSApple OSS Distributions 		    _igmp_inet_buf, if_name(inm->inm_ifp)));
2890*aca3beaaSApple OSS Distributions 	}
2891*aca3beaaSApple OSS Distributions 
2892*aca3beaaSApple OSS Distributions 	return error;
2893*aca3beaaSApple OSS Distributions }
2894*aca3beaaSApple OSS Distributions 
2895*aca3beaaSApple OSS Distributions /*
2896*aca3beaaSApple OSS Distributions  * Issue an intermediate state change during the IGMP life-cycle.
2897*aca3beaaSApple OSS Distributions  */
2898*aca3beaaSApple OSS Distributions static int
igmp_handle_state_change(struct in_multi * inm,struct igmp_ifinfo * igi,struct igmp_tparams * itp)2899*aca3beaaSApple OSS Distributions igmp_handle_state_change(struct in_multi *inm, struct igmp_ifinfo *igi,
2900*aca3beaaSApple OSS Distributions     struct igmp_tparams *itp)
2901*aca3beaaSApple OSS Distributions {
2902*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
2903*aca3beaaSApple OSS Distributions 	int                      retval = 0;
2904*aca3beaaSApple OSS Distributions 
2905*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2906*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_NOTHELD(igi);
2907*aca3beaaSApple OSS Distributions 	VERIFY(itp != NULL);
2908*aca3beaaSApple OSS Distributions 
2909*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(inm->inm_addr,
2910*aca3beaaSApple OSS Distributions 	    ("%s: state change for %s on ifp 0x%llx(%s)\n", __func__,
2911*aca3beaaSApple OSS Distributions 	    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifp),
2912*aca3beaaSApple OSS Distributions 	    if_name(inm->inm_ifp)));
2913*aca3beaaSApple OSS Distributions 
2914*aca3beaaSApple OSS Distributions 	ifp = inm->inm_ifp;
2915*aca3beaaSApple OSS Distributions 
2916*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
2917*aca3beaaSApple OSS Distributions 	VERIFY(igi->igi_ifp == ifp);
2918*aca3beaaSApple OSS Distributions 
2919*aca3beaaSApple OSS Distributions 	if ((ifp->if_flags & IFF_LOOPBACK) ||
2920*aca3beaaSApple OSS Distributions 	    (igi->igi_flags & IGIF_SILENT) ||
2921*aca3beaaSApple OSS Distributions 	    !igmp_isgroupreported(inm->inm_addr) ||
2922*aca3beaaSApple OSS Distributions 	    (igi->igi_version != IGMP_VERSION_3)) {
2923*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
2924*aca3beaaSApple OSS Distributions 		if (!igmp_isgroupreported(inm->inm_addr)) {
2925*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: not kicking state "
2926*aca3beaaSApple OSS Distributions 			    "machine for silent group\n", __func__));
2927*aca3beaaSApple OSS Distributions 		}
2928*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: nothing to do\n", __func__));
2929*aca3beaaSApple OSS Distributions 		inm_commit(inm);
2930*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(inm->inm_addr,
2931*aca3beaaSApple OSS Distributions 		    ("%s: T1 -> T0 for %s/%s\n", __func__,
2932*aca3beaaSApple OSS Distributions 		    _igmp_inet_buf, inm->inm_ifp->if_name));
2933*aca3beaaSApple OSS Distributions 		goto done;
2934*aca3beaaSApple OSS Distributions 	}
2935*aca3beaaSApple OSS Distributions 
2936*aca3beaaSApple OSS Distributions 	IF_DRAIN(&inm->inm_scq);
2937*aca3beaaSApple OSS Distributions 
2938*aca3beaaSApple OSS Distributions 	retval = igmp_v3_enqueue_group_record(&inm->inm_scq, inm, 1, 0, 0);
2939*aca3beaaSApple OSS Distributions 	itp->cst = (inm->inm_scq.ifq_len > 0);
2940*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: enqueue record = %d\n", __func__, retval));
2941*aca3beaaSApple OSS Distributions 	if (retval <= 0) {
2942*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
2943*aca3beaaSApple OSS Distributions 		retval *= -1;
2944*aca3beaaSApple OSS Distributions 		goto done;
2945*aca3beaaSApple OSS Distributions 	}
2946*aca3beaaSApple OSS Distributions 	/*
2947*aca3beaaSApple OSS Distributions 	 * If record(s) were enqueued, start the state-change
2948*aca3beaaSApple OSS Distributions 	 * report timer for this group.
2949*aca3beaaSApple OSS Distributions 	 */
2950*aca3beaaSApple OSS Distributions 	inm->inm_scrv = ((igi->igi_flags & IGIF_LOOPBACK) ? 1 : (uint16_t)igi->igi_rv);
2951*aca3beaaSApple OSS Distributions 	inm->inm_sctimer = 1;
2952*aca3beaaSApple OSS Distributions 	itp->sct = 1;
2953*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
2954*aca3beaaSApple OSS Distributions done:
2955*aca3beaaSApple OSS Distributions 	return retval;
2956*aca3beaaSApple OSS Distributions }
2957*aca3beaaSApple OSS Distributions 
2958*aca3beaaSApple OSS Distributions /*
2959*aca3beaaSApple OSS Distributions  * Perform the final leave for an IGMP group.
2960*aca3beaaSApple OSS Distributions  *
2961*aca3beaaSApple OSS Distributions  * When leaving a group:
2962*aca3beaaSApple OSS Distributions  *  IGMPv1 does nothing.
2963*aca3beaaSApple OSS Distributions  *  IGMPv2 sends a host leave message, if and only if we are the reporter.
2964*aca3beaaSApple OSS Distributions  *  IGMPv3 enqueues a state-change report containing a transition
2965*aca3beaaSApple OSS Distributions  *  to INCLUDE {} for immediate transmission.
2966*aca3beaaSApple OSS Distributions  */
2967*aca3beaaSApple OSS Distributions static void
igmp_final_leave(struct in_multi * inm,struct igmp_ifinfo * igi,struct igmp_tparams * itp)2968*aca3beaaSApple OSS Distributions igmp_final_leave(struct in_multi *inm, struct igmp_ifinfo *igi,
2969*aca3beaaSApple OSS Distributions     struct igmp_tparams *itp)
2970*aca3beaaSApple OSS Distributions {
2971*aca3beaaSApple OSS Distributions 	int syncstates = 1;
2972*aca3beaaSApple OSS Distributions 	bool retried_already = false;
2973*aca3beaaSApple OSS Distributions 
2974*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
2975*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_NOTHELD(igi);
2976*aca3beaaSApple OSS Distributions 	VERIFY(itp != NULL);
2977*aca3beaaSApple OSS Distributions 
2978*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(inm->inm_addr,
2979*aca3beaaSApple OSS Distributions 	    ("%s: final leave %s on ifp 0x%llx(%s)\n", __func__,
2980*aca3beaaSApple OSS Distributions 	    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifp),
2981*aca3beaaSApple OSS Distributions 	    if_name(inm->inm_ifp)));
2982*aca3beaaSApple OSS Distributions 
2983*aca3beaaSApple OSS Distributions retry:
2984*aca3beaaSApple OSS Distributions 	switch (inm->inm_state) {
2985*aca3beaaSApple OSS Distributions 	case IGMP_NOT_MEMBER:
2986*aca3beaaSApple OSS Distributions 	case IGMP_SILENT_MEMBER:
2987*aca3beaaSApple OSS Distributions 	case IGMP_LEAVING_MEMBER:
2988*aca3beaaSApple OSS Distributions 		/* Already leaving or left; do nothing. */
2989*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: not kicking state machine for silent group\n",
2990*aca3beaaSApple OSS Distributions 		    __func__));
2991*aca3beaaSApple OSS Distributions 		break;
2992*aca3beaaSApple OSS Distributions 	case IGMP_REPORTING_MEMBER:
2993*aca3beaaSApple OSS Distributions 	case IGMP_IDLE_MEMBER:
2994*aca3beaaSApple OSS Distributions 	case IGMP_G_QUERY_PENDING_MEMBER:
2995*aca3beaaSApple OSS Distributions 	case IGMP_SG_QUERY_PENDING_MEMBER:
2996*aca3beaaSApple OSS Distributions 		IGI_LOCK(igi);
2997*aca3beaaSApple OSS Distributions 		if (igi->igi_version == IGMP_VERSION_2) {
2998*aca3beaaSApple OSS Distributions 			if (inm->inm_state == IGMP_G_QUERY_PENDING_MEMBER ||
2999*aca3beaaSApple OSS Distributions 			    inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER) {
3000*aca3beaaSApple OSS Distributions 				/*
3001*aca3beaaSApple OSS Distributions 				 * We may be in the process of downgrading to
3002*aca3beaaSApple OSS Distributions 				 * IGMPv2 but because we just grabbed the
3003*aca3beaaSApple OSS Distributions 				 * igi_lock we may have lost the race.
3004*aca3beaaSApple OSS Distributions 				 */
3005*aca3beaaSApple OSS Distributions 				if (!retried_already) {
3006*aca3beaaSApple OSS Distributions 					IGI_UNLOCK(igi);
3007*aca3beaaSApple OSS Distributions 					retried_already = true;
3008*aca3beaaSApple OSS Distributions 					goto retry;
3009*aca3beaaSApple OSS Distributions 				} else {
3010*aca3beaaSApple OSS Distributions 					/*
3011*aca3beaaSApple OSS Distributions 					 * Proceed with leaving the group
3012*aca3beaaSApple OSS Distributions 					 * as if it were IGMPv2 even though we
3013*aca3beaaSApple OSS Distributions 					 * may have an inconsistent multicast state.
3014*aca3beaaSApple OSS Distributions 					 */
3015*aca3beaaSApple OSS Distributions 				}
3016*aca3beaaSApple OSS Distributions 			}
3017*aca3beaaSApple OSS Distributions 			/* scheduler timer if enqueue is successful */
3018*aca3beaaSApple OSS Distributions 			itp->cst = (igmp_v1v2_queue_report(inm,
3019*aca3beaaSApple OSS Distributions 			    IGMP_HOST_LEAVE_MESSAGE) == 0);
3020*aca3beaaSApple OSS Distributions 
3021*aca3beaaSApple OSS Distributions 			INM_LOCK_ASSERT_HELD(inm);
3022*aca3beaaSApple OSS Distributions 			IGI_LOCK_ASSERT_HELD(igi);
3023*aca3beaaSApple OSS Distributions 
3024*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_NOT_MEMBER;
3025*aca3beaaSApple OSS Distributions 		} else if (igi->igi_version == IGMP_VERSION_3) {
3026*aca3beaaSApple OSS Distributions 			/*
3027*aca3beaaSApple OSS Distributions 			 * Stop group timer and all pending reports.
3028*aca3beaaSApple OSS Distributions 			 * Immediately enqueue a state-change report
3029*aca3beaaSApple OSS Distributions 			 * TO_IN {} to be sent on the next timeout,
3030*aca3beaaSApple OSS Distributions 			 * giving us an opportunity to merge reports.
3031*aca3beaaSApple OSS Distributions 			 */
3032*aca3beaaSApple OSS Distributions 			IF_DRAIN(&inm->inm_scq);
3033*aca3beaaSApple OSS Distributions 			inm->inm_timer = 0;
3034*aca3beaaSApple OSS Distributions 			if (igi->igi_flags & IGIF_LOOPBACK) {
3035*aca3beaaSApple OSS Distributions 				inm->inm_scrv = 1;
3036*aca3beaaSApple OSS Distributions 			} else {
3037*aca3beaaSApple OSS Distributions 				inm->inm_scrv = (uint16_t)igi->igi_rv;
3038*aca3beaaSApple OSS Distributions 			}
3039*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(inm->inm_addr,
3040*aca3beaaSApple OSS Distributions 			    ("%s: Leaving %s/%s with %d "
3041*aca3beaaSApple OSS Distributions 			    "pending retransmissions.\n", __func__,
3042*aca3beaaSApple OSS Distributions 			    _igmp_inet_buf, if_name(inm->inm_ifp),
3043*aca3beaaSApple OSS Distributions 			    inm->inm_scrv));
3044*aca3beaaSApple OSS Distributions 			if (inm->inm_scrv == 0) {
3045*aca3beaaSApple OSS Distributions 				inm->inm_state = IGMP_NOT_MEMBER;
3046*aca3beaaSApple OSS Distributions 				inm->inm_sctimer = 0;
3047*aca3beaaSApple OSS Distributions 			} else {
3048*aca3beaaSApple OSS Distributions 				int retval;
3049*aca3beaaSApple OSS Distributions 				/*
3050*aca3beaaSApple OSS Distributions 				 * Stick around in the in_multihead list;
3051*aca3beaaSApple OSS Distributions 				 * the final detach will be issued by
3052*aca3beaaSApple OSS Distributions 				 * igmp_v3_process_group_timers() when
3053*aca3beaaSApple OSS Distributions 				 * the retransmit timer expires.
3054*aca3beaaSApple OSS Distributions 				 */
3055*aca3beaaSApple OSS Distributions 				INM_ADDREF_LOCKED(inm);
3056*aca3beaaSApple OSS Distributions 				VERIFY(inm->inm_debug & IFD_ATTACHED);
3057*aca3beaaSApple OSS Distributions 				inm->inm_reqcnt++;
3058*aca3beaaSApple OSS Distributions 				VERIFY(inm->inm_reqcnt >= 1);
3059*aca3beaaSApple OSS Distributions 				inm->inm_nrelecnt++;
3060*aca3beaaSApple OSS Distributions 				VERIFY(inm->inm_nrelecnt != 0);
3061*aca3beaaSApple OSS Distributions 
3062*aca3beaaSApple OSS Distributions 				retval = igmp_v3_enqueue_group_record(
3063*aca3beaaSApple OSS Distributions 					&inm->inm_scq, inm, 1, 0, 0);
3064*aca3beaaSApple OSS Distributions 				itp->cst = (inm->inm_scq.ifq_len > 0);
3065*aca3beaaSApple OSS Distributions 				KASSERT(retval != 0,
3066*aca3beaaSApple OSS Distributions 				    ("%s: enqueue record = %d\n", __func__,
3067*aca3beaaSApple OSS Distributions 				    retval));
3068*aca3beaaSApple OSS Distributions 
3069*aca3beaaSApple OSS Distributions 				inm->inm_state = IGMP_LEAVING_MEMBER;
3070*aca3beaaSApple OSS Distributions 				inm->inm_sctimer = 1;
3071*aca3beaaSApple OSS Distributions 				itp->sct = 1;
3072*aca3beaaSApple OSS Distributions 				syncstates = 0;
3073*aca3beaaSApple OSS Distributions 			}
3074*aca3beaaSApple OSS Distributions 		}
3075*aca3beaaSApple OSS Distributions 		IGI_UNLOCK(igi);
3076*aca3beaaSApple OSS Distributions 		break;
3077*aca3beaaSApple OSS Distributions 	case IGMP_LAZY_MEMBER:
3078*aca3beaaSApple OSS Distributions 	case IGMP_SLEEPING_MEMBER:
3079*aca3beaaSApple OSS Distributions 	case IGMP_AWAKENING_MEMBER:
3080*aca3beaaSApple OSS Distributions 		/* Our reports are suppressed; do nothing. */
3081*aca3beaaSApple OSS Distributions 		break;
3082*aca3beaaSApple OSS Distributions 	}
3083*aca3beaaSApple OSS Distributions 
3084*aca3beaaSApple OSS Distributions 	if (syncstates) {
3085*aca3beaaSApple OSS Distributions 		inm_commit(inm);
3086*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(inm->inm_addr,
3087*aca3beaaSApple OSS Distributions 		    ("%s: T1 -> T0 for %s/%s\n", __func__,
3088*aca3beaaSApple OSS Distributions 		    _igmp_inet_buf, if_name(inm->inm_ifp)));
3089*aca3beaaSApple OSS Distributions 		inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
3090*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(inm->inm_addr,
3091*aca3beaaSApple OSS Distributions 		    ("%s: T1 now MCAST_UNDEFINED for %s/%s\n",
3092*aca3beaaSApple OSS Distributions 		    __func__, _igmp_inet_buf, if_name(inm->inm_ifp)));
3093*aca3beaaSApple OSS Distributions 	}
3094*aca3beaaSApple OSS Distributions }
3095*aca3beaaSApple OSS Distributions 
3096*aca3beaaSApple OSS Distributions /*
3097*aca3beaaSApple OSS Distributions  * Enqueue an IGMPv3 group record to the given output queue.
3098*aca3beaaSApple OSS Distributions  *
3099*aca3beaaSApple OSS Distributions  * XXX This function could do with having the allocation code
3100*aca3beaaSApple OSS Distributions  * split out, and the multiple-tree-walks coalesced into a single
3101*aca3beaaSApple OSS Distributions  * routine as has been done in igmp_v3_enqueue_filter_change().
3102*aca3beaaSApple OSS Distributions  *
3103*aca3beaaSApple OSS Distributions  * If is_state_change is zero, a current-state record is appended.
3104*aca3beaaSApple OSS Distributions  * If is_state_change is non-zero, a state-change report is appended.
3105*aca3beaaSApple OSS Distributions  *
3106*aca3beaaSApple OSS Distributions  * If is_group_query is non-zero, an mbuf packet chain is allocated.
3107*aca3beaaSApple OSS Distributions  * If is_group_query is zero, and if there is a packet with free space
3108*aca3beaaSApple OSS Distributions  * at the tail of the queue, it will be appended to providing there
3109*aca3beaaSApple OSS Distributions  * is enough free space.
3110*aca3beaaSApple OSS Distributions  * Otherwise a new mbuf packet chain is allocated.
3111*aca3beaaSApple OSS Distributions  *
3112*aca3beaaSApple OSS Distributions  * If is_source_query is non-zero, each source is checked to see if
3113*aca3beaaSApple OSS Distributions  * it was recorded for a Group-Source query, and will be omitted if
3114*aca3beaaSApple OSS Distributions  * it is not both in-mode and recorded.
3115*aca3beaaSApple OSS Distributions  *
3116*aca3beaaSApple OSS Distributions  * The function will attempt to allocate leading space in the packet
3117*aca3beaaSApple OSS Distributions  * for the IP/IGMP header to be prepended without fragmenting the chain.
3118*aca3beaaSApple OSS Distributions  *
3119*aca3beaaSApple OSS Distributions  * If successful the size of all data appended to the queue is returned,
3120*aca3beaaSApple OSS Distributions  * otherwise an error code less than zero is returned, or zero if
3121*aca3beaaSApple OSS Distributions  * no record(s) were appended.
3122*aca3beaaSApple OSS Distributions  */
3123*aca3beaaSApple OSS Distributions static int
igmp_v3_enqueue_group_record(struct ifqueue * ifq,struct in_multi * inm,const int is_state_change,const int is_group_query,const int is_source_query)3124*aca3beaaSApple OSS Distributions igmp_v3_enqueue_group_record(struct ifqueue *ifq, struct in_multi *inm,
3125*aca3beaaSApple OSS Distributions     const int is_state_change, const int is_group_query,
3126*aca3beaaSApple OSS Distributions     const int is_source_query)
3127*aca3beaaSApple OSS Distributions {
3128*aca3beaaSApple OSS Distributions 	struct igmp_grouprec     ig;
3129*aca3beaaSApple OSS Distributions 	struct igmp_grouprec    *pig;
3130*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
3131*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *nims;
3132*aca3beaaSApple OSS Distributions 	struct mbuf             *m0, *m, *md;
3133*aca3beaaSApple OSS Distributions 	int                      error, is_filter_list_change;
3134*aca3beaaSApple OSS Distributions 	int                      minrec0len, m0srcs, nbytes, off;
3135*aca3beaaSApple OSS Distributions 	uint16_t                 msrcs;
3136*aca3beaaSApple OSS Distributions 	int                      record_has_sources;
3137*aca3beaaSApple OSS Distributions 	int                      now;
3138*aca3beaaSApple OSS Distributions 	int                      type;
3139*aca3beaaSApple OSS Distributions 	in_addr_t                naddr;
3140*aca3beaaSApple OSS Distributions 	uint16_t                 mode;
3141*aca3beaaSApple OSS Distributions 	u_int16_t                ig_numsrc;
3142*aca3beaaSApple OSS Distributions 
3143*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
3144*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(inm->inm_igi);
3145*aca3beaaSApple OSS Distributions 
3146*aca3beaaSApple OSS Distributions 	error = 0;
3147*aca3beaaSApple OSS Distributions 	ifp = inm->inm_ifp;
3148*aca3beaaSApple OSS Distributions 	is_filter_list_change = 0;
3149*aca3beaaSApple OSS Distributions 	m = NULL;
3150*aca3beaaSApple OSS Distributions 	m0 = NULL;
3151*aca3beaaSApple OSS Distributions 	m0srcs = 0;
3152*aca3beaaSApple OSS Distributions 	msrcs = 0;
3153*aca3beaaSApple OSS Distributions 	nbytes = 0;
3154*aca3beaaSApple OSS Distributions 	nims = NULL;
3155*aca3beaaSApple OSS Distributions 	record_has_sources = 1;
3156*aca3beaaSApple OSS Distributions 	pig = NULL;
3157*aca3beaaSApple OSS Distributions 	type = IGMP_DO_NOTHING;
3158*aca3beaaSApple OSS Distributions 	mode = inm->inm_st[1].iss_fmode;
3159*aca3beaaSApple OSS Distributions 
3160*aca3beaaSApple OSS Distributions 	/*
3161*aca3beaaSApple OSS Distributions 	 * If we did not transition out of ASM mode during t0->t1,
3162*aca3beaaSApple OSS Distributions 	 * and there are no source nodes to process, we can skip
3163*aca3beaaSApple OSS Distributions 	 * the generation of source records.
3164*aca3beaaSApple OSS Distributions 	 */
3165*aca3beaaSApple OSS Distributions 	if (inm->inm_st[0].iss_asm > 0 && inm->inm_st[1].iss_asm > 0 &&
3166*aca3beaaSApple OSS Distributions 	    inm->inm_nsrc == 0) {
3167*aca3beaaSApple OSS Distributions 		record_has_sources = 0;
3168*aca3beaaSApple OSS Distributions 	}
3169*aca3beaaSApple OSS Distributions 
3170*aca3beaaSApple OSS Distributions 	if (is_state_change) {
3171*aca3beaaSApple OSS Distributions 		/*
3172*aca3beaaSApple OSS Distributions 		 * Queue a state change record.
3173*aca3beaaSApple OSS Distributions 		 * If the mode did not change, and there are non-ASM
3174*aca3beaaSApple OSS Distributions 		 * listeners or source filters present,
3175*aca3beaaSApple OSS Distributions 		 * we potentially need to issue two records for the group.
3176*aca3beaaSApple OSS Distributions 		 * If we are transitioning to MCAST_UNDEFINED, we need
3177*aca3beaaSApple OSS Distributions 		 * not send any sources.
3178*aca3beaaSApple OSS Distributions 		 * If there are ASM listeners, and there was no filter
3179*aca3beaaSApple OSS Distributions 		 * mode transition of any kind, do nothing.
3180*aca3beaaSApple OSS Distributions 		 */
3181*aca3beaaSApple OSS Distributions 		if (mode != inm->inm_st[0].iss_fmode) {
3182*aca3beaaSApple OSS Distributions 			if (mode == MCAST_EXCLUDE) {
3183*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: change to EXCLUDE\n",
3184*aca3beaaSApple OSS Distributions 				    __func__));
3185*aca3beaaSApple OSS Distributions 				type = IGMP_CHANGE_TO_EXCLUDE_MODE;
3186*aca3beaaSApple OSS Distributions 			} else {
3187*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: change to INCLUDE\n",
3188*aca3beaaSApple OSS Distributions 				    __func__));
3189*aca3beaaSApple OSS Distributions 				type = IGMP_CHANGE_TO_INCLUDE_MODE;
3190*aca3beaaSApple OSS Distributions 				if (mode == MCAST_UNDEFINED) {
3191*aca3beaaSApple OSS Distributions 					record_has_sources = 0;
3192*aca3beaaSApple OSS Distributions 				}
3193*aca3beaaSApple OSS Distributions 			}
3194*aca3beaaSApple OSS Distributions 		} else {
3195*aca3beaaSApple OSS Distributions 			if (record_has_sources) {
3196*aca3beaaSApple OSS Distributions 				is_filter_list_change = 1;
3197*aca3beaaSApple OSS Distributions 			} else {
3198*aca3beaaSApple OSS Distributions 				type = IGMP_DO_NOTHING;
3199*aca3beaaSApple OSS Distributions 			}
3200*aca3beaaSApple OSS Distributions 		}
3201*aca3beaaSApple OSS Distributions 	} else {
3202*aca3beaaSApple OSS Distributions 		/*
3203*aca3beaaSApple OSS Distributions 		 * Queue a current state record.
3204*aca3beaaSApple OSS Distributions 		 */
3205*aca3beaaSApple OSS Distributions 		if (mode == MCAST_EXCLUDE) {
3206*aca3beaaSApple OSS Distributions 			type = IGMP_MODE_IS_EXCLUDE;
3207*aca3beaaSApple OSS Distributions 		} else if (mode == MCAST_INCLUDE) {
3208*aca3beaaSApple OSS Distributions 			type = IGMP_MODE_IS_INCLUDE;
3209*aca3beaaSApple OSS Distributions 			VERIFY(inm->inm_st[1].iss_asm == 0);
3210*aca3beaaSApple OSS Distributions 		}
3211*aca3beaaSApple OSS Distributions 	}
3212*aca3beaaSApple OSS Distributions 
3213*aca3beaaSApple OSS Distributions 	/*
3214*aca3beaaSApple OSS Distributions 	 * Generate the filter list changes using a separate function.
3215*aca3beaaSApple OSS Distributions 	 */
3216*aca3beaaSApple OSS Distributions 	if (is_filter_list_change) {
3217*aca3beaaSApple OSS Distributions 		return igmp_v3_enqueue_filter_change(ifq, inm);
3218*aca3beaaSApple OSS Distributions 	}
3219*aca3beaaSApple OSS Distributions 
3220*aca3beaaSApple OSS Distributions 	if (type == IGMP_DO_NOTHING) {
3221*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(inm->inm_addr,
3222*aca3beaaSApple OSS Distributions 		    ("%s: nothing to do for %s/%s\n",
3223*aca3beaaSApple OSS Distributions 		    __func__, _igmp_inet_buf,
3224*aca3beaaSApple OSS Distributions 		    if_name(inm->inm_ifp)));
3225*aca3beaaSApple OSS Distributions 		return 0;
3226*aca3beaaSApple OSS Distributions 	}
3227*aca3beaaSApple OSS Distributions 
3228*aca3beaaSApple OSS Distributions 	/*
3229*aca3beaaSApple OSS Distributions 	 * If any sources are present, we must be able to fit at least
3230*aca3beaaSApple OSS Distributions 	 * one in the trailing space of the tail packet's mbuf,
3231*aca3beaaSApple OSS Distributions 	 * ideally more.
3232*aca3beaaSApple OSS Distributions 	 */
3233*aca3beaaSApple OSS Distributions 	minrec0len = sizeof(struct igmp_grouprec);
3234*aca3beaaSApple OSS Distributions 	if (record_has_sources) {
3235*aca3beaaSApple OSS Distributions 		minrec0len += sizeof(in_addr_t);
3236*aca3beaaSApple OSS Distributions 	}
3237*aca3beaaSApple OSS Distributions 
3238*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(inm->inm_addr,
3239*aca3beaaSApple OSS Distributions 	    ("%s: queueing %s for %s/%s\n", __func__,
3240*aca3beaaSApple OSS Distributions 	    igmp_rec_type_to_str(type), _igmp_inet_buf,
3241*aca3beaaSApple OSS Distributions 	    if_name(inm->inm_ifp)));
3242*aca3beaaSApple OSS Distributions 
3243*aca3beaaSApple OSS Distributions 	/*
3244*aca3beaaSApple OSS Distributions 	 * Check if we have a packet in the tail of the queue for this
3245*aca3beaaSApple OSS Distributions 	 * group into which the first group record for this group will fit.
3246*aca3beaaSApple OSS Distributions 	 * Otherwise allocate a new packet.
3247*aca3beaaSApple OSS Distributions 	 * Always allocate leading space for IP+RA_OPT+IGMP+REPORT.
3248*aca3beaaSApple OSS Distributions 	 * Note: Group records for G/GSR query responses MUST be sent
3249*aca3beaaSApple OSS Distributions 	 * in their own packet.
3250*aca3beaaSApple OSS Distributions 	 */
3251*aca3beaaSApple OSS Distributions 	m0 = ifq->ifq_tail;
3252*aca3beaaSApple OSS Distributions 	if (!is_group_query &&
3253*aca3beaaSApple OSS Distributions 	    m0 != NULL &&
3254*aca3beaaSApple OSS Distributions 	    (m0->m_pkthdr.vt_nrecs + 1 <= IGMP_V3_REPORT_MAXRECS) &&
3255*aca3beaaSApple OSS Distributions 	    (m0->m_pkthdr.len + minrec0len) <
3256*aca3beaaSApple OSS Distributions 	    (ifp->if_mtu - IGMP_LEADINGSPACE)) {
3257*aca3beaaSApple OSS Distributions 		m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
3258*aca3beaaSApple OSS Distributions 		    sizeof(struct igmp_grouprec)) / sizeof(in_addr_t);
3259*aca3beaaSApple OSS Distributions 		m = m0;
3260*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: use existing packet\n", __func__));
3261*aca3beaaSApple OSS Distributions 	} else {
3262*aca3beaaSApple OSS Distributions 		if (IF_QFULL(ifq)) {
3263*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: outbound queue full\n", __func__));
3264*aca3beaaSApple OSS Distributions 			return -ENOMEM;
3265*aca3beaaSApple OSS Distributions 		}
3266*aca3beaaSApple OSS Distributions 		m = NULL;
3267*aca3beaaSApple OSS Distributions 		m0srcs = (ifp->if_mtu - IGMP_LEADINGSPACE -
3268*aca3beaaSApple OSS Distributions 		    sizeof(struct igmp_grouprec)) / sizeof(in_addr_t);
3269*aca3beaaSApple OSS Distributions 		if (!is_state_change && !is_group_query) {
3270*aca3beaaSApple OSS Distributions 			m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3271*aca3beaaSApple OSS Distributions 			if (m) {
3272*aca3beaaSApple OSS Distributions 				m->m_data += IGMP_LEADINGSPACE;
3273*aca3beaaSApple OSS Distributions 			}
3274*aca3beaaSApple OSS Distributions 		}
3275*aca3beaaSApple OSS Distributions 		if (m == NULL) {
3276*aca3beaaSApple OSS Distributions 			m = m_gethdr(M_DONTWAIT, MT_DATA);
3277*aca3beaaSApple OSS Distributions 			if (m) {
3278*aca3beaaSApple OSS Distributions 				MH_ALIGN(m, IGMP_LEADINGSPACE);
3279*aca3beaaSApple OSS Distributions 			}
3280*aca3beaaSApple OSS Distributions 		}
3281*aca3beaaSApple OSS Distributions 		if (m == NULL) {
3282*aca3beaaSApple OSS Distributions 			return -ENOMEM;
3283*aca3beaaSApple OSS Distributions 		}
3284*aca3beaaSApple OSS Distributions 
3285*aca3beaaSApple OSS Distributions 		igmp_save_context(m, ifp);
3286*aca3beaaSApple OSS Distributions 
3287*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: allocated first packet\n", __func__));
3288*aca3beaaSApple OSS Distributions 	}
3289*aca3beaaSApple OSS Distributions 
3290*aca3beaaSApple OSS Distributions 	/*
3291*aca3beaaSApple OSS Distributions 	 * Append group record.
3292*aca3beaaSApple OSS Distributions 	 * If we have sources, we don't know how many yet.
3293*aca3beaaSApple OSS Distributions 	 */
3294*aca3beaaSApple OSS Distributions 	ig.ig_type = (u_char)type;
3295*aca3beaaSApple OSS Distributions 	ig.ig_datalen = 0;
3296*aca3beaaSApple OSS Distributions 	ig.ig_numsrc = 0;
3297*aca3beaaSApple OSS Distributions 	ig.ig_group = inm->inm_addr;
3298*aca3beaaSApple OSS Distributions 	if (!m_append(m, sizeof(struct igmp_grouprec), (void *)&ig)) {
3299*aca3beaaSApple OSS Distributions 		if (m != m0) {
3300*aca3beaaSApple OSS Distributions 			m_freem(m);
3301*aca3beaaSApple OSS Distributions 		}
3302*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: m_append() failed.\n", __func__));
3303*aca3beaaSApple OSS Distributions 		return -ENOMEM;
3304*aca3beaaSApple OSS Distributions 	}
3305*aca3beaaSApple OSS Distributions 	nbytes += sizeof(struct igmp_grouprec);
3306*aca3beaaSApple OSS Distributions 
3307*aca3beaaSApple OSS Distributions 	/*
3308*aca3beaaSApple OSS Distributions 	 * Append as many sources as will fit in the first packet.
3309*aca3beaaSApple OSS Distributions 	 * If we are appending to a new packet, the chain allocation
3310*aca3beaaSApple OSS Distributions 	 * may potentially use clusters; use m_getptr() in this case.
3311*aca3beaaSApple OSS Distributions 	 * If we are appending to an existing packet, we need to obtain
3312*aca3beaaSApple OSS Distributions 	 * a pointer to the group record after m_append(), in case a new
3313*aca3beaaSApple OSS Distributions 	 * mbuf was allocated.
3314*aca3beaaSApple OSS Distributions 	 * Only append sources which are in-mode at t1. If we are
3315*aca3beaaSApple OSS Distributions 	 * transitioning to MCAST_UNDEFINED state on the group, do not
3316*aca3beaaSApple OSS Distributions 	 * include source entries.
3317*aca3beaaSApple OSS Distributions 	 * Only report recorded sources in our filter set when responding
3318*aca3beaaSApple OSS Distributions 	 * to a group-source query.
3319*aca3beaaSApple OSS Distributions 	 */
3320*aca3beaaSApple OSS Distributions 	if (record_has_sources) {
3321*aca3beaaSApple OSS Distributions 		if (m == m0) {
3322*aca3beaaSApple OSS Distributions 			md = m_last(m);
3323*aca3beaaSApple OSS Distributions 			pig = (struct igmp_grouprec *)(void *)
3324*aca3beaaSApple OSS Distributions 			    (mtod(md, uint8_t *) + md->m_len - nbytes);
3325*aca3beaaSApple OSS Distributions 		} else {
3326*aca3beaaSApple OSS Distributions 			md = m_getptr(m, 0, &off);
3327*aca3beaaSApple OSS Distributions 			pig = (struct igmp_grouprec *)(void *)
3328*aca3beaaSApple OSS Distributions 			    (mtod(md, uint8_t *) + off);
3329*aca3beaaSApple OSS Distributions 		}
3330*aca3beaaSApple OSS Distributions 		msrcs = 0;
3331*aca3beaaSApple OSS Distributions 		RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, nims) {
3332*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
3333*aca3beaaSApple OSS Distributions 			char buf[MAX_IPv4_STR_LEN];
3334*aca3beaaSApple OSS Distributions 
3335*aca3beaaSApple OSS Distributions 			inet_ntop_haddr(ims->ims_haddr, buf, sizeof(buf));
3336*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: visit node %s\n", __func__, buf));
3337*aca3beaaSApple OSS Distributions #endif
3338*aca3beaaSApple OSS Distributions 			now = ims_get_mode(inm, ims, 1);
3339*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: node is %d\n", __func__, now));
3340*aca3beaaSApple OSS Distributions 			if ((now != mode) ||
3341*aca3beaaSApple OSS Distributions 			    (now == mode && mode == MCAST_UNDEFINED)) {
3342*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: skip node\n", __func__));
3343*aca3beaaSApple OSS Distributions 				continue;
3344*aca3beaaSApple OSS Distributions 			}
3345*aca3beaaSApple OSS Distributions 			if (is_source_query && ims->ims_stp == 0) {
3346*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: skip unrecorded node\n",
3347*aca3beaaSApple OSS Distributions 				    __func__));
3348*aca3beaaSApple OSS Distributions 				continue;
3349*aca3beaaSApple OSS Distributions 			}
3350*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: append node\n", __func__));
3351*aca3beaaSApple OSS Distributions 			naddr = htonl(ims->ims_haddr);
3352*aca3beaaSApple OSS Distributions 			if (!m_append(m, sizeof(in_addr_t), (void *)&naddr)) {
3353*aca3beaaSApple OSS Distributions 				if (m != m0) {
3354*aca3beaaSApple OSS Distributions 					m_freem(m);
3355*aca3beaaSApple OSS Distributions 				}
3356*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: m_append() failed.\n",
3357*aca3beaaSApple OSS Distributions 				    __func__));
3358*aca3beaaSApple OSS Distributions 				return -ENOMEM;
3359*aca3beaaSApple OSS Distributions 			}
3360*aca3beaaSApple OSS Distributions 			nbytes += sizeof(in_addr_t);
3361*aca3beaaSApple OSS Distributions 			++msrcs;
3362*aca3beaaSApple OSS Distributions 			if (msrcs == m0srcs) {
3363*aca3beaaSApple OSS Distributions 				break;
3364*aca3beaaSApple OSS Distributions 			}
3365*aca3beaaSApple OSS Distributions 		}
3366*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: msrcs is %d this packet\n", __func__,
3367*aca3beaaSApple OSS Distributions 		    msrcs));
3368*aca3beaaSApple OSS Distributions 		ig_numsrc = htons(msrcs);
3369*aca3beaaSApple OSS Distributions 		bcopy(&ig_numsrc, &pig->ig_numsrc, sizeof(ig_numsrc));
3370*aca3beaaSApple OSS Distributions 		nbytes += (msrcs * sizeof(in_addr_t));
3371*aca3beaaSApple OSS Distributions 	}
3372*aca3beaaSApple OSS Distributions 
3373*aca3beaaSApple OSS Distributions 	if (is_source_query && msrcs == 0) {
3374*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: no recorded sources to report\n", __func__));
3375*aca3beaaSApple OSS Distributions 		if (m != m0) {
3376*aca3beaaSApple OSS Distributions 			m_freem(m);
3377*aca3beaaSApple OSS Distributions 		}
3378*aca3beaaSApple OSS Distributions 		return 0;
3379*aca3beaaSApple OSS Distributions 	}
3380*aca3beaaSApple OSS Distributions 
3381*aca3beaaSApple OSS Distributions 	/*
3382*aca3beaaSApple OSS Distributions 	 * We are good to go with first packet.
3383*aca3beaaSApple OSS Distributions 	 */
3384*aca3beaaSApple OSS Distributions 	if (m != m0) {
3385*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: enqueueing first packet\n", __func__));
3386*aca3beaaSApple OSS Distributions 		m->m_pkthdr.vt_nrecs = 1;
3387*aca3beaaSApple OSS Distributions 		IF_ENQUEUE(ifq, m);
3388*aca3beaaSApple OSS Distributions 	} else {
3389*aca3beaaSApple OSS Distributions 		m->m_pkthdr.vt_nrecs++;
3390*aca3beaaSApple OSS Distributions 	}
3391*aca3beaaSApple OSS Distributions 	/*
3392*aca3beaaSApple OSS Distributions 	 * No further work needed if no source list in packet(s).
3393*aca3beaaSApple OSS Distributions 	 */
3394*aca3beaaSApple OSS Distributions 	if (!record_has_sources) {
3395*aca3beaaSApple OSS Distributions 		return nbytes;
3396*aca3beaaSApple OSS Distributions 	}
3397*aca3beaaSApple OSS Distributions 
3398*aca3beaaSApple OSS Distributions 	/*
3399*aca3beaaSApple OSS Distributions 	 * Whilst sources remain to be announced, we need to allocate
3400*aca3beaaSApple OSS Distributions 	 * a new packet and fill out as many sources as will fit.
3401*aca3beaaSApple OSS Distributions 	 * Always try for a cluster first.
3402*aca3beaaSApple OSS Distributions 	 */
3403*aca3beaaSApple OSS Distributions 	while (nims != NULL) {
3404*aca3beaaSApple OSS Distributions 		if (IF_QFULL(ifq)) {
3405*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: outbound queue full\n", __func__));
3406*aca3beaaSApple OSS Distributions 			return -ENOMEM;
3407*aca3beaaSApple OSS Distributions 		}
3408*aca3beaaSApple OSS Distributions 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3409*aca3beaaSApple OSS Distributions 		if (m) {
3410*aca3beaaSApple OSS Distributions 			m->m_data += IGMP_LEADINGSPACE;
3411*aca3beaaSApple OSS Distributions 		}
3412*aca3beaaSApple OSS Distributions 		if (m == NULL) {
3413*aca3beaaSApple OSS Distributions 			m = m_gethdr(M_DONTWAIT, MT_DATA);
3414*aca3beaaSApple OSS Distributions 			if (m) {
3415*aca3beaaSApple OSS Distributions 				MH_ALIGN(m, IGMP_LEADINGSPACE);
3416*aca3beaaSApple OSS Distributions 			}
3417*aca3beaaSApple OSS Distributions 		}
3418*aca3beaaSApple OSS Distributions 		if (m == NULL) {
3419*aca3beaaSApple OSS Distributions 			return -ENOMEM;
3420*aca3beaaSApple OSS Distributions 		}
3421*aca3beaaSApple OSS Distributions 		igmp_save_context(m, ifp);
3422*aca3beaaSApple OSS Distributions 		md = m_getptr(m, 0, &off);
3423*aca3beaaSApple OSS Distributions 		pig = (struct igmp_grouprec *)(void *)
3424*aca3beaaSApple OSS Distributions 		    (mtod(md, uint8_t *) + off);
3425*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: allocated next packet\n", __func__));
3426*aca3beaaSApple OSS Distributions 
3427*aca3beaaSApple OSS Distributions 		if (!m_append(m, sizeof(struct igmp_grouprec), (void *)&ig)) {
3428*aca3beaaSApple OSS Distributions 			if (m != m0) {
3429*aca3beaaSApple OSS Distributions 				m_freem(m);
3430*aca3beaaSApple OSS Distributions 			}
3431*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: m_append() failed.\n", __func__));
3432*aca3beaaSApple OSS Distributions 			return -ENOMEM;
3433*aca3beaaSApple OSS Distributions 		}
3434*aca3beaaSApple OSS Distributions 		m->m_pkthdr.vt_nrecs = 1;
3435*aca3beaaSApple OSS Distributions 		nbytes += sizeof(struct igmp_grouprec);
3436*aca3beaaSApple OSS Distributions 
3437*aca3beaaSApple OSS Distributions 		m0srcs = (ifp->if_mtu - IGMP_LEADINGSPACE -
3438*aca3beaaSApple OSS Distributions 		    sizeof(struct igmp_grouprec)) / sizeof(in_addr_t);
3439*aca3beaaSApple OSS Distributions 
3440*aca3beaaSApple OSS Distributions 		msrcs = 0;
3441*aca3beaaSApple OSS Distributions 		RB_FOREACH_FROM(ims, ip_msource_tree, nims) {
3442*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
3443*aca3beaaSApple OSS Distributions 			char buf[MAX_IPv4_STR_LEN];
3444*aca3beaaSApple OSS Distributions 
3445*aca3beaaSApple OSS Distributions 			inet_ntop_haddr(ims->ims_haddr, buf, sizeof(buf));
3446*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: visit node %s\n", __func__, buf));
3447*aca3beaaSApple OSS Distributions #endif
3448*aca3beaaSApple OSS Distributions 			now = ims_get_mode(inm, ims, 1);
3449*aca3beaaSApple OSS Distributions 			if ((now != mode) ||
3450*aca3beaaSApple OSS Distributions 			    (now == mode && mode == MCAST_UNDEFINED)) {
3451*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: skip node\n", __func__));
3452*aca3beaaSApple OSS Distributions 				continue;
3453*aca3beaaSApple OSS Distributions 			}
3454*aca3beaaSApple OSS Distributions 			if (is_source_query && ims->ims_stp == 0) {
3455*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: skip unrecorded node\n",
3456*aca3beaaSApple OSS Distributions 				    __func__));
3457*aca3beaaSApple OSS Distributions 				continue;
3458*aca3beaaSApple OSS Distributions 			}
3459*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: append node\n", __func__));
3460*aca3beaaSApple OSS Distributions 			naddr = htonl(ims->ims_haddr);
3461*aca3beaaSApple OSS Distributions 			if (!m_append(m, sizeof(in_addr_t), (void *)&naddr)) {
3462*aca3beaaSApple OSS Distributions 				if (m != m0) {
3463*aca3beaaSApple OSS Distributions 					m_freem(m);
3464*aca3beaaSApple OSS Distributions 				}
3465*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: m_append() failed.\n",
3466*aca3beaaSApple OSS Distributions 				    __func__));
3467*aca3beaaSApple OSS Distributions 				return -ENOMEM;
3468*aca3beaaSApple OSS Distributions 			}
3469*aca3beaaSApple OSS Distributions 			++msrcs;
3470*aca3beaaSApple OSS Distributions 			if (msrcs == m0srcs) {
3471*aca3beaaSApple OSS Distributions 				break;
3472*aca3beaaSApple OSS Distributions 			}
3473*aca3beaaSApple OSS Distributions 		}
3474*aca3beaaSApple OSS Distributions 		ig_numsrc = htons(msrcs);
3475*aca3beaaSApple OSS Distributions 		bcopy(&ig_numsrc, &pig->ig_numsrc, sizeof(ig_numsrc));
3476*aca3beaaSApple OSS Distributions 		nbytes += (msrcs * sizeof(in_addr_t));
3477*aca3beaaSApple OSS Distributions 
3478*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: enqueueing next packet\n", __func__));
3479*aca3beaaSApple OSS Distributions 		IF_ENQUEUE(ifq, m);
3480*aca3beaaSApple OSS Distributions 	}
3481*aca3beaaSApple OSS Distributions 
3482*aca3beaaSApple OSS Distributions 	return nbytes;
3483*aca3beaaSApple OSS Distributions }
3484*aca3beaaSApple OSS Distributions 
3485*aca3beaaSApple OSS Distributions /*
3486*aca3beaaSApple OSS Distributions  * Type used to mark record pass completion.
3487*aca3beaaSApple OSS Distributions  * We exploit the fact we can cast to this easily from the
3488*aca3beaaSApple OSS Distributions  * current filter modes on each ip_msource node.
3489*aca3beaaSApple OSS Distributions  */
3490*aca3beaaSApple OSS Distributions typedef enum {
3491*aca3beaaSApple OSS Distributions 	REC_NONE = 0x00,        /* MCAST_UNDEFINED */
3492*aca3beaaSApple OSS Distributions 	REC_ALLOW = 0x01,       /* MCAST_INCLUDE */
3493*aca3beaaSApple OSS Distributions 	REC_BLOCK = 0x02,       /* MCAST_EXCLUDE */
3494*aca3beaaSApple OSS Distributions 	REC_FULL = REC_ALLOW | REC_BLOCK
3495*aca3beaaSApple OSS Distributions } rectype_t;
3496*aca3beaaSApple OSS Distributions 
3497*aca3beaaSApple OSS Distributions /*
3498*aca3beaaSApple OSS Distributions  * Enqueue an IGMPv3 filter list change to the given output queue.
3499*aca3beaaSApple OSS Distributions  *
3500*aca3beaaSApple OSS Distributions  * Source list filter state is held in an RB-tree. When the filter list
3501*aca3beaaSApple OSS Distributions  * for a group is changed without changing its mode, we need to compute
3502*aca3beaaSApple OSS Distributions  * the deltas between T0 and T1 for each source in the filter set,
3503*aca3beaaSApple OSS Distributions  * and enqueue the appropriate ALLOW_NEW/BLOCK_OLD records.
3504*aca3beaaSApple OSS Distributions  *
3505*aca3beaaSApple OSS Distributions  * As we may potentially queue two record types, and the entire R-B tree
3506*aca3beaaSApple OSS Distributions  * needs to be walked at once, we break this out into its own function
3507*aca3beaaSApple OSS Distributions  * so we can generate a tightly packed queue of packets.
3508*aca3beaaSApple OSS Distributions  *
3509*aca3beaaSApple OSS Distributions  * XXX This could be written to only use one tree walk, although that makes
3510*aca3beaaSApple OSS Distributions  * serializing into the mbuf chains a bit harder. For now we do two walks
3511*aca3beaaSApple OSS Distributions  * which makes things easier on us, and it may or may not be harder on
3512*aca3beaaSApple OSS Distributions  * the L2 cache.
3513*aca3beaaSApple OSS Distributions  *
3514*aca3beaaSApple OSS Distributions  * If successful the size of all data appended to the queue is returned,
3515*aca3beaaSApple OSS Distributions  * otherwise an error code less than zero is returned, or zero if
3516*aca3beaaSApple OSS Distributions  * no record(s) were appended.
3517*aca3beaaSApple OSS Distributions  */
3518*aca3beaaSApple OSS Distributions static int
igmp_v3_enqueue_filter_change(struct ifqueue * ifq,struct in_multi * inm)3519*aca3beaaSApple OSS Distributions igmp_v3_enqueue_filter_change(struct ifqueue *ifq, struct in_multi *inm)
3520*aca3beaaSApple OSS Distributions {
3521*aca3beaaSApple OSS Distributions 	static const int MINRECLEN =
3522*aca3beaaSApple OSS Distributions 	    sizeof(struct igmp_grouprec) + sizeof(in_addr_t);
3523*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
3524*aca3beaaSApple OSS Distributions 	struct igmp_grouprec     ig;
3525*aca3beaaSApple OSS Distributions 	struct igmp_grouprec    *pig;
3526*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *nims;
3527*aca3beaaSApple OSS Distributions 	struct mbuf             *m, *m0, *md;
3528*aca3beaaSApple OSS Distributions 	in_addr_t                naddr;
3529*aca3beaaSApple OSS Distributions 	int                      m0srcs, nbytes, npbytes, off, schanged;
3530*aca3beaaSApple OSS Distributions 	uint16_t                 rsrcs;
3531*aca3beaaSApple OSS Distributions 	int                      nallow, nblock;
3532*aca3beaaSApple OSS Distributions 	uint16_t                 mode;
3533*aca3beaaSApple OSS Distributions 	uint8_t                  now, then;
3534*aca3beaaSApple OSS Distributions 	rectype_t                crt, drt, nrt;
3535*aca3beaaSApple OSS Distributions 	u_int16_t                ig_numsrc;
3536*aca3beaaSApple OSS Distributions 
3537*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
3538*aca3beaaSApple OSS Distributions 
3539*aca3beaaSApple OSS Distributions 	if (inm->inm_nsrc == 0 ||
3540*aca3beaaSApple OSS Distributions 	    (inm->inm_st[0].iss_asm > 0 && inm->inm_st[1].iss_asm > 0)) {
3541*aca3beaaSApple OSS Distributions 		return 0;
3542*aca3beaaSApple OSS Distributions 	}
3543*aca3beaaSApple OSS Distributions 
3544*aca3beaaSApple OSS Distributions 	ifp = inm->inm_ifp;                     /* interface */
3545*aca3beaaSApple OSS Distributions 	mode = inm->inm_st[1].iss_fmode;        /* filter mode at t1 */
3546*aca3beaaSApple OSS Distributions 	crt = REC_NONE; /* current group record type */
3547*aca3beaaSApple OSS Distributions 	drt = REC_NONE; /* mask of completed group record types */
3548*aca3beaaSApple OSS Distributions 	nrt = REC_NONE; /* record type for current node */
3549*aca3beaaSApple OSS Distributions 	m0srcs = 0;     /* # source which will fit in current mbuf chain */
3550*aca3beaaSApple OSS Distributions 	nbytes = 0;     /* # of bytes appended to group's state-change queue */
3551*aca3beaaSApple OSS Distributions 	npbytes = 0;    /* # of bytes appended this packet */
3552*aca3beaaSApple OSS Distributions 	rsrcs = 0;      /* # sources encoded in current record */
3553*aca3beaaSApple OSS Distributions 	schanged = 0;   /* # nodes encoded in overall filter change */
3554*aca3beaaSApple OSS Distributions 	nallow = 0;     /* # of source entries in ALLOW_NEW */
3555*aca3beaaSApple OSS Distributions 	nblock = 0;     /* # of source entries in BLOCK_OLD */
3556*aca3beaaSApple OSS Distributions 	nims = NULL;    /* next tree node pointer */
3557*aca3beaaSApple OSS Distributions 
3558*aca3beaaSApple OSS Distributions 	/*
3559*aca3beaaSApple OSS Distributions 	 * For each possible filter record mode.
3560*aca3beaaSApple OSS Distributions 	 * The first kind of source we encounter tells us which
3561*aca3beaaSApple OSS Distributions 	 * is the first kind of record we start appending.
3562*aca3beaaSApple OSS Distributions 	 * If a node transitioned to UNDEFINED at t1, its mode is treated
3563*aca3beaaSApple OSS Distributions 	 * as the inverse of the group's filter mode.
3564*aca3beaaSApple OSS Distributions 	 */
3565*aca3beaaSApple OSS Distributions 	while (drt != REC_FULL) {
3566*aca3beaaSApple OSS Distributions 		do {
3567*aca3beaaSApple OSS Distributions 			m0 = ifq->ifq_tail;
3568*aca3beaaSApple OSS Distributions 			if (m0 != NULL &&
3569*aca3beaaSApple OSS Distributions 			    (m0->m_pkthdr.vt_nrecs + 1 <=
3570*aca3beaaSApple OSS Distributions 			    IGMP_V3_REPORT_MAXRECS) &&
3571*aca3beaaSApple OSS Distributions 			    (m0->m_pkthdr.len + MINRECLEN) <
3572*aca3beaaSApple OSS Distributions 			    (ifp->if_mtu - IGMP_LEADINGSPACE)) {
3573*aca3beaaSApple OSS Distributions 				m = m0;
3574*aca3beaaSApple OSS Distributions 				m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
3575*aca3beaaSApple OSS Distributions 				    sizeof(struct igmp_grouprec)) /
3576*aca3beaaSApple OSS Distributions 				    sizeof(in_addr_t);
3577*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: use previous packet\n",
3578*aca3beaaSApple OSS Distributions 				    __func__));
3579*aca3beaaSApple OSS Distributions 			} else {
3580*aca3beaaSApple OSS Distributions 				m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
3581*aca3beaaSApple OSS Distributions 				if (m) {
3582*aca3beaaSApple OSS Distributions 					m->m_data += IGMP_LEADINGSPACE;
3583*aca3beaaSApple OSS Distributions 				}
3584*aca3beaaSApple OSS Distributions 				if (m == NULL) {
3585*aca3beaaSApple OSS Distributions 					m = m_gethdr(M_DONTWAIT, MT_DATA);
3586*aca3beaaSApple OSS Distributions 					if (m) {
3587*aca3beaaSApple OSS Distributions 						MH_ALIGN(m, IGMP_LEADINGSPACE);
3588*aca3beaaSApple OSS Distributions 					}
3589*aca3beaaSApple OSS Distributions 				}
3590*aca3beaaSApple OSS Distributions 				if (m == NULL) {
3591*aca3beaaSApple OSS Distributions 					IGMP_PRINTF(("%s: m_get*() failed\n",
3592*aca3beaaSApple OSS Distributions 					    __func__));
3593*aca3beaaSApple OSS Distributions 					return -ENOMEM;
3594*aca3beaaSApple OSS Distributions 				}
3595*aca3beaaSApple OSS Distributions 				m->m_pkthdr.vt_nrecs = 0;
3596*aca3beaaSApple OSS Distributions 				igmp_save_context(m, ifp);
3597*aca3beaaSApple OSS Distributions 				m0srcs = (ifp->if_mtu - IGMP_LEADINGSPACE -
3598*aca3beaaSApple OSS Distributions 				    sizeof(struct igmp_grouprec)) /
3599*aca3beaaSApple OSS Distributions 				    sizeof(in_addr_t);
3600*aca3beaaSApple OSS Distributions 				npbytes = 0;
3601*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: allocated new packet\n",
3602*aca3beaaSApple OSS Distributions 				    __func__));
3603*aca3beaaSApple OSS Distributions 			}
3604*aca3beaaSApple OSS Distributions 			/*
3605*aca3beaaSApple OSS Distributions 			 * Append the IGMP group record header to the
3606*aca3beaaSApple OSS Distributions 			 * current packet's data area.
3607*aca3beaaSApple OSS Distributions 			 * Recalculate pointer to free space for next
3608*aca3beaaSApple OSS Distributions 			 * group record, in case m_append() allocated
3609*aca3beaaSApple OSS Distributions 			 * a new mbuf or cluster.
3610*aca3beaaSApple OSS Distributions 			 */
3611*aca3beaaSApple OSS Distributions 			memset(&ig, 0, sizeof(ig));
3612*aca3beaaSApple OSS Distributions 			ig.ig_group = inm->inm_addr;
3613*aca3beaaSApple OSS Distributions 			if (!m_append(m, sizeof(ig), (void *)&ig)) {
3614*aca3beaaSApple OSS Distributions 				if (m != m0) {
3615*aca3beaaSApple OSS Distributions 					m_freem(m);
3616*aca3beaaSApple OSS Distributions 				}
3617*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: m_append() failed\n",
3618*aca3beaaSApple OSS Distributions 				    __func__));
3619*aca3beaaSApple OSS Distributions 				return -ENOMEM;
3620*aca3beaaSApple OSS Distributions 			}
3621*aca3beaaSApple OSS Distributions 			npbytes += sizeof(struct igmp_grouprec);
3622*aca3beaaSApple OSS Distributions 			if (m != m0) {
3623*aca3beaaSApple OSS Distributions 				/* new packet; offset in c hain */
3624*aca3beaaSApple OSS Distributions 				md = m_getptr(m, npbytes -
3625*aca3beaaSApple OSS Distributions 				    sizeof(struct igmp_grouprec), &off);
3626*aca3beaaSApple OSS Distributions 				pig = (struct igmp_grouprec *)(void *)(mtod(md,
3627*aca3beaaSApple OSS Distributions 				    uint8_t *) + off);
3628*aca3beaaSApple OSS Distributions 			} else {
3629*aca3beaaSApple OSS Distributions 				/* current packet; offset from last append */
3630*aca3beaaSApple OSS Distributions 				md = m_last(m);
3631*aca3beaaSApple OSS Distributions 				pig = (struct igmp_grouprec *)(void *)(mtod(md,
3632*aca3beaaSApple OSS Distributions 				    uint8_t *) + md->m_len -
3633*aca3beaaSApple OSS Distributions 				    sizeof(struct igmp_grouprec));
3634*aca3beaaSApple OSS Distributions 			}
3635*aca3beaaSApple OSS Distributions 			/*
3636*aca3beaaSApple OSS Distributions 			 * Begin walking the tree for this record type
3637*aca3beaaSApple OSS Distributions 			 * pass, or continue from where we left off
3638*aca3beaaSApple OSS Distributions 			 * previously if we had to allocate a new packet.
3639*aca3beaaSApple OSS Distributions 			 * Only report deltas in-mode at t1.
3640*aca3beaaSApple OSS Distributions 			 * We need not report included sources as allowed
3641*aca3beaaSApple OSS Distributions 			 * if we are in inclusive mode on the group,
3642*aca3beaaSApple OSS Distributions 			 * however the converse is not true.
3643*aca3beaaSApple OSS Distributions 			 */
3644*aca3beaaSApple OSS Distributions 			rsrcs = 0;
3645*aca3beaaSApple OSS Distributions 			if (nims == NULL) {
3646*aca3beaaSApple OSS Distributions 				nims = RB_MIN(ip_msource_tree, &inm->inm_srcs);
3647*aca3beaaSApple OSS Distributions 			}
3648*aca3beaaSApple OSS Distributions 			RB_FOREACH_FROM(ims, ip_msource_tree, nims) {
3649*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
3650*aca3beaaSApple OSS Distributions 				char buf[MAX_IPv4_STR_LEN];
3651*aca3beaaSApple OSS Distributions 
3652*aca3beaaSApple OSS Distributions 				inet_ntop_haddr(ims->ims_haddr, buf, sizeof(buf));
3653*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: visit node %s\n", __func__, buf));
3654*aca3beaaSApple OSS Distributions #endif
3655*aca3beaaSApple OSS Distributions 				now = ims_get_mode(inm, ims, 1);
3656*aca3beaaSApple OSS Distributions 				then = ims_get_mode(inm, ims, 0);
3657*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: mode: t0 %d, t1 %d\n",
3658*aca3beaaSApple OSS Distributions 				    __func__, then, now));
3659*aca3beaaSApple OSS Distributions 				if (now == then) {
3660*aca3beaaSApple OSS Distributions 					IGMP_PRINTF(("%s: skip unchanged\n",
3661*aca3beaaSApple OSS Distributions 					    __func__));
3662*aca3beaaSApple OSS Distributions 					continue;
3663*aca3beaaSApple OSS Distributions 				}
3664*aca3beaaSApple OSS Distributions 				if (mode == MCAST_EXCLUDE &&
3665*aca3beaaSApple OSS Distributions 				    now == MCAST_INCLUDE) {
3666*aca3beaaSApple OSS Distributions 					IGMP_PRINTF(("%s: skip IN src on EX "
3667*aca3beaaSApple OSS Distributions 					    "group\n", __func__));
3668*aca3beaaSApple OSS Distributions 					continue;
3669*aca3beaaSApple OSS Distributions 				}
3670*aca3beaaSApple OSS Distributions 				nrt = (rectype_t)now;
3671*aca3beaaSApple OSS Distributions 				if (nrt == REC_NONE) {
3672*aca3beaaSApple OSS Distributions 					nrt = (rectype_t)(~mode & REC_FULL);
3673*aca3beaaSApple OSS Distributions 				}
3674*aca3beaaSApple OSS Distributions 				if (schanged++ == 0) {
3675*aca3beaaSApple OSS Distributions 					crt = nrt;
3676*aca3beaaSApple OSS Distributions 				} else if (crt != nrt) {
3677*aca3beaaSApple OSS Distributions 					continue;
3678*aca3beaaSApple OSS Distributions 				}
3679*aca3beaaSApple OSS Distributions 				naddr = htonl(ims->ims_haddr);
3680*aca3beaaSApple OSS Distributions 				if (!m_append(m, sizeof(in_addr_t),
3681*aca3beaaSApple OSS Distributions 				    (void *)&naddr)) {
3682*aca3beaaSApple OSS Distributions 					if (m != m0) {
3683*aca3beaaSApple OSS Distributions 						m_freem(m);
3684*aca3beaaSApple OSS Distributions 					}
3685*aca3beaaSApple OSS Distributions 					IGMP_PRINTF(("%s: m_append() failed\n",
3686*aca3beaaSApple OSS Distributions 					    __func__));
3687*aca3beaaSApple OSS Distributions 					return -ENOMEM;
3688*aca3beaaSApple OSS Distributions 				}
3689*aca3beaaSApple OSS Distributions 				nallow += !!(crt == REC_ALLOW);
3690*aca3beaaSApple OSS Distributions 				nblock += !!(crt == REC_BLOCK);
3691*aca3beaaSApple OSS Distributions 				if (++rsrcs == m0srcs) {
3692*aca3beaaSApple OSS Distributions 					break;
3693*aca3beaaSApple OSS Distributions 				}
3694*aca3beaaSApple OSS Distributions 			}
3695*aca3beaaSApple OSS Distributions 			/*
3696*aca3beaaSApple OSS Distributions 			 * If we did not append any tree nodes on this
3697*aca3beaaSApple OSS Distributions 			 * pass, back out of allocations.
3698*aca3beaaSApple OSS Distributions 			 */
3699*aca3beaaSApple OSS Distributions 			if (rsrcs == 0) {
3700*aca3beaaSApple OSS Distributions 				npbytes -= sizeof(struct igmp_grouprec);
3701*aca3beaaSApple OSS Distributions 				if (m != m0) {
3702*aca3beaaSApple OSS Distributions 					IGMP_PRINTF(("%s: m_free(m)\n",
3703*aca3beaaSApple OSS Distributions 					    __func__));
3704*aca3beaaSApple OSS Distributions 					m_freem(m);
3705*aca3beaaSApple OSS Distributions 				} else {
3706*aca3beaaSApple OSS Distributions 					IGMP_PRINTF(("%s: m_adj(m, -ig)\n",
3707*aca3beaaSApple OSS Distributions 					    __func__));
3708*aca3beaaSApple OSS Distributions 					m_adj(m, -((int)sizeof(
3709*aca3beaaSApple OSS Distributions 						    struct igmp_grouprec)));
3710*aca3beaaSApple OSS Distributions 				}
3711*aca3beaaSApple OSS Distributions 				continue;
3712*aca3beaaSApple OSS Distributions 			}
3713*aca3beaaSApple OSS Distributions 			npbytes += (rsrcs * sizeof(in_addr_t));
3714*aca3beaaSApple OSS Distributions 			if (crt == REC_ALLOW) {
3715*aca3beaaSApple OSS Distributions 				pig->ig_type = IGMP_ALLOW_NEW_SOURCES;
3716*aca3beaaSApple OSS Distributions 			} else if (crt == REC_BLOCK) {
3717*aca3beaaSApple OSS Distributions 				pig->ig_type = IGMP_BLOCK_OLD_SOURCES;
3718*aca3beaaSApple OSS Distributions 			}
3719*aca3beaaSApple OSS Distributions 			ig_numsrc = htons(rsrcs);
3720*aca3beaaSApple OSS Distributions 			bcopy(&ig_numsrc, &pig->ig_numsrc, sizeof(ig_numsrc));
3721*aca3beaaSApple OSS Distributions 			/*
3722*aca3beaaSApple OSS Distributions 			 * Count the new group record, and enqueue this
3723*aca3beaaSApple OSS Distributions 			 * packet if it wasn't already queued.
3724*aca3beaaSApple OSS Distributions 			 */
3725*aca3beaaSApple OSS Distributions 			m->m_pkthdr.vt_nrecs++;
3726*aca3beaaSApple OSS Distributions 			if (m != m0) {
3727*aca3beaaSApple OSS Distributions 				IF_ENQUEUE(ifq, m);
3728*aca3beaaSApple OSS Distributions 			}
3729*aca3beaaSApple OSS Distributions 			nbytes += npbytes;
3730*aca3beaaSApple OSS Distributions 		} while (nims != NULL);
3731*aca3beaaSApple OSS Distributions 		drt |= crt;
3732*aca3beaaSApple OSS Distributions 		crt = (~crt & REC_FULL);
3733*aca3beaaSApple OSS Distributions 	}
3734*aca3beaaSApple OSS Distributions 
3735*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: queued %d ALLOW_NEW, %d BLOCK_OLD\n", __func__,
3736*aca3beaaSApple OSS Distributions 	    nallow, nblock));
3737*aca3beaaSApple OSS Distributions 
3738*aca3beaaSApple OSS Distributions 	return nbytes;
3739*aca3beaaSApple OSS Distributions }
3740*aca3beaaSApple OSS Distributions 
3741*aca3beaaSApple OSS Distributions static int
igmp_v3_merge_state_changes(struct in_multi * inm,struct ifqueue * ifscq)3742*aca3beaaSApple OSS Distributions igmp_v3_merge_state_changes(struct in_multi *inm, struct ifqueue *ifscq)
3743*aca3beaaSApple OSS Distributions {
3744*aca3beaaSApple OSS Distributions 	struct ifqueue  *gq;
3745*aca3beaaSApple OSS Distributions 	struct mbuf     *m;             /* pending state-change */
3746*aca3beaaSApple OSS Distributions 	struct mbuf     *m0;            /* copy of pending state-change */
3747*aca3beaaSApple OSS Distributions 	struct mbuf     *mt;            /* last state-change in packet */
3748*aca3beaaSApple OSS Distributions 	struct mbuf     *n;
3749*aca3beaaSApple OSS Distributions 	int              docopy, domerge;
3750*aca3beaaSApple OSS Distributions 	u_int            recslen;
3751*aca3beaaSApple OSS Distributions 
3752*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
3753*aca3beaaSApple OSS Distributions 
3754*aca3beaaSApple OSS Distributions 	docopy = 0;
3755*aca3beaaSApple OSS Distributions 	domerge = 0;
3756*aca3beaaSApple OSS Distributions 	recslen = 0;
3757*aca3beaaSApple OSS Distributions 
3758*aca3beaaSApple OSS Distributions 	/*
3759*aca3beaaSApple OSS Distributions 	 * If there are further pending retransmissions, make a writable
3760*aca3beaaSApple OSS Distributions 	 * copy of each queued state-change message before merging.
3761*aca3beaaSApple OSS Distributions 	 */
3762*aca3beaaSApple OSS Distributions 	if (inm->inm_scrv > 0) {
3763*aca3beaaSApple OSS Distributions 		docopy = 1;
3764*aca3beaaSApple OSS Distributions 	}
3765*aca3beaaSApple OSS Distributions 
3766*aca3beaaSApple OSS Distributions 	gq = &inm->inm_scq;
3767*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
3768*aca3beaaSApple OSS Distributions 	if (gq->ifq_head == NULL) {
3769*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: WARNING: queue for inm 0x%llx is empty\n",
3770*aca3beaaSApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(inm)));
3771*aca3beaaSApple OSS Distributions 	}
3772*aca3beaaSApple OSS Distributions #endif
3773*aca3beaaSApple OSS Distributions 
3774*aca3beaaSApple OSS Distributions 	/*
3775*aca3beaaSApple OSS Distributions 	 * Use IF_REMQUEUE() instead of IF_DEQUEUE() below, since the
3776*aca3beaaSApple OSS Distributions 	 * packet might not always be at the head of the ifqueue.
3777*aca3beaaSApple OSS Distributions 	 */
3778*aca3beaaSApple OSS Distributions 	m = gq->ifq_head;
3779*aca3beaaSApple OSS Distributions 	while (m != NULL) {
3780*aca3beaaSApple OSS Distributions 		/*
3781*aca3beaaSApple OSS Distributions 		 * Only merge the report into the current packet if
3782*aca3beaaSApple OSS Distributions 		 * there is sufficient space to do so; an IGMPv3 report
3783*aca3beaaSApple OSS Distributions 		 * packet may only contain 65,535 group records.
3784*aca3beaaSApple OSS Distributions 		 * Always use a simple mbuf chain concatentation to do this,
3785*aca3beaaSApple OSS Distributions 		 * as large state changes for single groups may have
3786*aca3beaaSApple OSS Distributions 		 * allocated clusters.
3787*aca3beaaSApple OSS Distributions 		 */
3788*aca3beaaSApple OSS Distributions 		domerge = 0;
3789*aca3beaaSApple OSS Distributions 		mt = ifscq->ifq_tail;
3790*aca3beaaSApple OSS Distributions 		if (mt != NULL) {
3791*aca3beaaSApple OSS Distributions 			recslen = m_length(m);
3792*aca3beaaSApple OSS Distributions 
3793*aca3beaaSApple OSS Distributions 			if ((mt->m_pkthdr.vt_nrecs +
3794*aca3beaaSApple OSS Distributions 			    m->m_pkthdr.vt_nrecs <=
3795*aca3beaaSApple OSS Distributions 			    IGMP_V3_REPORT_MAXRECS) &&
3796*aca3beaaSApple OSS Distributions 			    (mt->m_pkthdr.len + recslen <=
3797*aca3beaaSApple OSS Distributions 			    (inm->inm_ifp->if_mtu - IGMP_LEADINGSPACE))) {
3798*aca3beaaSApple OSS Distributions 				domerge = 1;
3799*aca3beaaSApple OSS Distributions 			}
3800*aca3beaaSApple OSS Distributions 		}
3801*aca3beaaSApple OSS Distributions 
3802*aca3beaaSApple OSS Distributions 		if (!domerge && IF_QFULL(gq)) {
3803*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: outbound queue full, skipping whole "
3804*aca3beaaSApple OSS Distributions 			    "packet 0x%llx\n", __func__,
3805*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3806*aca3beaaSApple OSS Distributions 			n = m->m_nextpkt;
3807*aca3beaaSApple OSS Distributions 			if (!docopy) {
3808*aca3beaaSApple OSS Distributions 				IF_REMQUEUE(gq, m);
3809*aca3beaaSApple OSS Distributions 				m_freem(m);
3810*aca3beaaSApple OSS Distributions 			}
3811*aca3beaaSApple OSS Distributions 			m = n;
3812*aca3beaaSApple OSS Distributions 			continue;
3813*aca3beaaSApple OSS Distributions 		}
3814*aca3beaaSApple OSS Distributions 
3815*aca3beaaSApple OSS Distributions 		if (!docopy) {
3816*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: dequeueing 0x%llx\n", __func__,
3817*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3818*aca3beaaSApple OSS Distributions 			n = m->m_nextpkt;
3819*aca3beaaSApple OSS Distributions 			IF_REMQUEUE(gq, m);
3820*aca3beaaSApple OSS Distributions 			m0 = m;
3821*aca3beaaSApple OSS Distributions 			m = n;
3822*aca3beaaSApple OSS Distributions 		} else {
3823*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: copying 0x%llx\n", __func__,
3824*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3825*aca3beaaSApple OSS Distributions 			m0 = m_dup(m, M_NOWAIT);
3826*aca3beaaSApple OSS Distributions 			if (m0 == NULL) {
3827*aca3beaaSApple OSS Distributions 				return ENOMEM;
3828*aca3beaaSApple OSS Distributions 			}
3829*aca3beaaSApple OSS Distributions 			m0->m_nextpkt = NULL;
3830*aca3beaaSApple OSS Distributions 			m = m->m_nextpkt;
3831*aca3beaaSApple OSS Distributions 		}
3832*aca3beaaSApple OSS Distributions 
3833*aca3beaaSApple OSS Distributions 		if (!domerge) {
3834*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: queueing 0x%llx to ifscq 0x%llx)\n",
3835*aca3beaaSApple OSS Distributions 			    __func__, (uint64_t)VM_KERNEL_ADDRPERM(m0),
3836*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(ifscq)));
3837*aca3beaaSApple OSS Distributions 			IF_ENQUEUE(ifscq, m0);
3838*aca3beaaSApple OSS Distributions 		} else {
3839*aca3beaaSApple OSS Distributions 			struct mbuf *mtl;       /* last mbuf of packet mt */
3840*aca3beaaSApple OSS Distributions 
3841*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: merging 0x%llx with ifscq tail "
3842*aca3beaaSApple OSS Distributions 			    "0x%llx)\n", __func__,
3843*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m0),
3844*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(mt)));
3845*aca3beaaSApple OSS Distributions 
3846*aca3beaaSApple OSS Distributions 			mtl = m_last(mt);
3847*aca3beaaSApple OSS Distributions 			m0->m_flags &= ~M_PKTHDR;
3848*aca3beaaSApple OSS Distributions 			mt->m_pkthdr.len += recslen;
3849*aca3beaaSApple OSS Distributions 			mt->m_pkthdr.vt_nrecs +=
3850*aca3beaaSApple OSS Distributions 			    m0->m_pkthdr.vt_nrecs;
3851*aca3beaaSApple OSS Distributions 
3852*aca3beaaSApple OSS Distributions 			mtl->m_next = m0;
3853*aca3beaaSApple OSS Distributions 		}
3854*aca3beaaSApple OSS Distributions 	}
3855*aca3beaaSApple OSS Distributions 
3856*aca3beaaSApple OSS Distributions 	return 0;
3857*aca3beaaSApple OSS Distributions }
3858*aca3beaaSApple OSS Distributions 
3859*aca3beaaSApple OSS Distributions /*
3860*aca3beaaSApple OSS Distributions  * Respond to a pending IGMPv3 General Query.
3861*aca3beaaSApple OSS Distributions  */
3862*aca3beaaSApple OSS Distributions static uint32_t
igmp_v3_dispatch_general_query(struct igmp_ifinfo * igi)3863*aca3beaaSApple OSS Distributions igmp_v3_dispatch_general_query(struct igmp_ifinfo *igi)
3864*aca3beaaSApple OSS Distributions {
3865*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
3866*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
3867*aca3beaaSApple OSS Distributions 	struct in_multistep     step;
3868*aca3beaaSApple OSS Distributions 	int                      retval, loop;
3869*aca3beaaSApple OSS Distributions 
3870*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
3871*aca3beaaSApple OSS Distributions 
3872*aca3beaaSApple OSS Distributions 	VERIFY(igi->igi_version == IGMP_VERSION_3);
3873*aca3beaaSApple OSS Distributions 
3874*aca3beaaSApple OSS Distributions 	ifp = igi->igi_ifp;
3875*aca3beaaSApple OSS Distributions 	IGI_UNLOCK(igi);
3876*aca3beaaSApple OSS Distributions 
3877*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
3878*aca3beaaSApple OSS Distributions 	IN_FIRST_MULTI(step, inm);
3879*aca3beaaSApple OSS Distributions 	while (inm != NULL) {
3880*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
3881*aca3beaaSApple OSS Distributions 		if (inm->inm_ifp != ifp) {
3882*aca3beaaSApple OSS Distributions 			goto next;
3883*aca3beaaSApple OSS Distributions 		}
3884*aca3beaaSApple OSS Distributions 
3885*aca3beaaSApple OSS Distributions 		switch (inm->inm_state) {
3886*aca3beaaSApple OSS Distributions 		case IGMP_NOT_MEMBER:
3887*aca3beaaSApple OSS Distributions 		case IGMP_SILENT_MEMBER:
3888*aca3beaaSApple OSS Distributions 			break;
3889*aca3beaaSApple OSS Distributions 		case IGMP_REPORTING_MEMBER:
3890*aca3beaaSApple OSS Distributions 		case IGMP_IDLE_MEMBER:
3891*aca3beaaSApple OSS Distributions 		case IGMP_LAZY_MEMBER:
3892*aca3beaaSApple OSS Distributions 		case IGMP_SLEEPING_MEMBER:
3893*aca3beaaSApple OSS Distributions 		case IGMP_AWAKENING_MEMBER:
3894*aca3beaaSApple OSS Distributions 			inm->inm_state = IGMP_REPORTING_MEMBER;
3895*aca3beaaSApple OSS Distributions 			IGI_LOCK(igi);
3896*aca3beaaSApple OSS Distributions 			retval = igmp_v3_enqueue_group_record(&igi->igi_gq,
3897*aca3beaaSApple OSS Distributions 			    inm, 0, 0, 0);
3898*aca3beaaSApple OSS Distributions 			IGI_UNLOCK(igi);
3899*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: enqueue record = %d\n",
3900*aca3beaaSApple OSS Distributions 			    __func__, retval));
3901*aca3beaaSApple OSS Distributions 			break;
3902*aca3beaaSApple OSS Distributions 		case IGMP_G_QUERY_PENDING_MEMBER:
3903*aca3beaaSApple OSS Distributions 		case IGMP_SG_QUERY_PENDING_MEMBER:
3904*aca3beaaSApple OSS Distributions 		case IGMP_LEAVING_MEMBER:
3905*aca3beaaSApple OSS Distributions 			break;
3906*aca3beaaSApple OSS Distributions 		}
3907*aca3beaaSApple OSS Distributions next:
3908*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
3909*aca3beaaSApple OSS Distributions 		IN_NEXT_MULTI(step, inm);
3910*aca3beaaSApple OSS Distributions 	}
3911*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
3912*aca3beaaSApple OSS Distributions 
3913*aca3beaaSApple OSS Distributions 	IGI_LOCK(igi);
3914*aca3beaaSApple OSS Distributions 	loop = (igi->igi_flags & IGIF_LOOPBACK) ? 1 : 0;
3915*aca3beaaSApple OSS Distributions 	igmp_dispatch_queue(igi, &igi->igi_gq, IGMP_MAX_RESPONSE_BURST,
3916*aca3beaaSApple OSS Distributions 	    loop);
3917*aca3beaaSApple OSS Distributions 	IGI_LOCK_ASSERT_HELD(igi);
3918*aca3beaaSApple OSS Distributions 	/*
3919*aca3beaaSApple OSS Distributions 	 * Slew transmission of bursts over 1 second intervals.
3920*aca3beaaSApple OSS Distributions 	 */
3921*aca3beaaSApple OSS Distributions 	if (igi->igi_gq.ifq_head != NULL) {
3922*aca3beaaSApple OSS Distributions 		igi->igi_v3_timer = 1 + IGMP_RANDOM_DELAY(
3923*aca3beaaSApple OSS Distributions 			IGMP_RESPONSE_BURST_INTERVAL);
3924*aca3beaaSApple OSS Distributions 	}
3925*aca3beaaSApple OSS Distributions 
3926*aca3beaaSApple OSS Distributions 	return igi->igi_v3_timer;
3927*aca3beaaSApple OSS Distributions }
3928*aca3beaaSApple OSS Distributions 
3929*aca3beaaSApple OSS Distributions /*
3930*aca3beaaSApple OSS Distributions  * Transmit the next pending IGMP message in the output queue.
3931*aca3beaaSApple OSS Distributions  *
3932*aca3beaaSApple OSS Distributions  * Must not be called with inm_lock or igi_lock held.
3933*aca3beaaSApple OSS Distributions  */
3934*aca3beaaSApple OSS Distributions static void
igmp_sendpkt(struct mbuf * m)3935*aca3beaaSApple OSS Distributions igmp_sendpkt(struct mbuf *m)
3936*aca3beaaSApple OSS Distributions {
3937*aca3beaaSApple OSS Distributions 	struct ip_moptions      *imo;
3938*aca3beaaSApple OSS Distributions 	struct mbuf             *ipopts, *m0;
3939*aca3beaaSApple OSS Distributions 	int                     error;
3940*aca3beaaSApple OSS Distributions 	struct route            ro;
3941*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
3942*aca3beaaSApple OSS Distributions 
3943*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: transmit 0x%llx\n", __func__,
3944*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3945*aca3beaaSApple OSS Distributions 
3946*aca3beaaSApple OSS Distributions 	ifp = igmp_restore_context(m);
3947*aca3beaaSApple OSS Distributions 	/*
3948*aca3beaaSApple OSS Distributions 	 * Check if the ifnet is still attached.
3949*aca3beaaSApple OSS Distributions 	 */
3950*aca3beaaSApple OSS Distributions 	if (ifp == NULL || !ifnet_is_attached(ifp, 0)) {
3951*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: dropped 0x%llx as ifp went away.\n",
3952*aca3beaaSApple OSS Distributions 		    __func__, (uint64_t)VM_KERNEL_ADDRPERM(m)));
3953*aca3beaaSApple OSS Distributions 		m_freem(m);
3954*aca3beaaSApple OSS Distributions 		OSAddAtomic(1, &ipstat.ips_noroute);
3955*aca3beaaSApple OSS Distributions 		return;
3956*aca3beaaSApple OSS Distributions 	}
3957*aca3beaaSApple OSS Distributions 
3958*aca3beaaSApple OSS Distributions 	ipopts = igmp_sendra ? m_raopt : NULL;
3959*aca3beaaSApple OSS Distributions 
3960*aca3beaaSApple OSS Distributions 	imo = ip_allocmoptions(Z_WAITOK);
3961*aca3beaaSApple OSS Distributions 	if (imo == NULL) {
3962*aca3beaaSApple OSS Distributions 		m_freem(m);
3963*aca3beaaSApple OSS Distributions 		return;
3964*aca3beaaSApple OSS Distributions 	}
3965*aca3beaaSApple OSS Distributions 
3966*aca3beaaSApple OSS Distributions 	imo->imo_multicast_ttl  = 1;
3967*aca3beaaSApple OSS Distributions 	imo->imo_multicast_vif  = -1;
3968*aca3beaaSApple OSS Distributions 	imo->imo_multicast_loop = 0;
3969*aca3beaaSApple OSS Distributions 
3970*aca3beaaSApple OSS Distributions 	/*
3971*aca3beaaSApple OSS Distributions 	 * If the user requested that IGMP traffic be explicitly
3972*aca3beaaSApple OSS Distributions 	 * redirected to the loopback interface (e.g. they are running a
3973*aca3beaaSApple OSS Distributions 	 * MANET interface and the routing protocol needs to see the
3974*aca3beaaSApple OSS Distributions 	 * updates), handle this now.
3975*aca3beaaSApple OSS Distributions 	 */
3976*aca3beaaSApple OSS Distributions 	if (m->m_flags & M_IGMP_LOOP) {
3977*aca3beaaSApple OSS Distributions 		imo->imo_multicast_ifp = lo_ifp;
3978*aca3beaaSApple OSS Distributions 	} else {
3979*aca3beaaSApple OSS Distributions 		imo->imo_multicast_ifp = ifp;
3980*aca3beaaSApple OSS Distributions 	}
3981*aca3beaaSApple OSS Distributions 
3982*aca3beaaSApple OSS Distributions 	if (m->m_flags & M_IGMPV2) {
3983*aca3beaaSApple OSS Distributions 		m0 = m;
3984*aca3beaaSApple OSS Distributions 	} else {
3985*aca3beaaSApple OSS Distributions 		m0 = igmp_v3_encap_report(ifp, m);
3986*aca3beaaSApple OSS Distributions 		if (m0 == NULL) {
3987*aca3beaaSApple OSS Distributions 			/*
3988*aca3beaaSApple OSS Distributions 			 * If igmp_v3_encap_report() failed, then M_PREPEND()
3989*aca3beaaSApple OSS Distributions 			 * already freed the original mbuf chain.
3990*aca3beaaSApple OSS Distributions 			 * This means that we don't have to m_freem(m) here.
3991*aca3beaaSApple OSS Distributions 			 */
3992*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: dropped 0x%llx\n", __func__,
3993*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(m)));
3994*aca3beaaSApple OSS Distributions 			IMO_REMREF(imo);
3995*aca3beaaSApple OSS Distributions 			atomic_add_32(&ipstat.ips_odropped, 1);
3996*aca3beaaSApple OSS Distributions 			return;
3997*aca3beaaSApple OSS Distributions 		}
3998*aca3beaaSApple OSS Distributions 	}
3999*aca3beaaSApple OSS Distributions 
4000*aca3beaaSApple OSS Distributions 	igmp_scrub_context(m0);
4001*aca3beaaSApple OSS Distributions 	m->m_flags &= ~(M_PROTOFLAGS | M_IGMP_LOOP);
4002*aca3beaaSApple OSS Distributions 	m0->m_pkthdr.rcvif = lo_ifp;
4003*aca3beaaSApple OSS Distributions 
4004*aca3beaaSApple OSS Distributions 	if (ifp->if_eflags & IFEF_TXSTART) {
4005*aca3beaaSApple OSS Distributions 		/*
4006*aca3beaaSApple OSS Distributions 		 * Use control service class if the interface supports
4007*aca3beaaSApple OSS Distributions 		 * transmit-start model.
4008*aca3beaaSApple OSS Distributions 		 */
4009*aca3beaaSApple OSS Distributions 		(void) m_set_service_class(m0, MBUF_SC_CTL);
4010*aca3beaaSApple OSS Distributions 	}
4011*aca3beaaSApple OSS Distributions 	bzero(&ro, sizeof(ro));
4012*aca3beaaSApple OSS Distributions 	error = ip_output(m0, ipopts, &ro, 0, imo, NULL);
4013*aca3beaaSApple OSS Distributions 	ROUTE_RELEASE(&ro);
4014*aca3beaaSApple OSS Distributions 
4015*aca3beaaSApple OSS Distributions 	IMO_REMREF(imo);
4016*aca3beaaSApple OSS Distributions 
4017*aca3beaaSApple OSS Distributions 	if (error) {
4018*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ip_output(0x%llx) = %d\n", __func__,
4019*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(m0), error));
4020*aca3beaaSApple OSS Distributions 		return;
4021*aca3beaaSApple OSS Distributions 	}
4022*aca3beaaSApple OSS Distributions 
4023*aca3beaaSApple OSS Distributions 	IGMPSTAT_INC(igps_snd_reports);
4024*aca3beaaSApple OSS Distributions 	OIGMPSTAT_INC(igps_snd_reports);
4025*aca3beaaSApple OSS Distributions }
4026*aca3beaaSApple OSS Distributions /*
4027*aca3beaaSApple OSS Distributions  * Encapsulate an IGMPv3 report.
4028*aca3beaaSApple OSS Distributions  *
4029*aca3beaaSApple OSS Distributions  * The internal mbuf flag M_IGMPV3_HDR is used to indicate that the mbuf
4030*aca3beaaSApple OSS Distributions  * chain has already had its IP/IGMPv3 header prepended. In this case
4031*aca3beaaSApple OSS Distributions  * the function will not attempt to prepend; the lengths and checksums
4032*aca3beaaSApple OSS Distributions  * will however be re-computed.
4033*aca3beaaSApple OSS Distributions  *
4034*aca3beaaSApple OSS Distributions  * Returns a pointer to the new mbuf chain head, or NULL if the
4035*aca3beaaSApple OSS Distributions  * allocation failed.
4036*aca3beaaSApple OSS Distributions  */
4037*aca3beaaSApple OSS Distributions static struct mbuf *
igmp_v3_encap_report(struct ifnet * ifp,struct mbuf * m)4038*aca3beaaSApple OSS Distributions igmp_v3_encap_report(struct ifnet *ifp, struct mbuf *m)
4039*aca3beaaSApple OSS Distributions {
4040*aca3beaaSApple OSS Distributions 	struct igmp_report      *igmp;
4041*aca3beaaSApple OSS Distributions 	struct ip               *ip;
4042*aca3beaaSApple OSS Distributions 	unsigned int             hdrlen, igmpreclen;
4043*aca3beaaSApple OSS Distributions 
4044*aca3beaaSApple OSS Distributions 	VERIFY((m->m_flags & M_PKTHDR));
4045*aca3beaaSApple OSS Distributions 
4046*aca3beaaSApple OSS Distributions 	igmpreclen = m_length(m);
4047*aca3beaaSApple OSS Distributions 	hdrlen = sizeof(struct ip) + sizeof(struct igmp_report);
4048*aca3beaaSApple OSS Distributions 
4049*aca3beaaSApple OSS Distributions 	if (m->m_flags & M_IGMPV3_HDR) {
4050*aca3beaaSApple OSS Distributions 		igmpreclen -= hdrlen;
4051*aca3beaaSApple OSS Distributions 	} else {
4052*aca3beaaSApple OSS Distributions 		M_PREPEND(m, hdrlen, M_DONTWAIT, 1);
4053*aca3beaaSApple OSS Distributions 		if (m == NULL) {
4054*aca3beaaSApple OSS Distributions 			return NULL;
4055*aca3beaaSApple OSS Distributions 		}
4056*aca3beaaSApple OSS Distributions 		m->m_flags |= M_IGMPV3_HDR;
4057*aca3beaaSApple OSS Distributions 	}
4058*aca3beaaSApple OSS Distributions 	if (hdrlen + igmpreclen > USHRT_MAX) {
4059*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: invalid length %d\n", __func__, hdrlen + igmpreclen));
4060*aca3beaaSApple OSS Distributions 		m_freem(m);
4061*aca3beaaSApple OSS Distributions 		return NULL;
4062*aca3beaaSApple OSS Distributions 	}
4063*aca3beaaSApple OSS Distributions 
4064*aca3beaaSApple OSS Distributions 
4065*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: igmpreclen is %d\n", __func__, igmpreclen));
4066*aca3beaaSApple OSS Distributions 
4067*aca3beaaSApple OSS Distributions 	m->m_data += sizeof(struct ip);
4068*aca3beaaSApple OSS Distributions 	m->m_len -= sizeof(struct ip);
4069*aca3beaaSApple OSS Distributions 
4070*aca3beaaSApple OSS Distributions 	igmp = mtod(m, struct igmp_report *);
4071*aca3beaaSApple OSS Distributions 	igmp->ir_type = IGMP_v3_HOST_MEMBERSHIP_REPORT;
4072*aca3beaaSApple OSS Distributions 	igmp->ir_rsv1 = 0;
4073*aca3beaaSApple OSS Distributions 	igmp->ir_rsv2 = 0;
4074*aca3beaaSApple OSS Distributions 	igmp->ir_numgrps = htons(m->m_pkthdr.vt_nrecs);
4075*aca3beaaSApple OSS Distributions 	igmp->ir_cksum = 0;
4076*aca3beaaSApple OSS Distributions 	igmp->ir_cksum = in_cksum(m, sizeof(struct igmp_report) + igmpreclen);
4077*aca3beaaSApple OSS Distributions 	m->m_pkthdr.vt_nrecs = 0;
4078*aca3beaaSApple OSS Distributions 
4079*aca3beaaSApple OSS Distributions 	m->m_data -= sizeof(struct ip);
4080*aca3beaaSApple OSS Distributions 	m->m_len += sizeof(struct ip);
4081*aca3beaaSApple OSS Distributions 
4082*aca3beaaSApple OSS Distributions 	ip = mtod(m, struct ip *);
4083*aca3beaaSApple OSS Distributions 	ip->ip_tos = IPTOS_PREC_INTERNETCONTROL;
4084*aca3beaaSApple OSS Distributions 	ip->ip_len = (u_short)(hdrlen + igmpreclen);
4085*aca3beaaSApple OSS Distributions 	ip->ip_off = IP_DF;
4086*aca3beaaSApple OSS Distributions 	ip->ip_p = IPPROTO_IGMP;
4087*aca3beaaSApple OSS Distributions 	ip->ip_sum = 0;
4088*aca3beaaSApple OSS Distributions 
4089*aca3beaaSApple OSS Distributions 	ip->ip_src.s_addr = INADDR_ANY;
4090*aca3beaaSApple OSS Distributions 
4091*aca3beaaSApple OSS Distributions 	if (m->m_flags & M_IGMP_LOOP) {
4092*aca3beaaSApple OSS Distributions 		struct in_ifaddr *ia;
4093*aca3beaaSApple OSS Distributions 
4094*aca3beaaSApple OSS Distributions 		IFP_TO_IA(ifp, ia);
4095*aca3beaaSApple OSS Distributions 		if (ia != NULL) {
4096*aca3beaaSApple OSS Distributions 			IFA_LOCK(&ia->ia_ifa);
4097*aca3beaaSApple OSS Distributions 			ip->ip_src = ia->ia_addr.sin_addr;
4098*aca3beaaSApple OSS Distributions 			IFA_UNLOCK(&ia->ia_ifa);
4099*aca3beaaSApple OSS Distributions 			IFA_REMREF(&ia->ia_ifa);
4100*aca3beaaSApple OSS Distributions 		}
4101*aca3beaaSApple OSS Distributions 	}
4102*aca3beaaSApple OSS Distributions 
4103*aca3beaaSApple OSS Distributions 	ip->ip_dst.s_addr = htonl(INADDR_ALLRPTS_GROUP);
4104*aca3beaaSApple OSS Distributions 
4105*aca3beaaSApple OSS Distributions 	return m;
4106*aca3beaaSApple OSS Distributions }
4107*aca3beaaSApple OSS Distributions 
4108*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
4109*aca3beaaSApple OSS Distributions static const char *
igmp_rec_type_to_str(const int type)4110*aca3beaaSApple OSS Distributions igmp_rec_type_to_str(const int type)
4111*aca3beaaSApple OSS Distributions {
4112*aca3beaaSApple OSS Distributions 	switch (type) {
4113*aca3beaaSApple OSS Distributions 	case IGMP_CHANGE_TO_EXCLUDE_MODE:
4114*aca3beaaSApple OSS Distributions 		return "TO_EX";
4115*aca3beaaSApple OSS Distributions 	case IGMP_CHANGE_TO_INCLUDE_MODE:
4116*aca3beaaSApple OSS Distributions 		return "TO_IN";
4117*aca3beaaSApple OSS Distributions 	case IGMP_MODE_IS_EXCLUDE:
4118*aca3beaaSApple OSS Distributions 		return "MODE_EX";
4119*aca3beaaSApple OSS Distributions 	case IGMP_MODE_IS_INCLUDE:
4120*aca3beaaSApple OSS Distributions 		return "MODE_IN";
4121*aca3beaaSApple OSS Distributions 	case IGMP_ALLOW_NEW_SOURCES:
4122*aca3beaaSApple OSS Distributions 		return "ALLOW_NEW";
4123*aca3beaaSApple OSS Distributions 	case IGMP_BLOCK_OLD_SOURCES:
4124*aca3beaaSApple OSS Distributions 		return "BLOCK_OLD";
4125*aca3beaaSApple OSS Distributions 	default:
4126*aca3beaaSApple OSS Distributions 		break;
4127*aca3beaaSApple OSS Distributions 	}
4128*aca3beaaSApple OSS Distributions 	return "unknown";
4129*aca3beaaSApple OSS Distributions }
4130*aca3beaaSApple OSS Distributions #endif
4131*aca3beaaSApple OSS Distributions 
4132*aca3beaaSApple OSS Distributions void
igmp_init(struct protosw * pp,struct domain * dp)4133*aca3beaaSApple OSS Distributions igmp_init(struct protosw *pp, struct domain *dp)
4134*aca3beaaSApple OSS Distributions {
4135*aca3beaaSApple OSS Distributions #pragma unused(dp)
4136*aca3beaaSApple OSS Distributions 	static int igmp_initialized = 0;
4137*aca3beaaSApple OSS Distributions 
4138*aca3beaaSApple OSS Distributions 	VERIFY((pp->pr_flags & (PR_INITIALIZED | PR_ATTACHED)) == PR_ATTACHED);
4139*aca3beaaSApple OSS Distributions 
4140*aca3beaaSApple OSS Distributions 	if (igmp_initialized) {
4141*aca3beaaSApple OSS Distributions 		return;
4142*aca3beaaSApple OSS Distributions 	}
4143*aca3beaaSApple OSS Distributions 	igmp_initialized = 1;
4144*aca3beaaSApple OSS Distributions 
4145*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: initializing\n", __func__));
4146*aca3beaaSApple OSS Distributions 
4147*aca3beaaSApple OSS Distributions 	igmp_timers_are_running = 0;
4148*aca3beaaSApple OSS Distributions 
4149*aca3beaaSApple OSS Distributions 	LIST_INIT(&igi_head);
4150*aca3beaaSApple OSS Distributions 	m_raopt = igmp_ra_alloc();
4151*aca3beaaSApple OSS Distributions }
4152