xref: /xnu-8796.101.5/bsd/netinet/in_mcast.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1*aca3beaaSApple OSS Distributions /*
2*aca3beaaSApple OSS Distributions  * Copyright (c) 2010-2022 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) 2005 Robert N. M. Watson.
31*aca3beaaSApple OSS Distributions  * All rights reserved.
32*aca3beaaSApple OSS Distributions  *
33*aca3beaaSApple OSS Distributions  * Redistribution and use in source and binary forms, with or without
34*aca3beaaSApple OSS Distributions  * modification, are permitted provided that the following conditions
35*aca3beaaSApple OSS Distributions  * are met:
36*aca3beaaSApple OSS Distributions  * 1. Redistributions of source code must retain the above copyright
37*aca3beaaSApple OSS Distributions  *    notice, this list of conditions and the following disclaimer.
38*aca3beaaSApple OSS Distributions  * 2. Redistributions in binary form must reproduce the above copyright
39*aca3beaaSApple OSS Distributions  *    notice, this list of conditions and the following disclaimer in the
40*aca3beaaSApple OSS Distributions  *    documentation and/or other materials provided with the distribution.
41*aca3beaaSApple OSS Distributions  * 3. The name of the author may not be used to endorse or promote
42*aca3beaaSApple OSS Distributions  *    products derived from this software without specific prior written
43*aca3beaaSApple OSS Distributions  *    permission.
44*aca3beaaSApple OSS Distributions  *
45*aca3beaaSApple OSS Distributions  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46*aca3beaaSApple OSS Distributions  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47*aca3beaaSApple OSS Distributions  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48*aca3beaaSApple OSS Distributions  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49*aca3beaaSApple OSS Distributions  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50*aca3beaaSApple OSS Distributions  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51*aca3beaaSApple OSS Distributions  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52*aca3beaaSApple OSS Distributions  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53*aca3beaaSApple OSS Distributions  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54*aca3beaaSApple OSS Distributions  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55*aca3beaaSApple OSS Distributions  * SUCH DAMAGE.
56*aca3beaaSApple OSS Distributions  */
57*aca3beaaSApple OSS Distributions 
58*aca3beaaSApple OSS Distributions /*
59*aca3beaaSApple OSS Distributions  * IPv4 multicast socket, group, and socket option processing module.
60*aca3beaaSApple OSS Distributions  */
61*aca3beaaSApple OSS Distributions 
62*aca3beaaSApple OSS Distributions #include <sys/cdefs.h>
63*aca3beaaSApple OSS Distributions 
64*aca3beaaSApple OSS Distributions #include <sys/param.h>
65*aca3beaaSApple OSS Distributions #include <sys/systm.h>
66*aca3beaaSApple OSS Distributions #include <sys/kernel.h>
67*aca3beaaSApple OSS Distributions #include <sys/malloc.h>
68*aca3beaaSApple OSS Distributions #include <sys/mbuf.h>
69*aca3beaaSApple OSS Distributions #include <sys/protosw.h>
70*aca3beaaSApple OSS Distributions #include <sys/socket.h>
71*aca3beaaSApple OSS Distributions #include <sys/socketvar.h>
72*aca3beaaSApple OSS Distributions #include <sys/protosw.h>
73*aca3beaaSApple OSS Distributions #include <sys/sysctl.h>
74*aca3beaaSApple OSS Distributions #include <sys/tree.h>
75*aca3beaaSApple OSS Distributions #include <sys/mcache.h>
76*aca3beaaSApple OSS Distributions 
77*aca3beaaSApple OSS Distributions #include <kern/zalloc.h>
78*aca3beaaSApple OSS Distributions 
79*aca3beaaSApple OSS Distributions #include <pexpert/pexpert.h>
80*aca3beaaSApple OSS Distributions 
81*aca3beaaSApple OSS Distributions #include <net/if.h>
82*aca3beaaSApple OSS Distributions #include <net/if_dl.h>
83*aca3beaaSApple OSS Distributions #include <net/net_api_stats.h>
84*aca3beaaSApple OSS Distributions #include <net/route.h>
85*aca3beaaSApple OSS Distributions 
86*aca3beaaSApple OSS Distributions #include <netinet/in.h>
87*aca3beaaSApple OSS Distributions #include <netinet/in_systm.h>
88*aca3beaaSApple OSS Distributions #include <netinet/in_pcb.h>
89*aca3beaaSApple OSS Distributions #include <netinet/in_var.h>
90*aca3beaaSApple OSS Distributions #include <netinet/ip_var.h>
91*aca3beaaSApple OSS Distributions #include <netinet/igmp_var.h>
92*aca3beaaSApple OSS Distributions 
93*aca3beaaSApple OSS Distributions /*
94*aca3beaaSApple OSS Distributions  * Functions with non-static linkage defined in this file should be
95*aca3beaaSApple OSS Distributions  * declared in in_var.h:
96*aca3beaaSApple OSS Distributions  *  imo_multi_filter()
97*aca3beaaSApple OSS Distributions  *  in_addmulti()
98*aca3beaaSApple OSS Distributions  *  in_delmulti()
99*aca3beaaSApple OSS Distributions  *  in_joingroup()
100*aca3beaaSApple OSS Distributions  *  in_leavegroup()
101*aca3beaaSApple OSS Distributions  * and ip_var.h:
102*aca3beaaSApple OSS Distributions  *  inp_freemoptions()
103*aca3beaaSApple OSS Distributions  *  inp_getmoptions()
104*aca3beaaSApple OSS Distributions  *  inp_setmoptions()
105*aca3beaaSApple OSS Distributions  *
106*aca3beaaSApple OSS Distributions  * XXX: Both carp and pf need to use the legacy (*,G) KPIs in_addmulti()
107*aca3beaaSApple OSS Distributions  * and in_delmulti().
108*aca3beaaSApple OSS Distributions  */
109*aca3beaaSApple OSS Distributions static void     imf_commit(struct in_mfilter *);
110*aca3beaaSApple OSS Distributions static int      imf_get_source(struct in_mfilter *imf,
111*aca3beaaSApple OSS Distributions     const struct sockaddr_in *psin,
112*aca3beaaSApple OSS Distributions     struct in_msource **);
113*aca3beaaSApple OSS Distributions static struct in_msource *
114*aca3beaaSApple OSS Distributions imf_graft(struct in_mfilter *, const uint8_t,
115*aca3beaaSApple OSS Distributions     const struct sockaddr_in *);
116*aca3beaaSApple OSS Distributions static int      imf_prune(struct in_mfilter *, const struct sockaddr_in *);
117*aca3beaaSApple OSS Distributions static void     imf_rollback(struct in_mfilter *);
118*aca3beaaSApple OSS Distributions static void     imf_reap(struct in_mfilter *);
119*aca3beaaSApple OSS Distributions static int      imo_grow(struct ip_moptions *, uint16_t);
120*aca3beaaSApple OSS Distributions static size_t   imo_match_group(const struct ip_moptions *,
121*aca3beaaSApple OSS Distributions     const struct ifnet *, const struct sockaddr_in *);
122*aca3beaaSApple OSS Distributions static struct in_msource *
123*aca3beaaSApple OSS Distributions imo_match_source(const struct ip_moptions *, const size_t,
124*aca3beaaSApple OSS Distributions     const struct sockaddr_in *);
125*aca3beaaSApple OSS Distributions static void     ims_merge(struct ip_msource *ims,
126*aca3beaaSApple OSS Distributions     const struct in_msource *lims, const int rollback);
127*aca3beaaSApple OSS Distributions static int      in_getmulti(struct ifnet *, const struct in_addr *,
128*aca3beaaSApple OSS Distributions     struct in_multi **);
129*aca3beaaSApple OSS Distributions static int      in_joingroup(struct ifnet *, const struct in_addr *,
130*aca3beaaSApple OSS Distributions     struct in_mfilter *, struct in_multi **);
131*aca3beaaSApple OSS Distributions static int      inm_get_source(struct in_multi *inm, const in_addr_t haddr,
132*aca3beaaSApple OSS Distributions     const int noalloc, struct ip_msource **pims);
133*aca3beaaSApple OSS Distributions static int      inm_is_ifp_detached(const struct in_multi *);
134*aca3beaaSApple OSS Distributions static int      inm_merge(struct in_multi *, /*const*/ struct in_mfilter *);
135*aca3beaaSApple OSS Distributions static void     inm_reap(struct in_multi *);
136*aca3beaaSApple OSS Distributions static struct ip_moptions *
137*aca3beaaSApple OSS Distributions inp_findmoptions(struct inpcb *);
138*aca3beaaSApple OSS Distributions static int      inp_get_source_filters(struct inpcb *, struct sockopt *);
139*aca3beaaSApple OSS Distributions static struct ifnet *
140*aca3beaaSApple OSS Distributions inp_lookup_mcast_ifp(const struct inpcb *,
141*aca3beaaSApple OSS Distributions     const struct sockaddr_in *, const struct in_addr);
142*aca3beaaSApple OSS Distributions static int      inp_block_unblock_source(struct inpcb *, struct sockopt *);
143*aca3beaaSApple OSS Distributions static int      inp_set_multicast_if(struct inpcb *, struct sockopt *);
144*aca3beaaSApple OSS Distributions static int      inp_set_source_filters(struct inpcb *, struct sockopt *);
145*aca3beaaSApple OSS Distributions static int      sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS;
146*aca3beaaSApple OSS Distributions static struct ifnet * ip_multicast_if(struct in_addr *, unsigned int *);
147*aca3beaaSApple OSS Distributions static __inline__ int ip_msource_cmp(const struct ip_msource *,
148*aca3beaaSApple OSS Distributions     const struct ip_msource *);
149*aca3beaaSApple OSS Distributions 
150*aca3beaaSApple OSS Distributions SYSCTL_NODE(_net_inet_ip, OID_AUTO, mcast, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "IPv4 multicast");
151*aca3beaaSApple OSS Distributions 
152*aca3beaaSApple OSS Distributions static u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER;
153*aca3beaaSApple OSS Distributions SYSCTL_LONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc,
154*aca3beaaSApple OSS Distributions     CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_maxgrpsrc, "Max source filters per group");
155*aca3beaaSApple OSS Distributions 
156*aca3beaaSApple OSS Distributions static u_int in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER;
157*aca3beaaSApple OSS Distributions SYSCTL_UINT(_net_inet_ip_mcast, OID_AUTO, maxsocksrc,
158*aca3beaaSApple OSS Distributions     CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_maxsocksrc, IP_MAX_SOCK_SRC_FILTER,
159*aca3beaaSApple OSS Distributions     "Max source filters per socket");
160*aca3beaaSApple OSS Distributions 
161*aca3beaaSApple OSS Distributions int in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP;
162*aca3beaaSApple OSS Distributions SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_LOCKED,
163*aca3beaaSApple OSS Distributions     &in_mcast_loop, 0, "Loopback multicast datagrams by default");
164*aca3beaaSApple OSS Distributions 
165*aca3beaaSApple OSS Distributions SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters,
166*aca3beaaSApple OSS Distributions     CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_ip_mcast_filters,
167*aca3beaaSApple OSS Distributions     "Per-interface stack-wide source filters");
168*aca3beaaSApple OSS Distributions 
169*aca3beaaSApple OSS Distributions RB_GENERATE_PREV(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp);
170*aca3beaaSApple OSS Distributions 
171*aca3beaaSApple OSS Distributions #define INM_TRACE_HIST_SIZE     32      /* size of trace history */
172*aca3beaaSApple OSS Distributions 
173*aca3beaaSApple OSS Distributions /* For gdb */
174*aca3beaaSApple OSS Distributions __private_extern__ unsigned int inm_trace_hist_size = INM_TRACE_HIST_SIZE;
175*aca3beaaSApple OSS Distributions 
176*aca3beaaSApple OSS Distributions struct in_multi_dbg {
177*aca3beaaSApple OSS Distributions 	struct in_multi         inm;                    /* in_multi */
178*aca3beaaSApple OSS Distributions 	u_int16_t               inm_refhold_cnt;        /* # of ref */
179*aca3beaaSApple OSS Distributions 	u_int16_t               inm_refrele_cnt;        /* # of rele */
180*aca3beaaSApple OSS Distributions 	/*
181*aca3beaaSApple OSS Distributions 	 * Circular lists of inm_addref and inm_remref callers.
182*aca3beaaSApple OSS Distributions 	 */
183*aca3beaaSApple OSS Distributions 	ctrace_t                inm_refhold[INM_TRACE_HIST_SIZE];
184*aca3beaaSApple OSS Distributions 	ctrace_t                inm_refrele[INM_TRACE_HIST_SIZE];
185*aca3beaaSApple OSS Distributions 	/*
186*aca3beaaSApple OSS Distributions 	 * Trash list linkage
187*aca3beaaSApple OSS Distributions 	 */
188*aca3beaaSApple OSS Distributions 	TAILQ_ENTRY(in_multi_dbg) inm_trash_link;
189*aca3beaaSApple OSS Distributions };
190*aca3beaaSApple OSS Distributions 
191*aca3beaaSApple OSS Distributions static LCK_ATTR_DECLARE(in_multihead_lock_attr, 0, 0);
192*aca3beaaSApple OSS Distributions static LCK_GRP_DECLARE(in_multihead_lock_grp, "in_multihead");
193*aca3beaaSApple OSS Distributions 
194*aca3beaaSApple OSS Distributions /* List of trash in_multi entries protected by inm_trash_lock */
195*aca3beaaSApple OSS Distributions static TAILQ_HEAD(, in_multi_dbg) inm_trash_head;
196*aca3beaaSApple OSS Distributions static LCK_MTX_DECLARE_ATTR(inm_trash_lock, &in_multihead_lock_grp,
197*aca3beaaSApple OSS Distributions     &in_multihead_lock_attr);
198*aca3beaaSApple OSS Distributions 
199*aca3beaaSApple OSS Distributions #if DEBUG
200*aca3beaaSApple OSS Distributions static TUNABLE(bool, inm_debug, "ifa_debug", true); /* debugging (enabled) */
201*aca3beaaSApple OSS Distributions #else
202*aca3beaaSApple OSS Distributions static TUNABLE(bool, inm_debug, "ifa_debug", false); /* debugging (disabled) */
203*aca3beaaSApple OSS Distributions #endif /* !DEBUG */
204*aca3beaaSApple OSS Distributions #define INM_ZONE_NAME           "in_multi"      /* zone name */
205*aca3beaaSApple OSS Distributions static struct zone *inm_zone;                   /* zone for in_multi */
206*aca3beaaSApple OSS Distributions 
207*aca3beaaSApple OSS Distributions static KALLOC_TYPE_DEFINE(ipms_zone, struct ip_msource, NET_KT_DEFAULT);
208*aca3beaaSApple OSS Distributions static KALLOC_TYPE_DEFINE(inms_zone, struct in_msource, NET_KT_DEFAULT);
209*aca3beaaSApple OSS Distributions 
210*aca3beaaSApple OSS Distributions static LCK_RW_DECLARE_ATTR(in_multihead_lock, &in_multihead_lock_grp,
211*aca3beaaSApple OSS Distributions     &in_multihead_lock_attr);
212*aca3beaaSApple OSS Distributions 
213*aca3beaaSApple OSS Distributions struct in_multihead in_multihead;
214*aca3beaaSApple OSS Distributions 
215*aca3beaaSApple OSS Distributions static struct in_multi *in_multi_alloc(zalloc_flags_t);
216*aca3beaaSApple OSS Distributions static void in_multi_free(struct in_multi *);
217*aca3beaaSApple OSS Distributions static void in_multi_attach(struct in_multi *);
218*aca3beaaSApple OSS Distributions static void inm_trace(struct in_multi *, int);
219*aca3beaaSApple OSS Distributions 
220*aca3beaaSApple OSS Distributions static struct ip_msource *ipms_alloc(zalloc_flags_t);
221*aca3beaaSApple OSS Distributions static void ipms_free(struct ip_msource *);
222*aca3beaaSApple OSS Distributions static struct in_msource *inms_alloc(zalloc_flags_t);
223*aca3beaaSApple OSS Distributions static void inms_free(struct in_msource *);
224*aca3beaaSApple OSS Distributions 
225*aca3beaaSApple OSS Distributions static __inline int
ip_msource_cmp(const struct ip_msource * a,const struct ip_msource * b)226*aca3beaaSApple OSS Distributions ip_msource_cmp(const struct ip_msource *a, const struct ip_msource *b)
227*aca3beaaSApple OSS Distributions {
228*aca3beaaSApple OSS Distributions 	if (a->ims_haddr < b->ims_haddr) {
229*aca3beaaSApple OSS Distributions 		return -1;
230*aca3beaaSApple OSS Distributions 	}
231*aca3beaaSApple OSS Distributions 	if (a->ims_haddr == b->ims_haddr) {
232*aca3beaaSApple OSS Distributions 		return 0;
233*aca3beaaSApple OSS Distributions 	}
234*aca3beaaSApple OSS Distributions 	return 1;
235*aca3beaaSApple OSS Distributions }
236*aca3beaaSApple OSS Distributions 
237*aca3beaaSApple OSS Distributions /*
238*aca3beaaSApple OSS Distributions  * Inline function which wraps assertions for a valid ifp.
239*aca3beaaSApple OSS Distributions  */
240*aca3beaaSApple OSS Distributions static __inline__ int
inm_is_ifp_detached(const struct in_multi * inm)241*aca3beaaSApple OSS Distributions inm_is_ifp_detached(const struct in_multi *inm)
242*aca3beaaSApple OSS Distributions {
243*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_ifma != NULL);
244*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_ifp == inm->inm_ifma->ifma_ifp);
245*aca3beaaSApple OSS Distributions 
246*aca3beaaSApple OSS Distributions 	return !ifnet_is_attached(inm->inm_ifp, 0);
247*aca3beaaSApple OSS Distributions }
248*aca3beaaSApple OSS Distributions 
249*aca3beaaSApple OSS Distributions /*
250*aca3beaaSApple OSS Distributions  * Initialize an in_mfilter structure to a known state at t0, t1
251*aca3beaaSApple OSS Distributions  * with an empty source filter list.
252*aca3beaaSApple OSS Distributions  */
253*aca3beaaSApple OSS Distributions static __inline__ void
imf_init(struct in_mfilter * imf,const uint8_t st0,const uint8_t st1)254*aca3beaaSApple OSS Distributions imf_init(struct in_mfilter *imf, const uint8_t st0, const uint8_t st1)
255*aca3beaaSApple OSS Distributions {
256*aca3beaaSApple OSS Distributions 	memset(imf, 0, sizeof(struct in_mfilter));
257*aca3beaaSApple OSS Distributions 	RB_INIT(&imf->imf_sources);
258*aca3beaaSApple OSS Distributions 	imf->imf_st[0] = st0;
259*aca3beaaSApple OSS Distributions 	imf->imf_st[1] = st1;
260*aca3beaaSApple OSS Distributions }
261*aca3beaaSApple OSS Distributions 
262*aca3beaaSApple OSS Distributions /*
263*aca3beaaSApple OSS Distributions  * Resize the ip_moptions vector to the next power-of-two minus 1.
264*aca3beaaSApple OSS Distributions  */
265*aca3beaaSApple OSS Distributions static int
imo_grow(struct ip_moptions * imo,uint16_t newmax)266*aca3beaaSApple OSS Distributions imo_grow(struct ip_moptions *imo, uint16_t newmax)
267*aca3beaaSApple OSS Distributions {
268*aca3beaaSApple OSS Distributions 	struct in_multi         **nmships;
269*aca3beaaSApple OSS Distributions 	struct in_multi         **omships;
270*aca3beaaSApple OSS Distributions 	struct in_mfilter        *nmfilters;
271*aca3beaaSApple OSS Distributions 	struct in_mfilter        *omfilters;
272*aca3beaaSApple OSS Distributions 	int                       err;
273*aca3beaaSApple OSS Distributions 	uint16_t                  idx;
274*aca3beaaSApple OSS Distributions 	uint16_t                  oldmax;
275*aca3beaaSApple OSS Distributions 
276*aca3beaaSApple OSS Distributions 	IMO_LOCK_ASSERT_HELD(imo);
277*aca3beaaSApple OSS Distributions 
278*aca3beaaSApple OSS Distributions 	nmships = NULL;
279*aca3beaaSApple OSS Distributions 	nmfilters = NULL;
280*aca3beaaSApple OSS Distributions 	err = 0;
281*aca3beaaSApple OSS Distributions 	omships = imo->imo_membership;
282*aca3beaaSApple OSS Distributions 	omfilters = imo->imo_mfilters;
283*aca3beaaSApple OSS Distributions 	oldmax = imo->imo_max_memberships;
284*aca3beaaSApple OSS Distributions 
285*aca3beaaSApple OSS Distributions 	if (newmax == 0) {
286*aca3beaaSApple OSS Distributions 		newmax = ((oldmax + 1) * 2) - 1;
287*aca3beaaSApple OSS Distributions 	} else if (newmax <= oldmax) {
288*aca3beaaSApple OSS Distributions 		/* Nothing to do, exit early. */
289*aca3beaaSApple OSS Distributions 		return 0;
290*aca3beaaSApple OSS Distributions 	}
291*aca3beaaSApple OSS Distributions 
292*aca3beaaSApple OSS Distributions 	if (newmax > IP_MAX_MEMBERSHIPS) {
293*aca3beaaSApple OSS Distributions 		err = ETOOMANYREFS;
294*aca3beaaSApple OSS Distributions 		goto cleanup;
295*aca3beaaSApple OSS Distributions 	}
296*aca3beaaSApple OSS Distributions 
297*aca3beaaSApple OSS Distributions 	if ((nmships = kalloc_type(struct in_multi *, newmax,
298*aca3beaaSApple OSS Distributions 	    Z_WAITOK | Z_ZERO)) == NULL) {
299*aca3beaaSApple OSS Distributions 		err = ENOMEM;
300*aca3beaaSApple OSS Distributions 		goto cleanup;
301*aca3beaaSApple OSS Distributions 	}
302*aca3beaaSApple OSS Distributions 
303*aca3beaaSApple OSS Distributions 	if ((nmfilters = kalloc_type(struct in_mfilter, newmax,
304*aca3beaaSApple OSS Distributions 	    Z_WAITOK | Z_ZERO)) == NULL) {
305*aca3beaaSApple OSS Distributions 		err = ENOMEM;
306*aca3beaaSApple OSS Distributions 		goto cleanup;
307*aca3beaaSApple OSS Distributions 	}
308*aca3beaaSApple OSS Distributions 
309*aca3beaaSApple OSS Distributions 	/* Copy the existing memberships and release the memory. */
310*aca3beaaSApple OSS Distributions 	if (omships != NULL) {
311*aca3beaaSApple OSS Distributions 		VERIFY(oldmax <= newmax);
312*aca3beaaSApple OSS Distributions 		memcpy(nmships, omships, oldmax * sizeof(struct in_multi *));
313*aca3beaaSApple OSS Distributions 		kfree_type(struct in_multi *, oldmax, omships);
314*aca3beaaSApple OSS Distributions 	}
315*aca3beaaSApple OSS Distributions 
316*aca3beaaSApple OSS Distributions 	/* Copy the existing filters and release the memory. */
317*aca3beaaSApple OSS Distributions 	if (omfilters != NULL) {
318*aca3beaaSApple OSS Distributions 		VERIFY(oldmax <= newmax);
319*aca3beaaSApple OSS Distributions 		memcpy(nmfilters, omfilters, oldmax * sizeof(struct in_mfilter));
320*aca3beaaSApple OSS Distributions 		kfree_type(struct in_mfilter, oldmax, omfilters);
321*aca3beaaSApple OSS Distributions 	}
322*aca3beaaSApple OSS Distributions 
323*aca3beaaSApple OSS Distributions 	/* Initialize the newly allocated source filter heads. */
324*aca3beaaSApple OSS Distributions 	for (idx = oldmax; idx < newmax; idx++) {
325*aca3beaaSApple OSS Distributions 		imf_init(&nmfilters[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
326*aca3beaaSApple OSS Distributions 	}
327*aca3beaaSApple OSS Distributions 
328*aca3beaaSApple OSS Distributions 	imo->imo_membership = nmships;
329*aca3beaaSApple OSS Distributions 	nmships = NULL;
330*aca3beaaSApple OSS Distributions 	imo->imo_mfilters = nmfilters;
331*aca3beaaSApple OSS Distributions 	nmfilters = NULL;
332*aca3beaaSApple OSS Distributions 	imo->imo_max_memberships = newmax;
333*aca3beaaSApple OSS Distributions 
334*aca3beaaSApple OSS Distributions 	return 0;
335*aca3beaaSApple OSS Distributions 
336*aca3beaaSApple OSS Distributions cleanup:
337*aca3beaaSApple OSS Distributions 	if (nmfilters != NULL) {
338*aca3beaaSApple OSS Distributions 		kfree_type(struct in_mfilter, newmax, nmfilters);
339*aca3beaaSApple OSS Distributions 	}
340*aca3beaaSApple OSS Distributions 
341*aca3beaaSApple OSS Distributions 	if (nmships != NULL) {
342*aca3beaaSApple OSS Distributions 		kfree_type(struct in_multi *, newmax, nmships);
343*aca3beaaSApple OSS Distributions 	}
344*aca3beaaSApple OSS Distributions 
345*aca3beaaSApple OSS Distributions 	return err;
346*aca3beaaSApple OSS Distributions }
347*aca3beaaSApple OSS Distributions 
348*aca3beaaSApple OSS Distributions /*
349*aca3beaaSApple OSS Distributions  * Find an IPv4 multicast group entry for this ip_moptions instance
350*aca3beaaSApple OSS Distributions  * which matches the specified group, and optionally an interface.
351*aca3beaaSApple OSS Distributions  * Return its index into the array, or -1 if not found.
352*aca3beaaSApple OSS Distributions  */
353*aca3beaaSApple OSS Distributions static size_t
imo_match_group(const struct ip_moptions * imo,const struct ifnet * ifp,const struct sockaddr_in * group)354*aca3beaaSApple OSS Distributions imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp,
355*aca3beaaSApple OSS Distributions     const struct sockaddr_in *group)
356*aca3beaaSApple OSS Distributions {
357*aca3beaaSApple OSS Distributions 	struct in_multi *pinm;
358*aca3beaaSApple OSS Distributions 	int               idx;
359*aca3beaaSApple OSS Distributions 	int               nmships;
360*aca3beaaSApple OSS Distributions 
361*aca3beaaSApple OSS Distributions 	IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo));
362*aca3beaaSApple OSS Distributions 
363*aca3beaaSApple OSS Distributions 
364*aca3beaaSApple OSS Distributions 	/* The imo_membership array may be lazy allocated. */
365*aca3beaaSApple OSS Distributions 	if (imo->imo_membership == NULL || imo->imo_num_memberships == 0) {
366*aca3beaaSApple OSS Distributions 		return -1;
367*aca3beaaSApple OSS Distributions 	}
368*aca3beaaSApple OSS Distributions 
369*aca3beaaSApple OSS Distributions 	nmships = imo->imo_num_memberships;
370*aca3beaaSApple OSS Distributions 	for (idx = 0; idx < nmships; idx++) {
371*aca3beaaSApple OSS Distributions 		pinm = imo->imo_membership[idx];
372*aca3beaaSApple OSS Distributions 		if (pinm == NULL) {
373*aca3beaaSApple OSS Distributions 			continue;
374*aca3beaaSApple OSS Distributions 		}
375*aca3beaaSApple OSS Distributions 		INM_LOCK(pinm);
376*aca3beaaSApple OSS Distributions 		if ((ifp == NULL || (pinm->inm_ifp == ifp)) &&
377*aca3beaaSApple OSS Distributions 		    in_hosteq(pinm->inm_addr, group->sin_addr)) {
378*aca3beaaSApple OSS Distributions 			INM_UNLOCK(pinm);
379*aca3beaaSApple OSS Distributions 			break;
380*aca3beaaSApple OSS Distributions 		}
381*aca3beaaSApple OSS Distributions 		INM_UNLOCK(pinm);
382*aca3beaaSApple OSS Distributions 	}
383*aca3beaaSApple OSS Distributions 	if (idx >= nmships) {
384*aca3beaaSApple OSS Distributions 		idx = -1;
385*aca3beaaSApple OSS Distributions 	}
386*aca3beaaSApple OSS Distributions 
387*aca3beaaSApple OSS Distributions 	return idx;
388*aca3beaaSApple OSS Distributions }
389*aca3beaaSApple OSS Distributions 
390*aca3beaaSApple OSS Distributions /*
391*aca3beaaSApple OSS Distributions  * Find an IPv4 multicast source entry for this imo which matches
392*aca3beaaSApple OSS Distributions  * the given group index for this socket, and source address.
393*aca3beaaSApple OSS Distributions  *
394*aca3beaaSApple OSS Distributions  * NOTE: This does not check if the entry is in-mode, merely if
395*aca3beaaSApple OSS Distributions  * it exists, which may not be the desired behaviour.
396*aca3beaaSApple OSS Distributions  */
397*aca3beaaSApple OSS Distributions static struct in_msource *
imo_match_source(const struct ip_moptions * imo,const size_t gidx,const struct sockaddr_in * src)398*aca3beaaSApple OSS Distributions imo_match_source(const struct ip_moptions *imo, const size_t gidx,
399*aca3beaaSApple OSS Distributions     const struct sockaddr_in *src)
400*aca3beaaSApple OSS Distributions {
401*aca3beaaSApple OSS Distributions 	struct ip_msource        find;
402*aca3beaaSApple OSS Distributions 	struct in_mfilter       *imf;
403*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
404*aca3beaaSApple OSS Distributions 
405*aca3beaaSApple OSS Distributions 	IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo));
406*aca3beaaSApple OSS Distributions 
407*aca3beaaSApple OSS Distributions 	VERIFY(src->sin_family == AF_INET);
408*aca3beaaSApple OSS Distributions 	VERIFY(gidx != (size_t)-1 && gidx < imo->imo_num_memberships);
409*aca3beaaSApple OSS Distributions 
410*aca3beaaSApple OSS Distributions 	/* The imo_mfilters array may be lazy allocated. */
411*aca3beaaSApple OSS Distributions 	if (imo->imo_mfilters == NULL) {
412*aca3beaaSApple OSS Distributions 		return NULL;
413*aca3beaaSApple OSS Distributions 	}
414*aca3beaaSApple OSS Distributions 	imf = &imo->imo_mfilters[gidx];
415*aca3beaaSApple OSS Distributions 
416*aca3beaaSApple OSS Distributions 	/* Source trees are keyed in host byte order. */
417*aca3beaaSApple OSS Distributions 	find.ims_haddr = ntohl(src->sin_addr.s_addr);
418*aca3beaaSApple OSS Distributions 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
419*aca3beaaSApple OSS Distributions 
420*aca3beaaSApple OSS Distributions 	return (struct in_msource *)ims;
421*aca3beaaSApple OSS Distributions }
422*aca3beaaSApple OSS Distributions 
423*aca3beaaSApple OSS Distributions /*
424*aca3beaaSApple OSS Distributions  * Perform filtering for multicast datagrams on a socket by group and source.
425*aca3beaaSApple OSS Distributions  *
426*aca3beaaSApple OSS Distributions  * Returns 0 if a datagram should be allowed through, or various error codes
427*aca3beaaSApple OSS Distributions  * if the socket was not a member of the group, or the source was muted, etc.
428*aca3beaaSApple OSS Distributions  */
429*aca3beaaSApple OSS Distributions int
imo_multi_filter(const struct ip_moptions * imo,const struct ifnet * ifp,const struct sockaddr_in * group,const struct sockaddr_in * src)430*aca3beaaSApple OSS Distributions imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp,
431*aca3beaaSApple OSS Distributions     const struct sockaddr_in *group, const struct sockaddr_in *src)
432*aca3beaaSApple OSS Distributions {
433*aca3beaaSApple OSS Distributions 	size_t gidx;
434*aca3beaaSApple OSS Distributions 	struct in_msource *ims;
435*aca3beaaSApple OSS Distributions 	int mode;
436*aca3beaaSApple OSS Distributions 
437*aca3beaaSApple OSS Distributions 	IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo));
438*aca3beaaSApple OSS Distributions 	VERIFY(ifp != NULL);
439*aca3beaaSApple OSS Distributions 
440*aca3beaaSApple OSS Distributions 	gidx = imo_match_group(imo, ifp, group);
441*aca3beaaSApple OSS Distributions 	if (gidx == (size_t)-1) {
442*aca3beaaSApple OSS Distributions 		return MCAST_NOTGMEMBER;
443*aca3beaaSApple OSS Distributions 	}
444*aca3beaaSApple OSS Distributions 
445*aca3beaaSApple OSS Distributions 	/*
446*aca3beaaSApple OSS Distributions 	 * Check if the source was included in an (S,G) join.
447*aca3beaaSApple OSS Distributions 	 * Allow reception on exclusive memberships by default,
448*aca3beaaSApple OSS Distributions 	 * reject reception on inclusive memberships by default.
449*aca3beaaSApple OSS Distributions 	 * Exclude source only if an in-mode exclude filter exists.
450*aca3beaaSApple OSS Distributions 	 * Include source only if an in-mode include filter exists.
451*aca3beaaSApple OSS Distributions 	 * NOTE: We are comparing group state here at IGMP t1 (now)
452*aca3beaaSApple OSS Distributions 	 * with socket-layer t0 (since last downcall).
453*aca3beaaSApple OSS Distributions 	 */
454*aca3beaaSApple OSS Distributions 	mode = imo->imo_mfilters[gidx].imf_st[1];
455*aca3beaaSApple OSS Distributions 	ims = imo_match_source(imo, gidx, src);
456*aca3beaaSApple OSS Distributions 
457*aca3beaaSApple OSS Distributions 	if ((ims == NULL && mode == MCAST_INCLUDE) ||
458*aca3beaaSApple OSS Distributions 	    (ims != NULL && ims->imsl_st[0] != mode)) {
459*aca3beaaSApple OSS Distributions 		return MCAST_NOTSMEMBER;
460*aca3beaaSApple OSS Distributions 	}
461*aca3beaaSApple OSS Distributions 
462*aca3beaaSApple OSS Distributions 	return MCAST_PASS;
463*aca3beaaSApple OSS Distributions }
464*aca3beaaSApple OSS Distributions 
465*aca3beaaSApple OSS Distributions int
imo_clone(struct inpcb * from_inp,struct inpcb * to_inp)466*aca3beaaSApple OSS Distributions imo_clone(struct inpcb *from_inp, struct inpcb *to_inp)
467*aca3beaaSApple OSS Distributions {
468*aca3beaaSApple OSS Distributions 	int err = 0;
469*aca3beaaSApple OSS Distributions 	struct ip_moptions *from;
470*aca3beaaSApple OSS Distributions 	struct ip_moptions *to;
471*aca3beaaSApple OSS Distributions 
472*aca3beaaSApple OSS Distributions 	from = inp_findmoptions(from_inp);
473*aca3beaaSApple OSS Distributions 	if (from == NULL) {
474*aca3beaaSApple OSS Distributions 		return ENOMEM;
475*aca3beaaSApple OSS Distributions 	}
476*aca3beaaSApple OSS Distributions 
477*aca3beaaSApple OSS Distributions 	to = inp_findmoptions(to_inp);
478*aca3beaaSApple OSS Distributions 	if (to == NULL) {
479*aca3beaaSApple OSS Distributions 		IMO_REMREF(from);
480*aca3beaaSApple OSS Distributions 		return ENOMEM;
481*aca3beaaSApple OSS Distributions 	}
482*aca3beaaSApple OSS Distributions 
483*aca3beaaSApple OSS Distributions 	IMO_LOCK(from);
484*aca3beaaSApple OSS Distributions 	IMO_LOCK(to);
485*aca3beaaSApple OSS Distributions 
486*aca3beaaSApple OSS Distributions 	to->imo_multicast_ifp = from->imo_multicast_ifp;
487*aca3beaaSApple OSS Distributions 	to->imo_multicast_vif = from->imo_multicast_vif;
488*aca3beaaSApple OSS Distributions 	to->imo_multicast_ttl = from->imo_multicast_ttl;
489*aca3beaaSApple OSS Distributions 	to->imo_multicast_loop = from->imo_multicast_loop;
490*aca3beaaSApple OSS Distributions 
491*aca3beaaSApple OSS Distributions 	/*
492*aca3beaaSApple OSS Distributions 	 * We're cloning, so drop any existing memberships and source
493*aca3beaaSApple OSS Distributions 	 * filters on the destination ip_moptions.
494*aca3beaaSApple OSS Distributions 	 */
495*aca3beaaSApple OSS Distributions 	IMO_PURGE_LOCKED(to);
496*aca3beaaSApple OSS Distributions 
497*aca3beaaSApple OSS Distributions 	VERIFY(to->imo_max_memberships != 0 && from->imo_max_memberships != 0);
498*aca3beaaSApple OSS Distributions 	if (to->imo_max_memberships < from->imo_max_memberships) {
499*aca3beaaSApple OSS Distributions 		/*
500*aca3beaaSApple OSS Distributions 		 * Ensure source and destination ip_moptions memberships
501*aca3beaaSApple OSS Distributions 		 * and source filters arrays are at least equal in size.
502*aca3beaaSApple OSS Distributions 		 */
503*aca3beaaSApple OSS Distributions 		err = imo_grow(to, from->imo_max_memberships);
504*aca3beaaSApple OSS Distributions 		if (err != 0) {
505*aca3beaaSApple OSS Distributions 			goto done;
506*aca3beaaSApple OSS Distributions 		}
507*aca3beaaSApple OSS Distributions 	}
508*aca3beaaSApple OSS Distributions 	VERIFY(to->imo_max_memberships >= from->imo_max_memberships);
509*aca3beaaSApple OSS Distributions 
510*aca3beaaSApple OSS Distributions 	/*
511*aca3beaaSApple OSS Distributions 	 * Source filtering doesn't apply to OpenTransport socket,
512*aca3beaaSApple OSS Distributions 	 * so simply hold additional reference count per membership.
513*aca3beaaSApple OSS Distributions 	 */
514*aca3beaaSApple OSS Distributions 	for (int i = 0; i < from->imo_num_memberships; i++) {
515*aca3beaaSApple OSS Distributions 		to->imo_membership[i] =
516*aca3beaaSApple OSS Distributions 		    in_addmulti(&from->imo_membership[i]->inm_addr,
517*aca3beaaSApple OSS Distributions 		    from->imo_membership[i]->inm_ifp);
518*aca3beaaSApple OSS Distributions 		if (to->imo_membership[i] == NULL) {
519*aca3beaaSApple OSS Distributions 			break;
520*aca3beaaSApple OSS Distributions 		}
521*aca3beaaSApple OSS Distributions 		to->imo_num_memberships++;
522*aca3beaaSApple OSS Distributions 	}
523*aca3beaaSApple OSS Distributions 	VERIFY(to->imo_num_memberships == from->imo_num_memberships);
524*aca3beaaSApple OSS Distributions 
525*aca3beaaSApple OSS Distributions done:
526*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(to);
527*aca3beaaSApple OSS Distributions 	IMO_REMREF(to);
528*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(from);
529*aca3beaaSApple OSS Distributions 	IMO_REMREF(from);
530*aca3beaaSApple OSS Distributions 
531*aca3beaaSApple OSS Distributions 	return err;
532*aca3beaaSApple OSS Distributions }
533*aca3beaaSApple OSS Distributions 
534*aca3beaaSApple OSS Distributions /*
535*aca3beaaSApple OSS Distributions  * Find and return a reference to an in_multi record for (ifp, group),
536*aca3beaaSApple OSS Distributions  * and bump its reference count.
537*aca3beaaSApple OSS Distributions  * If one does not exist, try to allocate it, and update link-layer multicast
538*aca3beaaSApple OSS Distributions  * filters on ifp to listen for group.
539*aca3beaaSApple OSS Distributions  * Return 0 if successful, otherwise return an appropriate error code.
540*aca3beaaSApple OSS Distributions  */
541*aca3beaaSApple OSS Distributions static int
in_getmulti(struct ifnet * ifp,const struct in_addr * group,struct in_multi ** pinm)542*aca3beaaSApple OSS Distributions in_getmulti(struct ifnet *ifp, const struct in_addr *group,
543*aca3beaaSApple OSS Distributions     struct in_multi **pinm)
544*aca3beaaSApple OSS Distributions {
545*aca3beaaSApple OSS Distributions 	struct sockaddr_in       gsin;
546*aca3beaaSApple OSS Distributions 	struct ifmultiaddr      *ifma;
547*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
548*aca3beaaSApple OSS Distributions 	int                     error;
549*aca3beaaSApple OSS Distributions 
550*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
551*aca3beaaSApple OSS Distributions 	IN_LOOKUP_MULTI(group, ifp, inm);
552*aca3beaaSApple OSS Distributions 	if (inm != NULL) {
553*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
554*aca3beaaSApple OSS Distributions 		VERIFY(inm->inm_reqcnt >= 1);
555*aca3beaaSApple OSS Distributions 		inm->inm_reqcnt++;
556*aca3beaaSApple OSS Distributions 		VERIFY(inm->inm_reqcnt != 0);
557*aca3beaaSApple OSS Distributions 		*pinm = inm;
558*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
559*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
560*aca3beaaSApple OSS Distributions 		/*
561*aca3beaaSApple OSS Distributions 		 * We already joined this group; return the inm
562*aca3beaaSApple OSS Distributions 		 * with a refcount held (via lookup) for caller.
563*aca3beaaSApple OSS Distributions 		 */
564*aca3beaaSApple OSS Distributions 		return 0;
565*aca3beaaSApple OSS Distributions 	}
566*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
567*aca3beaaSApple OSS Distributions 
568*aca3beaaSApple OSS Distributions 	bzero(&gsin, sizeof(gsin));
569*aca3beaaSApple OSS Distributions 	gsin.sin_family = AF_INET;
570*aca3beaaSApple OSS Distributions 	gsin.sin_len = sizeof(struct sockaddr_in);
571*aca3beaaSApple OSS Distributions 	gsin.sin_addr = *group;
572*aca3beaaSApple OSS Distributions 
573*aca3beaaSApple OSS Distributions 	/*
574*aca3beaaSApple OSS Distributions 	 * Check if a link-layer group is already associated
575*aca3beaaSApple OSS Distributions 	 * with this network-layer group on the given ifnet.
576*aca3beaaSApple OSS Distributions 	 */
577*aca3beaaSApple OSS Distributions 	error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma);
578*aca3beaaSApple OSS Distributions 	if (error != 0) {
579*aca3beaaSApple OSS Distributions 		return error;
580*aca3beaaSApple OSS Distributions 	}
581*aca3beaaSApple OSS Distributions 
582*aca3beaaSApple OSS Distributions 	/*
583*aca3beaaSApple OSS Distributions 	 * See comments in inm_remref() for access to ifma_protospec.
584*aca3beaaSApple OSS Distributions 	 */
585*aca3beaaSApple OSS Distributions 	in_multihead_lock_exclusive();
586*aca3beaaSApple OSS Distributions 	IFMA_LOCK(ifma);
587*aca3beaaSApple OSS Distributions 	if ((inm = ifma->ifma_protospec) != NULL) {
588*aca3beaaSApple OSS Distributions 		VERIFY(ifma->ifma_addr != NULL);
589*aca3beaaSApple OSS Distributions 		VERIFY(ifma->ifma_addr->sa_family == AF_INET);
590*aca3beaaSApple OSS Distributions 		INM_ADDREF(inm);        /* for caller */
591*aca3beaaSApple OSS Distributions 		IFMA_UNLOCK(ifma);
592*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
593*aca3beaaSApple OSS Distributions 		VERIFY(inm->inm_ifma == ifma);
594*aca3beaaSApple OSS Distributions 		VERIFY(inm->inm_ifp == ifp);
595*aca3beaaSApple OSS Distributions 		VERIFY(in_hosteq(inm->inm_addr, *group));
596*aca3beaaSApple OSS Distributions 		if (inm->inm_debug & IFD_ATTACHED) {
597*aca3beaaSApple OSS Distributions 			VERIFY(inm->inm_reqcnt >= 1);
598*aca3beaaSApple OSS Distributions 			inm->inm_reqcnt++;
599*aca3beaaSApple OSS Distributions 			VERIFY(inm->inm_reqcnt != 0);
600*aca3beaaSApple OSS Distributions 			*pinm = inm;
601*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
602*aca3beaaSApple OSS Distributions 			in_multihead_lock_done();
603*aca3beaaSApple OSS Distributions 			IFMA_REMREF(ifma);
604*aca3beaaSApple OSS Distributions 			/*
605*aca3beaaSApple OSS Distributions 			 * We lost the race with another thread doing
606*aca3beaaSApple OSS Distributions 			 * in_getmulti(); since this group has already
607*aca3beaaSApple OSS Distributions 			 * been joined; return the inm with a refcount
608*aca3beaaSApple OSS Distributions 			 * held for caller.
609*aca3beaaSApple OSS Distributions 			 */
610*aca3beaaSApple OSS Distributions 			return 0;
611*aca3beaaSApple OSS Distributions 		}
612*aca3beaaSApple OSS Distributions 		/*
613*aca3beaaSApple OSS Distributions 		 * We lost the race with another thread doing in_delmulti();
614*aca3beaaSApple OSS Distributions 		 * the inm referring to the ifma has been detached, thus we
615*aca3beaaSApple OSS Distributions 		 * reattach it back to the in_multihead list and return the
616*aca3beaaSApple OSS Distributions 		 * inm with a refcount held for the caller.
617*aca3beaaSApple OSS Distributions 		 */
618*aca3beaaSApple OSS Distributions 		in_multi_attach(inm);
619*aca3beaaSApple OSS Distributions 		VERIFY((inm->inm_debug &
620*aca3beaaSApple OSS Distributions 		    (IFD_ATTACHED | IFD_TRASHED)) == IFD_ATTACHED);
621*aca3beaaSApple OSS Distributions 		*pinm = inm;
622*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
623*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
624*aca3beaaSApple OSS Distributions 		IFMA_REMREF(ifma);
625*aca3beaaSApple OSS Distributions 		return 0;
626*aca3beaaSApple OSS Distributions 	}
627*aca3beaaSApple OSS Distributions 	IFMA_UNLOCK(ifma);
628*aca3beaaSApple OSS Distributions 
629*aca3beaaSApple OSS Distributions 	/*
630*aca3beaaSApple OSS Distributions 	 * A new in_multi record is needed; allocate and initialize it.
631*aca3beaaSApple OSS Distributions 	 * We DO NOT perform an IGMP join as the in_ layer may need to
632*aca3beaaSApple OSS Distributions 	 * push an initial source list down to IGMP to support SSM.
633*aca3beaaSApple OSS Distributions 	 *
634*aca3beaaSApple OSS Distributions 	 * The initial source filter state is INCLUDE, {} as per the RFC.
635*aca3beaaSApple OSS Distributions 	 */
636*aca3beaaSApple OSS Distributions 	inm = in_multi_alloc(Z_WAITOK);
637*aca3beaaSApple OSS Distributions 
638*aca3beaaSApple OSS Distributions 	INM_LOCK(inm);
639*aca3beaaSApple OSS Distributions 	inm->inm_addr = *group;
640*aca3beaaSApple OSS Distributions 	inm->inm_ifp = ifp;
641*aca3beaaSApple OSS Distributions 	inm->inm_igi = IGMP_IFINFO(ifp);
642*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_igi != NULL);
643*aca3beaaSApple OSS Distributions 	IGI_ADDREF(inm->inm_igi);
644*aca3beaaSApple OSS Distributions 	inm->inm_ifma = ifma;           /* keep refcount from if_addmulti() */
645*aca3beaaSApple OSS Distributions 	inm->inm_state = IGMP_NOT_MEMBER;
646*aca3beaaSApple OSS Distributions 	/*
647*aca3beaaSApple OSS Distributions 	 * Pending state-changes per group are subject to a bounds check.
648*aca3beaaSApple OSS Distributions 	 */
649*aca3beaaSApple OSS Distributions 	inm->inm_scq.ifq_maxlen = IGMP_MAX_STATE_CHANGES;
650*aca3beaaSApple OSS Distributions 	inm->inm_st[0].iss_fmode = MCAST_UNDEFINED;
651*aca3beaaSApple OSS Distributions 	inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
652*aca3beaaSApple OSS Distributions 	RB_INIT(&inm->inm_srcs);
653*aca3beaaSApple OSS Distributions 	*pinm = inm;
654*aca3beaaSApple OSS Distributions 	in_multi_attach(inm);
655*aca3beaaSApple OSS Distributions 	VERIFY((inm->inm_debug & (IFD_ATTACHED | IFD_TRASHED)) == IFD_ATTACHED);
656*aca3beaaSApple OSS Distributions 	INM_ADDREF_LOCKED(inm);         /* for caller */
657*aca3beaaSApple OSS Distributions 	INM_UNLOCK(inm);
658*aca3beaaSApple OSS Distributions 
659*aca3beaaSApple OSS Distributions 	IFMA_LOCK(ifma);
660*aca3beaaSApple OSS Distributions 	VERIFY(ifma->ifma_protospec == NULL);
661*aca3beaaSApple OSS Distributions 	ifma->ifma_protospec = inm;
662*aca3beaaSApple OSS Distributions 	IFMA_UNLOCK(ifma);
663*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
664*aca3beaaSApple OSS Distributions 
665*aca3beaaSApple OSS Distributions 	return 0;
666*aca3beaaSApple OSS Distributions }
667*aca3beaaSApple OSS Distributions 
668*aca3beaaSApple OSS Distributions /*
669*aca3beaaSApple OSS Distributions  * Clear recorded source entries for a group.
670*aca3beaaSApple OSS Distributions  * Used by the IGMP code.
671*aca3beaaSApple OSS Distributions  * FIXME: Should reap.
672*aca3beaaSApple OSS Distributions  */
673*aca3beaaSApple OSS Distributions void
inm_clear_recorded(struct in_multi * inm)674*aca3beaaSApple OSS Distributions inm_clear_recorded(struct in_multi *inm)
675*aca3beaaSApple OSS Distributions {
676*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
677*aca3beaaSApple OSS Distributions 
678*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
679*aca3beaaSApple OSS Distributions 
680*aca3beaaSApple OSS Distributions 	RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
681*aca3beaaSApple OSS Distributions 		if (ims->ims_stp) {
682*aca3beaaSApple OSS Distributions 			ims->ims_stp = 0;
683*aca3beaaSApple OSS Distributions 			--inm->inm_st[1].iss_rec;
684*aca3beaaSApple OSS Distributions 		}
685*aca3beaaSApple OSS Distributions 	}
686*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_st[1].iss_rec == 0);
687*aca3beaaSApple OSS Distributions }
688*aca3beaaSApple OSS Distributions 
689*aca3beaaSApple OSS Distributions /*
690*aca3beaaSApple OSS Distributions  * Record a source as pending for a Source-Group IGMPv3 query.
691*aca3beaaSApple OSS Distributions  * This lives here as it modifies the shared tree.
692*aca3beaaSApple OSS Distributions  *
693*aca3beaaSApple OSS Distributions  * inm is the group descriptor.
694*aca3beaaSApple OSS Distributions  * naddr is the address of the source to record in network-byte order.
695*aca3beaaSApple OSS Distributions  *
696*aca3beaaSApple OSS Distributions  * If the net.inet.igmp.sgalloc sysctl is non-zero, we will
697*aca3beaaSApple OSS Distributions  * lazy-allocate a source node in response to an SG query.
698*aca3beaaSApple OSS Distributions  * Otherwise, no allocation is performed. This saves some memory
699*aca3beaaSApple OSS Distributions  * with the trade-off that the source will not be reported to the
700*aca3beaaSApple OSS Distributions  * router if joined in the window between the query response and
701*aca3beaaSApple OSS Distributions  * the group actually being joined on the local host.
702*aca3beaaSApple OSS Distributions  *
703*aca3beaaSApple OSS Distributions  * Return 0 if the source didn't exist or was already marked as recorded.
704*aca3beaaSApple OSS Distributions  * Return 1 if the source was marked as recorded by this function.
705*aca3beaaSApple OSS Distributions  * Return <0 if any error occured (negated errno code).
706*aca3beaaSApple OSS Distributions  */
707*aca3beaaSApple OSS Distributions int
inm_record_source(struct in_multi * inm,const in_addr_t naddr)708*aca3beaaSApple OSS Distributions inm_record_source(struct in_multi *inm, const in_addr_t naddr)
709*aca3beaaSApple OSS Distributions {
710*aca3beaaSApple OSS Distributions 	struct ip_msource        find;
711*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *nims;
712*aca3beaaSApple OSS Distributions 
713*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
714*aca3beaaSApple OSS Distributions 
715*aca3beaaSApple OSS Distributions 	find.ims_haddr = ntohl(naddr);
716*aca3beaaSApple OSS Distributions 	ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
717*aca3beaaSApple OSS Distributions 	if (ims && ims->ims_stp) {
718*aca3beaaSApple OSS Distributions 		return 0;
719*aca3beaaSApple OSS Distributions 	}
720*aca3beaaSApple OSS Distributions 	if (ims == NULL) {
721*aca3beaaSApple OSS Distributions 		if (inm->inm_nsrc == in_mcast_maxgrpsrc) {
722*aca3beaaSApple OSS Distributions 			return -ENOSPC;
723*aca3beaaSApple OSS Distributions 		}
724*aca3beaaSApple OSS Distributions 		nims = ipms_alloc(Z_WAITOK);
725*aca3beaaSApple OSS Distributions 		nims->ims_haddr = find.ims_haddr;
726*aca3beaaSApple OSS Distributions 		RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
727*aca3beaaSApple OSS Distributions 		++inm->inm_nsrc;
728*aca3beaaSApple OSS Distributions 		ims = nims;
729*aca3beaaSApple OSS Distributions 	}
730*aca3beaaSApple OSS Distributions 
731*aca3beaaSApple OSS Distributions 	/*
732*aca3beaaSApple OSS Distributions 	 * Mark the source as recorded and update the recorded
733*aca3beaaSApple OSS Distributions 	 * source count.
734*aca3beaaSApple OSS Distributions 	 */
735*aca3beaaSApple OSS Distributions 	++ims->ims_stp;
736*aca3beaaSApple OSS Distributions 	++inm->inm_st[1].iss_rec;
737*aca3beaaSApple OSS Distributions 
738*aca3beaaSApple OSS Distributions 	return 1;
739*aca3beaaSApple OSS Distributions }
740*aca3beaaSApple OSS Distributions 
741*aca3beaaSApple OSS Distributions /*
742*aca3beaaSApple OSS Distributions  * Return a pointer to an in_msource owned by an in_mfilter,
743*aca3beaaSApple OSS Distributions  * given its source address.
744*aca3beaaSApple OSS Distributions  * Lazy-allocate if needed. If this is a new entry its filter state is
745*aca3beaaSApple OSS Distributions  * undefined at t0.
746*aca3beaaSApple OSS Distributions  *
747*aca3beaaSApple OSS Distributions  * imf is the filter set being modified.
748*aca3beaaSApple OSS Distributions  * haddr is the source address in *host* byte-order.
749*aca3beaaSApple OSS Distributions  *
750*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
751*aca3beaaSApple OSS Distributions  */
752*aca3beaaSApple OSS Distributions static int
imf_get_source(struct in_mfilter * imf,const struct sockaddr_in * psin,struct in_msource ** plims)753*aca3beaaSApple OSS Distributions imf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin,
754*aca3beaaSApple OSS Distributions     struct in_msource **plims)
755*aca3beaaSApple OSS Distributions {
756*aca3beaaSApple OSS Distributions 	struct ip_msource        find;
757*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
758*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
759*aca3beaaSApple OSS Distributions 	int                      error;
760*aca3beaaSApple OSS Distributions 
761*aca3beaaSApple OSS Distributions 	error = 0;
762*aca3beaaSApple OSS Distributions 	ims = NULL;
763*aca3beaaSApple OSS Distributions 	lims = NULL;
764*aca3beaaSApple OSS Distributions 
765*aca3beaaSApple OSS Distributions 	/* key is host byte order */
766*aca3beaaSApple OSS Distributions 	find.ims_haddr = ntohl(psin->sin_addr.s_addr);
767*aca3beaaSApple OSS Distributions 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
768*aca3beaaSApple OSS Distributions 	lims = (struct in_msource *)ims;
769*aca3beaaSApple OSS Distributions 	if (lims == NULL) {
770*aca3beaaSApple OSS Distributions 		if (imf->imf_nsrc == in_mcast_maxsocksrc) {
771*aca3beaaSApple OSS Distributions 			return ENOSPC;
772*aca3beaaSApple OSS Distributions 		}
773*aca3beaaSApple OSS Distributions 		lims = inms_alloc(Z_WAITOK);
774*aca3beaaSApple OSS Distributions 		lims->ims_haddr = find.ims_haddr;
775*aca3beaaSApple OSS Distributions 		lims->imsl_st[0] = MCAST_UNDEFINED;
776*aca3beaaSApple OSS Distributions 		RB_INSERT(ip_msource_tree, &imf->imf_sources,
777*aca3beaaSApple OSS Distributions 		    (struct ip_msource *)lims);
778*aca3beaaSApple OSS Distributions 		++imf->imf_nsrc;
779*aca3beaaSApple OSS Distributions 	}
780*aca3beaaSApple OSS Distributions 
781*aca3beaaSApple OSS Distributions 	*plims = lims;
782*aca3beaaSApple OSS Distributions 
783*aca3beaaSApple OSS Distributions 	return error;
784*aca3beaaSApple OSS Distributions }
785*aca3beaaSApple OSS Distributions 
786*aca3beaaSApple OSS Distributions /*
787*aca3beaaSApple OSS Distributions  * Graft a source entry into an existing socket-layer filter set,
788*aca3beaaSApple OSS Distributions  * maintaining any required invariants and checking allocations.
789*aca3beaaSApple OSS Distributions  *
790*aca3beaaSApple OSS Distributions  * The source is marked as being in the new filter mode at t1.
791*aca3beaaSApple OSS Distributions  *
792*aca3beaaSApple OSS Distributions  * Return the pointer to the new node, otherwise return NULL.
793*aca3beaaSApple OSS Distributions  *
794*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
795*aca3beaaSApple OSS Distributions  */
796*aca3beaaSApple OSS Distributions static struct in_msource *
imf_graft(struct in_mfilter * imf,const uint8_t st1,const struct sockaddr_in * psin)797*aca3beaaSApple OSS Distributions imf_graft(struct in_mfilter *imf, const uint8_t st1,
798*aca3beaaSApple OSS Distributions     const struct sockaddr_in *psin)
799*aca3beaaSApple OSS Distributions {
800*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
801*aca3beaaSApple OSS Distributions 
802*aca3beaaSApple OSS Distributions 	lims = inms_alloc(Z_WAITOK);
803*aca3beaaSApple OSS Distributions 	lims->ims_haddr = ntohl(psin->sin_addr.s_addr);
804*aca3beaaSApple OSS Distributions 	lims->imsl_st[0] = MCAST_UNDEFINED;
805*aca3beaaSApple OSS Distributions 	lims->imsl_st[1] = st1;
806*aca3beaaSApple OSS Distributions 	RB_INSERT(ip_msource_tree, &imf->imf_sources,
807*aca3beaaSApple OSS Distributions 	    (struct ip_msource *)lims);
808*aca3beaaSApple OSS Distributions 	++imf->imf_nsrc;
809*aca3beaaSApple OSS Distributions 
810*aca3beaaSApple OSS Distributions 	return lims;
811*aca3beaaSApple OSS Distributions }
812*aca3beaaSApple OSS Distributions 
813*aca3beaaSApple OSS Distributions /*
814*aca3beaaSApple OSS Distributions  * Prune a source entry from an existing socket-layer filter set,
815*aca3beaaSApple OSS Distributions  * maintaining any required invariants and checking allocations.
816*aca3beaaSApple OSS Distributions  *
817*aca3beaaSApple OSS Distributions  * The source is marked as being left at t1, it is not freed.
818*aca3beaaSApple OSS Distributions  *
819*aca3beaaSApple OSS Distributions  * Return 0 if no error occurred, otherwise return an errno value.
820*aca3beaaSApple OSS Distributions  *
821*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
822*aca3beaaSApple OSS Distributions  */
823*aca3beaaSApple OSS Distributions static int
imf_prune(struct in_mfilter * imf,const struct sockaddr_in * psin)824*aca3beaaSApple OSS Distributions imf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin)
825*aca3beaaSApple OSS Distributions {
826*aca3beaaSApple OSS Distributions 	struct ip_msource        find;
827*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
828*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
829*aca3beaaSApple OSS Distributions 
830*aca3beaaSApple OSS Distributions 	/* key is host byte order */
831*aca3beaaSApple OSS Distributions 	find.ims_haddr = ntohl(psin->sin_addr.s_addr);
832*aca3beaaSApple OSS Distributions 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
833*aca3beaaSApple OSS Distributions 	if (ims == NULL) {
834*aca3beaaSApple OSS Distributions 		return ENOENT;
835*aca3beaaSApple OSS Distributions 	}
836*aca3beaaSApple OSS Distributions 	lims = (struct in_msource *)ims;
837*aca3beaaSApple OSS Distributions 	lims->imsl_st[1] = MCAST_UNDEFINED;
838*aca3beaaSApple OSS Distributions 	return 0;
839*aca3beaaSApple OSS Distributions }
840*aca3beaaSApple OSS Distributions 
841*aca3beaaSApple OSS Distributions /*
842*aca3beaaSApple OSS Distributions  * Revert socket-layer filter set deltas at t1 to t0 state.
843*aca3beaaSApple OSS Distributions  *
844*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
845*aca3beaaSApple OSS Distributions  */
846*aca3beaaSApple OSS Distributions static void
imf_rollback(struct in_mfilter * imf)847*aca3beaaSApple OSS Distributions imf_rollback(struct in_mfilter *imf)
848*aca3beaaSApple OSS Distributions {
849*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *tims;
850*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
851*aca3beaaSApple OSS Distributions 
852*aca3beaaSApple OSS Distributions 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
853*aca3beaaSApple OSS Distributions 		lims = (struct in_msource *)ims;
854*aca3beaaSApple OSS Distributions 		if (lims->imsl_st[0] == lims->imsl_st[1]) {
855*aca3beaaSApple OSS Distributions 			/* no change at t1 */
856*aca3beaaSApple OSS Distributions 			continue;
857*aca3beaaSApple OSS Distributions 		} else if (lims->imsl_st[0] != MCAST_UNDEFINED) {
858*aca3beaaSApple OSS Distributions 			/* revert change to existing source at t1 */
859*aca3beaaSApple OSS Distributions 			lims->imsl_st[1] = lims->imsl_st[0];
860*aca3beaaSApple OSS Distributions 		} else {
861*aca3beaaSApple OSS Distributions 			/* revert source added t1 */
862*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__,
863*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(lims)));
864*aca3beaaSApple OSS Distributions 			RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
865*aca3beaaSApple OSS Distributions 			inms_free(lims);
866*aca3beaaSApple OSS Distributions 			imf->imf_nsrc--;
867*aca3beaaSApple OSS Distributions 		}
868*aca3beaaSApple OSS Distributions 	}
869*aca3beaaSApple OSS Distributions 	imf->imf_st[1] = imf->imf_st[0];
870*aca3beaaSApple OSS Distributions }
871*aca3beaaSApple OSS Distributions 
872*aca3beaaSApple OSS Distributions /*
873*aca3beaaSApple OSS Distributions  * Mark socket-layer filter set as INCLUDE {} at t1.
874*aca3beaaSApple OSS Distributions  *
875*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
876*aca3beaaSApple OSS Distributions  */
877*aca3beaaSApple OSS Distributions void
imf_leave(struct in_mfilter * imf)878*aca3beaaSApple OSS Distributions imf_leave(struct in_mfilter *imf)
879*aca3beaaSApple OSS Distributions {
880*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
881*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
882*aca3beaaSApple OSS Distributions 
883*aca3beaaSApple OSS Distributions 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
884*aca3beaaSApple OSS Distributions 		lims = (struct in_msource *)ims;
885*aca3beaaSApple OSS Distributions 		lims->imsl_st[1] = MCAST_UNDEFINED;
886*aca3beaaSApple OSS Distributions 	}
887*aca3beaaSApple OSS Distributions 	imf->imf_st[1] = MCAST_INCLUDE;
888*aca3beaaSApple OSS Distributions }
889*aca3beaaSApple OSS Distributions 
890*aca3beaaSApple OSS Distributions /*
891*aca3beaaSApple OSS Distributions  * Mark socket-layer filter set deltas as committed.
892*aca3beaaSApple OSS Distributions  *
893*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
894*aca3beaaSApple OSS Distributions  */
895*aca3beaaSApple OSS Distributions static void
imf_commit(struct in_mfilter * imf)896*aca3beaaSApple OSS Distributions imf_commit(struct in_mfilter *imf)
897*aca3beaaSApple OSS Distributions {
898*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
899*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
900*aca3beaaSApple OSS Distributions 
901*aca3beaaSApple OSS Distributions 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
902*aca3beaaSApple OSS Distributions 		lims = (struct in_msource *)ims;
903*aca3beaaSApple OSS Distributions 		lims->imsl_st[0] = lims->imsl_st[1];
904*aca3beaaSApple OSS Distributions 	}
905*aca3beaaSApple OSS Distributions 	imf->imf_st[0] = imf->imf_st[1];
906*aca3beaaSApple OSS Distributions }
907*aca3beaaSApple OSS Distributions 
908*aca3beaaSApple OSS Distributions /*
909*aca3beaaSApple OSS Distributions  * Reap unreferenced sources from socket-layer filter set.
910*aca3beaaSApple OSS Distributions  *
911*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
912*aca3beaaSApple OSS Distributions  */
913*aca3beaaSApple OSS Distributions static void
imf_reap(struct in_mfilter * imf)914*aca3beaaSApple OSS Distributions imf_reap(struct in_mfilter *imf)
915*aca3beaaSApple OSS Distributions {
916*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *tims;
917*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
918*aca3beaaSApple OSS Distributions 
919*aca3beaaSApple OSS Distributions 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
920*aca3beaaSApple OSS Distributions 		lims = (struct in_msource *)ims;
921*aca3beaaSApple OSS Distributions 		if ((lims->imsl_st[0] == MCAST_UNDEFINED) &&
922*aca3beaaSApple OSS Distributions 		    (lims->imsl_st[1] == MCAST_UNDEFINED)) {
923*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__,
924*aca3beaaSApple OSS Distributions 			    (uint64_t)VM_KERNEL_ADDRPERM(lims)));
925*aca3beaaSApple OSS Distributions 			RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
926*aca3beaaSApple OSS Distributions 			inms_free(lims);
927*aca3beaaSApple OSS Distributions 			imf->imf_nsrc--;
928*aca3beaaSApple OSS Distributions 		}
929*aca3beaaSApple OSS Distributions 	}
930*aca3beaaSApple OSS Distributions }
931*aca3beaaSApple OSS Distributions 
932*aca3beaaSApple OSS Distributions /*
933*aca3beaaSApple OSS Distributions  * Purge socket-layer filter set.
934*aca3beaaSApple OSS Distributions  *
935*aca3beaaSApple OSS Distributions  * Caller is expected to be holding imo_lock.
936*aca3beaaSApple OSS Distributions  */
937*aca3beaaSApple OSS Distributions void
imf_purge(struct in_mfilter * imf)938*aca3beaaSApple OSS Distributions imf_purge(struct in_mfilter *imf)
939*aca3beaaSApple OSS Distributions {
940*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *tims;
941*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
942*aca3beaaSApple OSS Distributions 
943*aca3beaaSApple OSS Distributions 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
944*aca3beaaSApple OSS Distributions 		lims = (struct in_msource *)ims;
945*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__,
946*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(lims)));
947*aca3beaaSApple OSS Distributions 		RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
948*aca3beaaSApple OSS Distributions 		inms_free(lims);
949*aca3beaaSApple OSS Distributions 		imf->imf_nsrc--;
950*aca3beaaSApple OSS Distributions 	}
951*aca3beaaSApple OSS Distributions 	imf->imf_st[0] = imf->imf_st[1] = MCAST_UNDEFINED;
952*aca3beaaSApple OSS Distributions 	VERIFY(RB_EMPTY(&imf->imf_sources));
953*aca3beaaSApple OSS Distributions }
954*aca3beaaSApple OSS Distributions 
955*aca3beaaSApple OSS Distributions /*
956*aca3beaaSApple OSS Distributions  * Look up a source filter entry for a multicast group.
957*aca3beaaSApple OSS Distributions  *
958*aca3beaaSApple OSS Distributions  * inm is the group descriptor to work with.
959*aca3beaaSApple OSS Distributions  * haddr is the host-byte-order IPv4 address to look up.
960*aca3beaaSApple OSS Distributions  * noalloc may be non-zero to suppress allocation of sources.
961*aca3beaaSApple OSS Distributions  * *pims will be set to the address of the retrieved or allocated source.
962*aca3beaaSApple OSS Distributions  *
963*aca3beaaSApple OSS Distributions  * Return 0 if successful, otherwise return a non-zero error code.
964*aca3beaaSApple OSS Distributions  */
965*aca3beaaSApple OSS Distributions static int
inm_get_source(struct in_multi * inm,const in_addr_t haddr,const int noalloc,struct ip_msource ** pims)966*aca3beaaSApple OSS Distributions inm_get_source(struct in_multi *inm, const in_addr_t haddr,
967*aca3beaaSApple OSS Distributions     const int noalloc, struct ip_msource **pims)
968*aca3beaaSApple OSS Distributions {
969*aca3beaaSApple OSS Distributions 	struct ip_msource        find;
970*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *nims;
971*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
972*aca3beaaSApple OSS Distributions 	struct in_addr ia;
973*aca3beaaSApple OSS Distributions 	char buf[MAX_IPv4_STR_LEN];
974*aca3beaaSApple OSS Distributions #endif
975*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
976*aca3beaaSApple OSS Distributions 
977*aca3beaaSApple OSS Distributions 	find.ims_haddr = haddr;
978*aca3beaaSApple OSS Distributions 	ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
979*aca3beaaSApple OSS Distributions 	if (ims == NULL && !noalloc) {
980*aca3beaaSApple OSS Distributions 		if (inm->inm_nsrc == in_mcast_maxgrpsrc) {
981*aca3beaaSApple OSS Distributions 			return ENOSPC;
982*aca3beaaSApple OSS Distributions 		}
983*aca3beaaSApple OSS Distributions 		nims = ipms_alloc(Z_WAITOK);
984*aca3beaaSApple OSS Distributions 		nims->ims_haddr = haddr;
985*aca3beaaSApple OSS Distributions 		RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
986*aca3beaaSApple OSS Distributions 		++inm->inm_nsrc;
987*aca3beaaSApple OSS Distributions 		ims = nims;
988*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
989*aca3beaaSApple OSS Distributions 		ia.s_addr = htonl(haddr);
990*aca3beaaSApple OSS Distributions 		inet_ntop(AF_INET, &ia, buf, sizeof(buf));
991*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: allocated %s as 0x%llx\n", __func__,
992*aca3beaaSApple OSS Distributions 		    buf, (uint64_t)VM_KERNEL_ADDRPERM(ims)));
993*aca3beaaSApple OSS Distributions #endif
994*aca3beaaSApple OSS Distributions 	}
995*aca3beaaSApple OSS Distributions 
996*aca3beaaSApple OSS Distributions 	*pims = ims;
997*aca3beaaSApple OSS Distributions 	return 0;
998*aca3beaaSApple OSS Distributions }
999*aca3beaaSApple OSS Distributions 
1000*aca3beaaSApple OSS Distributions /*
1001*aca3beaaSApple OSS Distributions  * Helper function to derive the filter mode on a source entry
1002*aca3beaaSApple OSS Distributions  * from its internal counters. Predicates are:
1003*aca3beaaSApple OSS Distributions  *  A source is only excluded if all listeners exclude it.
1004*aca3beaaSApple OSS Distributions  *  A source is only included if no listeners exclude it,
1005*aca3beaaSApple OSS Distributions  *  and at least one listener includes it.
1006*aca3beaaSApple OSS Distributions  * May be used by ifmcstat(8).
1007*aca3beaaSApple OSS Distributions  */
1008*aca3beaaSApple OSS Distributions uint8_t
ims_get_mode(const struct in_multi * inm,const struct ip_msource * ims,uint8_t t)1009*aca3beaaSApple OSS Distributions ims_get_mode(const struct in_multi *inm, const struct ip_msource *ims,
1010*aca3beaaSApple OSS Distributions     uint8_t t)
1011*aca3beaaSApple OSS Distributions {
1012*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(__DECONST(struct in_multi *, inm));
1013*aca3beaaSApple OSS Distributions 
1014*aca3beaaSApple OSS Distributions 	t = !!t;
1015*aca3beaaSApple OSS Distributions 	if (inm->inm_st[t].iss_ex > 0 &&
1016*aca3beaaSApple OSS Distributions 	    inm->inm_st[t].iss_ex == ims->ims_st[t].ex) {
1017*aca3beaaSApple OSS Distributions 		return MCAST_EXCLUDE;
1018*aca3beaaSApple OSS Distributions 	} else if (ims->ims_st[t].in > 0 && ims->ims_st[t].ex == 0) {
1019*aca3beaaSApple OSS Distributions 		return MCAST_INCLUDE;
1020*aca3beaaSApple OSS Distributions 	}
1021*aca3beaaSApple OSS Distributions 	return MCAST_UNDEFINED;
1022*aca3beaaSApple OSS Distributions }
1023*aca3beaaSApple OSS Distributions 
1024*aca3beaaSApple OSS Distributions /*
1025*aca3beaaSApple OSS Distributions  * Merge socket-layer source into IGMP-layer source.
1026*aca3beaaSApple OSS Distributions  * If rollback is non-zero, perform the inverse of the merge.
1027*aca3beaaSApple OSS Distributions  */
1028*aca3beaaSApple OSS Distributions static void
ims_merge(struct ip_msource * ims,const struct in_msource * lims,const int rollback)1029*aca3beaaSApple OSS Distributions ims_merge(struct ip_msource *ims, const struct in_msource *lims,
1030*aca3beaaSApple OSS Distributions     const int rollback)
1031*aca3beaaSApple OSS Distributions {
1032*aca3beaaSApple OSS Distributions 	int n = rollback ? -1 : 1;
1033*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
1034*aca3beaaSApple OSS Distributions 	struct in_addr ia;
1035*aca3beaaSApple OSS Distributions 
1036*aca3beaaSApple OSS Distributions 	ia.s_addr = htonl(ims->ims_haddr);
1037*aca3beaaSApple OSS Distributions #endif
1038*aca3beaaSApple OSS Distributions 
1039*aca3beaaSApple OSS Distributions 	if (lims->imsl_st[0] == MCAST_EXCLUDE) {
1040*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(ia,
1041*aca3beaaSApple OSS Distributions 		    ("%s: t1 ex -= %d on %s\n",
1042*aca3beaaSApple OSS Distributions 		    __func__, n, _igmp_inet_buf));
1043*aca3beaaSApple OSS Distributions 		ims->ims_st[1].ex -= n;
1044*aca3beaaSApple OSS Distributions 	} else if (lims->imsl_st[0] == MCAST_INCLUDE) {
1045*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(ia,
1046*aca3beaaSApple OSS Distributions 		    ("%s: t1 in -= %d on %s\n",
1047*aca3beaaSApple OSS Distributions 		    __func__, n, _igmp_inet_buf));
1048*aca3beaaSApple OSS Distributions 		ims->ims_st[1].in -= n;
1049*aca3beaaSApple OSS Distributions 	}
1050*aca3beaaSApple OSS Distributions 
1051*aca3beaaSApple OSS Distributions 	if (lims->imsl_st[1] == MCAST_EXCLUDE) {
1052*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(ia,
1053*aca3beaaSApple OSS Distributions 		    ("%s: t1 ex += %d on %s\n",
1054*aca3beaaSApple OSS Distributions 		    __func__, n, _igmp_inet_buf));
1055*aca3beaaSApple OSS Distributions 		ims->ims_st[1].ex += n;
1056*aca3beaaSApple OSS Distributions 	} else if (lims->imsl_st[1] == MCAST_INCLUDE) {
1057*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(ia,
1058*aca3beaaSApple OSS Distributions 		    ("%s: t1 in += %d on %s\n",
1059*aca3beaaSApple OSS Distributions 		    __func__, n, _igmp_inet_buf));
1060*aca3beaaSApple OSS Distributions 		ims->ims_st[1].in += n;
1061*aca3beaaSApple OSS Distributions 	}
1062*aca3beaaSApple OSS Distributions }
1063*aca3beaaSApple OSS Distributions 
1064*aca3beaaSApple OSS Distributions /*
1065*aca3beaaSApple OSS Distributions  * Atomically update the global in_multi state, when a membership's
1066*aca3beaaSApple OSS Distributions  * filter list is being updated in any way.
1067*aca3beaaSApple OSS Distributions  *
1068*aca3beaaSApple OSS Distributions  * imf is the per-inpcb-membership group filter pointer.
1069*aca3beaaSApple OSS Distributions  * A fake imf may be passed for in-kernel consumers.
1070*aca3beaaSApple OSS Distributions  *
1071*aca3beaaSApple OSS Distributions  * XXX This is a candidate for a set-symmetric-difference style loop
1072*aca3beaaSApple OSS Distributions  * which would eliminate the repeated lookup from root of ims nodes,
1073*aca3beaaSApple OSS Distributions  * as they share the same key space.
1074*aca3beaaSApple OSS Distributions  *
1075*aca3beaaSApple OSS Distributions  * If any error occurred this function will back out of refcounts
1076*aca3beaaSApple OSS Distributions  * and return a non-zero value.
1077*aca3beaaSApple OSS Distributions  */
1078*aca3beaaSApple OSS Distributions static int
inm_merge(struct in_multi * inm,struct in_mfilter * imf)1079*aca3beaaSApple OSS Distributions inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1080*aca3beaaSApple OSS Distributions {
1081*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *nims = NULL;
1082*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
1083*aca3beaaSApple OSS Distributions 	int                      schanged, error;
1084*aca3beaaSApple OSS Distributions 	int                      nsrc0, nsrc1;
1085*aca3beaaSApple OSS Distributions 
1086*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
1087*aca3beaaSApple OSS Distributions 
1088*aca3beaaSApple OSS Distributions 	schanged = 0;
1089*aca3beaaSApple OSS Distributions 	error = 0;
1090*aca3beaaSApple OSS Distributions 	nsrc1 = nsrc0 = 0;
1091*aca3beaaSApple OSS Distributions 
1092*aca3beaaSApple OSS Distributions 	/*
1093*aca3beaaSApple OSS Distributions 	 * Update the source filters first, as this may fail.
1094*aca3beaaSApple OSS Distributions 	 * Maintain count of in-mode filters at t0, t1. These are
1095*aca3beaaSApple OSS Distributions 	 * used to work out if we transition into ASM mode or not.
1096*aca3beaaSApple OSS Distributions 	 * Maintain a count of source filters whose state was
1097*aca3beaaSApple OSS Distributions 	 * actually modified by this operation.
1098*aca3beaaSApple OSS Distributions 	 */
1099*aca3beaaSApple OSS Distributions 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1100*aca3beaaSApple OSS Distributions 		lims = (struct in_msource *)ims;
1101*aca3beaaSApple OSS Distributions 		if (lims->imsl_st[0] == imf->imf_st[0]) {
1102*aca3beaaSApple OSS Distributions 			nsrc0++;
1103*aca3beaaSApple OSS Distributions 		}
1104*aca3beaaSApple OSS Distributions 		if (lims->imsl_st[1] == imf->imf_st[1]) {
1105*aca3beaaSApple OSS Distributions 			nsrc1++;
1106*aca3beaaSApple OSS Distributions 		}
1107*aca3beaaSApple OSS Distributions 		if (lims->imsl_st[0] == lims->imsl_st[1]) {
1108*aca3beaaSApple OSS Distributions 			continue;
1109*aca3beaaSApple OSS Distributions 		}
1110*aca3beaaSApple OSS Distributions 		error = inm_get_source(inm, lims->ims_haddr, 0, &nims);
1111*aca3beaaSApple OSS Distributions 		++schanged;
1112*aca3beaaSApple OSS Distributions 		if (error) {
1113*aca3beaaSApple OSS Distributions 			break;
1114*aca3beaaSApple OSS Distributions 		}
1115*aca3beaaSApple OSS Distributions 		ims_merge(nims, lims, 0);
1116*aca3beaaSApple OSS Distributions 	}
1117*aca3beaaSApple OSS Distributions 	if (error) {
1118*aca3beaaSApple OSS Distributions 		struct ip_msource *bims;
1119*aca3beaaSApple OSS Distributions 
1120*aca3beaaSApple OSS Distributions 		RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) {
1121*aca3beaaSApple OSS Distributions 			lims = (struct in_msource *)ims;
1122*aca3beaaSApple OSS Distributions 			if (lims->imsl_st[0] == lims->imsl_st[1]) {
1123*aca3beaaSApple OSS Distributions 				continue;
1124*aca3beaaSApple OSS Distributions 			}
1125*aca3beaaSApple OSS Distributions 			(void) inm_get_source(inm, lims->ims_haddr, 1, &bims);
1126*aca3beaaSApple OSS Distributions 			if (bims == NULL) {
1127*aca3beaaSApple OSS Distributions 				continue;
1128*aca3beaaSApple OSS Distributions 			}
1129*aca3beaaSApple OSS Distributions 			ims_merge(bims, lims, 1);
1130*aca3beaaSApple OSS Distributions 		}
1131*aca3beaaSApple OSS Distributions 		goto out_reap;
1132*aca3beaaSApple OSS Distributions 	}
1133*aca3beaaSApple OSS Distributions 
1134*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: imf filters in-mode: %d at t0, %d at t1\n",
1135*aca3beaaSApple OSS Distributions 	    __func__, nsrc0, nsrc1));
1136*aca3beaaSApple OSS Distributions 
1137*aca3beaaSApple OSS Distributions 	/* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */
1138*aca3beaaSApple OSS Distributions 	if (imf->imf_st[0] == imf->imf_st[1] &&
1139*aca3beaaSApple OSS Distributions 	    imf->imf_st[1] == MCAST_INCLUDE) {
1140*aca3beaaSApple OSS Distributions 		if (nsrc1 == 0) {
1141*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: --in on inm at t1\n", __func__));
1142*aca3beaaSApple OSS Distributions 			--inm->inm_st[1].iss_in;
1143*aca3beaaSApple OSS Distributions 		}
1144*aca3beaaSApple OSS Distributions 	}
1145*aca3beaaSApple OSS Distributions 
1146*aca3beaaSApple OSS Distributions 	/* Handle filter mode transition on socket. */
1147*aca3beaaSApple OSS Distributions 	if (imf->imf_st[0] != imf->imf_st[1]) {
1148*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: imf transition %d to %d\n",
1149*aca3beaaSApple OSS Distributions 		    __func__, imf->imf_st[0], imf->imf_st[1]));
1150*aca3beaaSApple OSS Distributions 
1151*aca3beaaSApple OSS Distributions 		if (imf->imf_st[0] == MCAST_EXCLUDE) {
1152*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: --ex on inm at t1\n", __func__));
1153*aca3beaaSApple OSS Distributions 			--inm->inm_st[1].iss_ex;
1154*aca3beaaSApple OSS Distributions 		} else if (imf->imf_st[0] == MCAST_INCLUDE) {
1155*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: --in on inm at t1\n", __func__));
1156*aca3beaaSApple OSS Distributions 			--inm->inm_st[1].iss_in;
1157*aca3beaaSApple OSS Distributions 		}
1158*aca3beaaSApple OSS Distributions 
1159*aca3beaaSApple OSS Distributions 		if (imf->imf_st[1] == MCAST_EXCLUDE) {
1160*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: ex++ on inm at t1\n", __func__));
1161*aca3beaaSApple OSS Distributions 			inm->inm_st[1].iss_ex++;
1162*aca3beaaSApple OSS Distributions 		} else if (imf->imf_st[1] == MCAST_INCLUDE && nsrc1 > 0) {
1163*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: in++ on inm at t1\n", __func__));
1164*aca3beaaSApple OSS Distributions 			inm->inm_st[1].iss_in++;
1165*aca3beaaSApple OSS Distributions 		}
1166*aca3beaaSApple OSS Distributions 	}
1167*aca3beaaSApple OSS Distributions 
1168*aca3beaaSApple OSS Distributions 	/*
1169*aca3beaaSApple OSS Distributions 	 * Track inm filter state in terms of listener counts.
1170*aca3beaaSApple OSS Distributions 	 * If there are any exclusive listeners, stack-wide
1171*aca3beaaSApple OSS Distributions 	 * membership is exclusive.
1172*aca3beaaSApple OSS Distributions 	 * Otherwise, if only inclusive listeners, stack-wide is inclusive.
1173*aca3beaaSApple OSS Distributions 	 * If no listeners remain, state is undefined at t1,
1174*aca3beaaSApple OSS Distributions 	 * and the IGMP lifecycle for this group should finish.
1175*aca3beaaSApple OSS Distributions 	 */
1176*aca3beaaSApple OSS Distributions 	if (inm->inm_st[1].iss_ex > 0) {
1177*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: transition to EX\n", __func__));
1178*aca3beaaSApple OSS Distributions 		inm->inm_st[1].iss_fmode = MCAST_EXCLUDE;
1179*aca3beaaSApple OSS Distributions 	} else if (inm->inm_st[1].iss_in > 0) {
1180*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: transition to IN\n", __func__));
1181*aca3beaaSApple OSS Distributions 		inm->inm_st[1].iss_fmode = MCAST_INCLUDE;
1182*aca3beaaSApple OSS Distributions 	} else {
1183*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: transition to UNDEF\n", __func__));
1184*aca3beaaSApple OSS Distributions 		inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
1185*aca3beaaSApple OSS Distributions 	}
1186*aca3beaaSApple OSS Distributions 
1187*aca3beaaSApple OSS Distributions 	/* Decrement ASM listener count on transition out of ASM mode. */
1188*aca3beaaSApple OSS Distributions 	if (imf->imf_st[0] == MCAST_EXCLUDE && nsrc0 == 0) {
1189*aca3beaaSApple OSS Distributions 		if ((imf->imf_st[1] != MCAST_EXCLUDE) ||
1190*aca3beaaSApple OSS Distributions 		    (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) {
1191*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: --asm on inm at t1\n", __func__));
1192*aca3beaaSApple OSS Distributions 			--inm->inm_st[1].iss_asm;
1193*aca3beaaSApple OSS Distributions 		}
1194*aca3beaaSApple OSS Distributions 	}
1195*aca3beaaSApple OSS Distributions 
1196*aca3beaaSApple OSS Distributions 	/* Increment ASM listener count on transition to ASM mode. */
1197*aca3beaaSApple OSS Distributions 	if (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 == 0) {
1198*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: asm++ on inm at t1\n", __func__));
1199*aca3beaaSApple OSS Distributions 		inm->inm_st[1].iss_asm++;
1200*aca3beaaSApple OSS Distributions 	}
1201*aca3beaaSApple OSS Distributions 
1202*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: merged imf 0x%llx to inm 0x%llx\n", __func__,
1203*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(imf),
1204*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm)));
1205*aca3beaaSApple OSS Distributions 	inm_print(inm);
1206*aca3beaaSApple OSS Distributions 
1207*aca3beaaSApple OSS Distributions out_reap:
1208*aca3beaaSApple OSS Distributions 	if (schanged > 0) {
1209*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: sources changed; reaping\n", __func__));
1210*aca3beaaSApple OSS Distributions 		inm_reap(inm);
1211*aca3beaaSApple OSS Distributions 	}
1212*aca3beaaSApple OSS Distributions 	return error;
1213*aca3beaaSApple OSS Distributions }
1214*aca3beaaSApple OSS Distributions 
1215*aca3beaaSApple OSS Distributions /*
1216*aca3beaaSApple OSS Distributions  * Mark an in_multi's filter set deltas as committed.
1217*aca3beaaSApple OSS Distributions  * Called by IGMP after a state change has been enqueued.
1218*aca3beaaSApple OSS Distributions  */
1219*aca3beaaSApple OSS Distributions void
inm_commit(struct in_multi * inm)1220*aca3beaaSApple OSS Distributions inm_commit(struct in_multi *inm)
1221*aca3beaaSApple OSS Distributions {
1222*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
1223*aca3beaaSApple OSS Distributions 
1224*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
1225*aca3beaaSApple OSS Distributions 
1226*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: commit inm 0x%llx\n", __func__,
1227*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm)));
1228*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: pre commit:\n", __func__));
1229*aca3beaaSApple OSS Distributions 	inm_print(inm);
1230*aca3beaaSApple OSS Distributions 
1231*aca3beaaSApple OSS Distributions 	RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
1232*aca3beaaSApple OSS Distributions 		ims->ims_st[0] = ims->ims_st[1];
1233*aca3beaaSApple OSS Distributions 	}
1234*aca3beaaSApple OSS Distributions 	inm->inm_st[0] = inm->inm_st[1];
1235*aca3beaaSApple OSS Distributions }
1236*aca3beaaSApple OSS Distributions 
1237*aca3beaaSApple OSS Distributions /*
1238*aca3beaaSApple OSS Distributions  * Reap unreferenced nodes from an in_multi's filter set.
1239*aca3beaaSApple OSS Distributions  */
1240*aca3beaaSApple OSS Distributions static void
inm_reap(struct in_multi * inm)1241*aca3beaaSApple OSS Distributions inm_reap(struct in_multi *inm)
1242*aca3beaaSApple OSS Distributions {
1243*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *tims;
1244*aca3beaaSApple OSS Distributions 
1245*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
1246*aca3beaaSApple OSS Distributions 
1247*aca3beaaSApple OSS Distributions 	RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1248*aca3beaaSApple OSS Distributions 		if (ims->ims_st[0].ex > 0 || ims->ims_st[0].in > 0 ||
1249*aca3beaaSApple OSS Distributions 		    ims->ims_st[1].ex > 0 || ims->ims_st[1].in > 0 ||
1250*aca3beaaSApple OSS Distributions 		    ims->ims_stp != 0) {
1251*aca3beaaSApple OSS Distributions 			continue;
1252*aca3beaaSApple OSS Distributions 		}
1253*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: free ims 0x%llx\n", __func__,
1254*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ims)));
1255*aca3beaaSApple OSS Distributions 		RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1256*aca3beaaSApple OSS Distributions 		ipms_free(ims);
1257*aca3beaaSApple OSS Distributions 		inm->inm_nsrc--;
1258*aca3beaaSApple OSS Distributions 	}
1259*aca3beaaSApple OSS Distributions }
1260*aca3beaaSApple OSS Distributions 
1261*aca3beaaSApple OSS Distributions /*
1262*aca3beaaSApple OSS Distributions  * Purge all source nodes from an in_multi's filter set.
1263*aca3beaaSApple OSS Distributions  */
1264*aca3beaaSApple OSS Distributions void
inm_purge(struct in_multi * inm)1265*aca3beaaSApple OSS Distributions inm_purge(struct in_multi *inm)
1266*aca3beaaSApple OSS Distributions {
1267*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims, *tims;
1268*aca3beaaSApple OSS Distributions 
1269*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
1270*aca3beaaSApple OSS Distributions 
1271*aca3beaaSApple OSS Distributions 	RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1272*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: free ims 0x%llx\n", __func__,
1273*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(ims)));
1274*aca3beaaSApple OSS Distributions 		RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1275*aca3beaaSApple OSS Distributions 		ipms_free(ims);
1276*aca3beaaSApple OSS Distributions 		inm->inm_nsrc--;
1277*aca3beaaSApple OSS Distributions 	}
1278*aca3beaaSApple OSS Distributions }
1279*aca3beaaSApple OSS Distributions 
1280*aca3beaaSApple OSS Distributions /*
1281*aca3beaaSApple OSS Distributions  * Join a multicast group; real entry point.
1282*aca3beaaSApple OSS Distributions  *
1283*aca3beaaSApple OSS Distributions  * Only preserves atomicity at inm level.
1284*aca3beaaSApple OSS Distributions  * NOTE: imf argument cannot be const due to sys/tree.h limitations.
1285*aca3beaaSApple OSS Distributions  *
1286*aca3beaaSApple OSS Distributions  * If the IGMP downcall fails, the group is not joined, and an error
1287*aca3beaaSApple OSS Distributions  * code is returned.
1288*aca3beaaSApple OSS Distributions  */
1289*aca3beaaSApple OSS Distributions static int
in_joingroup(struct ifnet * ifp,const struct in_addr * gina,struct in_mfilter * imf,struct in_multi ** pinm)1290*aca3beaaSApple OSS Distributions in_joingroup(struct ifnet *ifp, const struct in_addr *gina,
1291*aca3beaaSApple OSS Distributions     /*const*/ struct in_mfilter *imf, struct in_multi **pinm)
1292*aca3beaaSApple OSS Distributions {
1293*aca3beaaSApple OSS Distributions 	struct in_mfilter        timf;
1294*aca3beaaSApple OSS Distributions 	struct in_multi         *inm = NULL;
1295*aca3beaaSApple OSS Distributions 	int                      error = 0;
1296*aca3beaaSApple OSS Distributions 	struct igmp_tparams      itp;
1297*aca3beaaSApple OSS Distributions 
1298*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(*gina, ("%s: join %s on 0x%llx(%s))\n", __func__,
1299*aca3beaaSApple OSS Distributions 	    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
1300*aca3beaaSApple OSS Distributions 
1301*aca3beaaSApple OSS Distributions 	bzero(&itp, sizeof(itp));
1302*aca3beaaSApple OSS Distributions 	*pinm = NULL;
1303*aca3beaaSApple OSS Distributions 
1304*aca3beaaSApple OSS Distributions 	/*
1305*aca3beaaSApple OSS Distributions 	 * If no imf was specified (i.e. kernel consumer),
1306*aca3beaaSApple OSS Distributions 	 * fake one up and assume it is an ASM join.
1307*aca3beaaSApple OSS Distributions 	 */
1308*aca3beaaSApple OSS Distributions 	if (imf == NULL) {
1309*aca3beaaSApple OSS Distributions 		imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE);
1310*aca3beaaSApple OSS Distributions 		imf = &timf;
1311*aca3beaaSApple OSS Distributions 	}
1312*aca3beaaSApple OSS Distributions 
1313*aca3beaaSApple OSS Distributions 	error = in_getmulti(ifp, gina, &inm);
1314*aca3beaaSApple OSS Distributions 	if (error) {
1315*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: in_getmulti() failure\n", __func__));
1316*aca3beaaSApple OSS Distributions 		return error;
1317*aca3beaaSApple OSS Distributions 	}
1318*aca3beaaSApple OSS Distributions 
1319*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
1320*aca3beaaSApple OSS Distributions 
1321*aca3beaaSApple OSS Distributions 	INM_LOCK(inm);
1322*aca3beaaSApple OSS Distributions 	error = inm_merge(inm, imf);
1323*aca3beaaSApple OSS Distributions 	if (error) {
1324*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: failed to merge inm state\n", __func__));
1325*aca3beaaSApple OSS Distributions 		goto out_inm_release;
1326*aca3beaaSApple OSS Distributions 	}
1327*aca3beaaSApple OSS Distributions 
1328*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
1329*aca3beaaSApple OSS Distributions 	error = igmp_change_state(inm, &itp);
1330*aca3beaaSApple OSS Distributions 	if (error) {
1331*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: failed to update source\n", __func__));
1332*aca3beaaSApple OSS Distributions 		imf_rollback(imf);
1333*aca3beaaSApple OSS Distributions 		goto out_inm_release;
1334*aca3beaaSApple OSS Distributions 	}
1335*aca3beaaSApple OSS Distributions 
1336*aca3beaaSApple OSS Distributions out_inm_release:
1337*aca3beaaSApple OSS Distributions 	if (error) {
1338*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: dropping ref on 0x%llx\n", __func__,
1339*aca3beaaSApple OSS Distributions 		    (uint64_t)VM_KERNEL_ADDRPERM(inm)));
1340*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
1341*aca3beaaSApple OSS Distributions 		INM_REMREF(inm);
1342*aca3beaaSApple OSS Distributions 	} else {
1343*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
1344*aca3beaaSApple OSS Distributions 		*pinm = inm;    /* keep refcount from in_getmulti() */
1345*aca3beaaSApple OSS Distributions 	}
1346*aca3beaaSApple OSS Distributions 
1347*aca3beaaSApple OSS Distributions 	/* schedule timer now that we've dropped the lock(s) */
1348*aca3beaaSApple OSS Distributions 	igmp_set_fast_timeout(&itp);
1349*aca3beaaSApple OSS Distributions 
1350*aca3beaaSApple OSS Distributions 	return error;
1351*aca3beaaSApple OSS Distributions }
1352*aca3beaaSApple OSS Distributions 
1353*aca3beaaSApple OSS Distributions /*
1354*aca3beaaSApple OSS Distributions  * Leave a multicast group; real entry point.
1355*aca3beaaSApple OSS Distributions  * All source filters will be expunged.
1356*aca3beaaSApple OSS Distributions  *
1357*aca3beaaSApple OSS Distributions  * Only preserves atomicity at inm level.
1358*aca3beaaSApple OSS Distributions  *
1359*aca3beaaSApple OSS Distributions  * Note: This is not the same as inm_release(*) as this function also
1360*aca3beaaSApple OSS Distributions  * makes a state change downcall into IGMP.
1361*aca3beaaSApple OSS Distributions  */
1362*aca3beaaSApple OSS Distributions int
in_leavegroup(struct in_multi * inm,struct in_mfilter * imf)1363*aca3beaaSApple OSS Distributions in_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1364*aca3beaaSApple OSS Distributions {
1365*aca3beaaSApple OSS Distributions 	struct in_mfilter        timf;
1366*aca3beaaSApple OSS Distributions 	int                      error, lastref;
1367*aca3beaaSApple OSS Distributions 	struct igmp_tparams      itp;
1368*aca3beaaSApple OSS Distributions 
1369*aca3beaaSApple OSS Distributions 	bzero(&itp, sizeof(itp));
1370*aca3beaaSApple OSS Distributions 	error = 0;
1371*aca3beaaSApple OSS Distributions 
1372*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_NOTHELD(inm);
1373*aca3beaaSApple OSS Distributions 
1374*aca3beaaSApple OSS Distributions 	in_multihead_lock_exclusive();
1375*aca3beaaSApple OSS Distributions 	INM_LOCK(inm);
1376*aca3beaaSApple OSS Distributions 
1377*aca3beaaSApple OSS Distributions 	IGMP_INET_PRINTF(inm->inm_addr,
1378*aca3beaaSApple OSS Distributions 	    ("%s: leave inm 0x%llx, %s/%s%d, imf 0x%llx\n", __func__,
1379*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm), _igmp_inet_buf,
1380*aca3beaaSApple OSS Distributions 	    (inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_name),
1381*aca3beaaSApple OSS Distributions 	    inm->inm_ifp->if_unit, (uint64_t)VM_KERNEL_ADDRPERM(imf)));
1382*aca3beaaSApple OSS Distributions 
1383*aca3beaaSApple OSS Distributions 	/*
1384*aca3beaaSApple OSS Distributions 	 * If no imf was specified (i.e. kernel consumer),
1385*aca3beaaSApple OSS Distributions 	 * fake one up and assume it is an ASM join.
1386*aca3beaaSApple OSS Distributions 	 */
1387*aca3beaaSApple OSS Distributions 	if (imf == NULL) {
1388*aca3beaaSApple OSS Distributions 		imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED);
1389*aca3beaaSApple OSS Distributions 		imf = &timf;
1390*aca3beaaSApple OSS Distributions 	}
1391*aca3beaaSApple OSS Distributions 
1392*aca3beaaSApple OSS Distributions 	/*
1393*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at IGMP layer.
1394*aca3beaaSApple OSS Distributions 	 *
1395*aca3beaaSApple OSS Distributions 	 * As this particular invocation should not cause any memory
1396*aca3beaaSApple OSS Distributions 	 * to be allocated, and there is no opportunity to roll back
1397*aca3beaaSApple OSS Distributions 	 * the transaction, it MUST NOT fail.
1398*aca3beaaSApple OSS Distributions 	 */
1399*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
1400*aca3beaaSApple OSS Distributions 
1401*aca3beaaSApple OSS Distributions 	error = inm_merge(inm, imf);
1402*aca3beaaSApple OSS Distributions 	KASSERT(error == 0, ("%s: failed to merge inm state\n", __func__));
1403*aca3beaaSApple OSS Distributions 
1404*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
1405*aca3beaaSApple OSS Distributions 	error = igmp_change_state(inm, &itp);
1406*aca3beaaSApple OSS Distributions #if IGMP_DEBUG
1407*aca3beaaSApple OSS Distributions 	if (error) {
1408*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
1409*aca3beaaSApple OSS Distributions 	}
1410*aca3beaaSApple OSS Distributions #endif
1411*aca3beaaSApple OSS Distributions 	lastref = in_multi_detach(inm);
1412*aca3beaaSApple OSS Distributions 	VERIFY(!lastref || (!(inm->inm_debug & IFD_ATTACHED) &&
1413*aca3beaaSApple OSS Distributions 	    inm->inm_reqcnt == 0));
1414*aca3beaaSApple OSS Distributions 	INM_UNLOCK(inm);
1415*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
1416*aca3beaaSApple OSS Distributions 
1417*aca3beaaSApple OSS Distributions 	if (lastref) {
1418*aca3beaaSApple OSS Distributions 		INM_REMREF(inm);        /* for in_multihead list */
1419*aca3beaaSApple OSS Distributions 	}
1420*aca3beaaSApple OSS Distributions 	/* schedule timer now that we've dropped the lock(s) */
1421*aca3beaaSApple OSS Distributions 	igmp_set_fast_timeout(&itp);
1422*aca3beaaSApple OSS Distributions 
1423*aca3beaaSApple OSS Distributions 	return error;
1424*aca3beaaSApple OSS Distributions }
1425*aca3beaaSApple OSS Distributions 
1426*aca3beaaSApple OSS Distributions /*
1427*aca3beaaSApple OSS Distributions  * Join an IPv4 multicast group in (*,G) exclusive mode.
1428*aca3beaaSApple OSS Distributions  * The group must be a 224.0.0.0/24 link-scope group.
1429*aca3beaaSApple OSS Distributions  * This KPI is for legacy kernel consumers only.
1430*aca3beaaSApple OSS Distributions  */
1431*aca3beaaSApple OSS Distributions struct in_multi *
in_addmulti(struct in_addr * ap,struct ifnet * ifp)1432*aca3beaaSApple OSS Distributions in_addmulti(struct in_addr *ap, struct ifnet *ifp)
1433*aca3beaaSApple OSS Distributions {
1434*aca3beaaSApple OSS Distributions 	struct in_multi *pinm = NULL;
1435*aca3beaaSApple OSS Distributions 	int error;
1436*aca3beaaSApple OSS Distributions 
1437*aca3beaaSApple OSS Distributions 	KASSERT(IN_LOCAL_GROUP(ntohl(ap->s_addr)),
1438*aca3beaaSApple OSS Distributions 	    ("%s: %s not in 224.0.0.0/24\n", __func__, inet_ntoa(*ap)));
1439*aca3beaaSApple OSS Distributions 
1440*aca3beaaSApple OSS Distributions 	error = in_joingroup(ifp, ap, NULL, &pinm);
1441*aca3beaaSApple OSS Distributions 	VERIFY(pinm != NULL || error != 0);
1442*aca3beaaSApple OSS Distributions 
1443*aca3beaaSApple OSS Distributions 	return pinm;
1444*aca3beaaSApple OSS Distributions }
1445*aca3beaaSApple OSS Distributions 
1446*aca3beaaSApple OSS Distributions /*
1447*aca3beaaSApple OSS Distributions  * Leave an IPv4 multicast group, assumed to be in exclusive (*,G) mode.
1448*aca3beaaSApple OSS Distributions  * This KPI is for legacy kernel consumers only.
1449*aca3beaaSApple OSS Distributions  */
1450*aca3beaaSApple OSS Distributions void
in_delmulti(struct in_multi * inm)1451*aca3beaaSApple OSS Distributions in_delmulti(struct in_multi *inm)
1452*aca3beaaSApple OSS Distributions {
1453*aca3beaaSApple OSS Distributions 	(void) in_leavegroup(inm, NULL);
1454*aca3beaaSApple OSS Distributions }
1455*aca3beaaSApple OSS Distributions 
1456*aca3beaaSApple OSS Distributions /*
1457*aca3beaaSApple OSS Distributions  * Block or unblock an ASM multicast source on an inpcb.
1458*aca3beaaSApple OSS Distributions  * This implements the delta-based API described in RFC 3678.
1459*aca3beaaSApple OSS Distributions  *
1460*aca3beaaSApple OSS Distributions  * The delta-based API applies only to exclusive-mode memberships.
1461*aca3beaaSApple OSS Distributions  * An IGMP downcall will be performed.
1462*aca3beaaSApple OSS Distributions  *
1463*aca3beaaSApple OSS Distributions  * Return 0 if successful, otherwise return an appropriate error code.
1464*aca3beaaSApple OSS Distributions  */
1465*aca3beaaSApple OSS Distributions static int
inp_block_unblock_source(struct inpcb * inp,struct sockopt * sopt)1466*aca3beaaSApple OSS Distributions inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
1467*aca3beaaSApple OSS Distributions {
1468*aca3beaaSApple OSS Distributions 	struct group_source_req          gsr;
1469*aca3beaaSApple OSS Distributions 	struct sockaddr_in              *gsa, *ssa;
1470*aca3beaaSApple OSS Distributions 	struct ifnet                    *ifp;
1471*aca3beaaSApple OSS Distributions 	struct in_mfilter               *imf;
1472*aca3beaaSApple OSS Distributions 	struct ip_moptions              *imo;
1473*aca3beaaSApple OSS Distributions 	struct in_msource               *ims;
1474*aca3beaaSApple OSS Distributions 	struct in_multi                 *inm;
1475*aca3beaaSApple OSS Distributions 	size_t                           idx;
1476*aca3beaaSApple OSS Distributions 	uint8_t                          fmode;
1477*aca3beaaSApple OSS Distributions 	int                              error, doblock;
1478*aca3beaaSApple OSS Distributions 	unsigned int                     ifindex = 0;
1479*aca3beaaSApple OSS Distributions 	struct igmp_tparams              itp;
1480*aca3beaaSApple OSS Distributions 
1481*aca3beaaSApple OSS Distributions 	bzero(&itp, sizeof(itp));
1482*aca3beaaSApple OSS Distributions 	ifp = NULL;
1483*aca3beaaSApple OSS Distributions 	error = 0;
1484*aca3beaaSApple OSS Distributions 	doblock = 0;
1485*aca3beaaSApple OSS Distributions 
1486*aca3beaaSApple OSS Distributions 	memset(&gsr, 0, sizeof(struct group_source_req));
1487*aca3beaaSApple OSS Distributions 	gsa = (struct sockaddr_in *)&gsr.gsr_group;
1488*aca3beaaSApple OSS Distributions 	ssa = (struct sockaddr_in *)&gsr.gsr_source;
1489*aca3beaaSApple OSS Distributions 
1490*aca3beaaSApple OSS Distributions 	switch (sopt->sopt_name) {
1491*aca3beaaSApple OSS Distributions 	case IP_BLOCK_SOURCE:
1492*aca3beaaSApple OSS Distributions 	case IP_UNBLOCK_SOURCE: {
1493*aca3beaaSApple OSS Distributions 		struct ip_mreq_source    mreqs;
1494*aca3beaaSApple OSS Distributions 
1495*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &mreqs,
1496*aca3beaaSApple OSS Distributions 		    sizeof(struct ip_mreq_source),
1497*aca3beaaSApple OSS Distributions 		    sizeof(struct ip_mreq_source));
1498*aca3beaaSApple OSS Distributions 		if (error) {
1499*aca3beaaSApple OSS Distributions 			return error;
1500*aca3beaaSApple OSS Distributions 		}
1501*aca3beaaSApple OSS Distributions 
1502*aca3beaaSApple OSS Distributions 		gsa->sin_family = AF_INET;
1503*aca3beaaSApple OSS Distributions 		gsa->sin_len = sizeof(struct sockaddr_in);
1504*aca3beaaSApple OSS Distributions 		gsa->sin_addr = mreqs.imr_multiaddr;
1505*aca3beaaSApple OSS Distributions 
1506*aca3beaaSApple OSS Distributions 		ssa->sin_family = AF_INET;
1507*aca3beaaSApple OSS Distributions 		ssa->sin_len = sizeof(struct sockaddr_in);
1508*aca3beaaSApple OSS Distributions 		ssa->sin_addr = mreqs.imr_sourceaddr;
1509*aca3beaaSApple OSS Distributions 
1510*aca3beaaSApple OSS Distributions 		if (!in_nullhost(mreqs.imr_interface)) {
1511*aca3beaaSApple OSS Distributions 			ifp = ip_multicast_if(&mreqs.imr_interface, &ifindex);
1512*aca3beaaSApple OSS Distributions 		}
1513*aca3beaaSApple OSS Distributions 
1514*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == IP_BLOCK_SOURCE) {
1515*aca3beaaSApple OSS Distributions 			doblock = 1;
1516*aca3beaaSApple OSS Distributions 		}
1517*aca3beaaSApple OSS Distributions 
1518*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(mreqs.imr_interface,
1519*aca3beaaSApple OSS Distributions 		    ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__,
1520*aca3beaaSApple OSS Distributions 		    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
1521*aca3beaaSApple OSS Distributions 		break;
1522*aca3beaaSApple OSS Distributions 	}
1523*aca3beaaSApple OSS Distributions 
1524*aca3beaaSApple OSS Distributions 	case MCAST_BLOCK_SOURCE:
1525*aca3beaaSApple OSS Distributions 	case MCAST_UNBLOCK_SOURCE:
1526*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &gsr,
1527*aca3beaaSApple OSS Distributions 		    sizeof(struct group_source_req),
1528*aca3beaaSApple OSS Distributions 		    sizeof(struct group_source_req));
1529*aca3beaaSApple OSS Distributions 		if (error) {
1530*aca3beaaSApple OSS Distributions 			return error;
1531*aca3beaaSApple OSS Distributions 		}
1532*aca3beaaSApple OSS Distributions 
1533*aca3beaaSApple OSS Distributions 		if (gsa->sin_family != AF_INET ||
1534*aca3beaaSApple OSS Distributions 		    gsa->sin_len != sizeof(struct sockaddr_in)) {
1535*aca3beaaSApple OSS Distributions 			return EINVAL;
1536*aca3beaaSApple OSS Distributions 		}
1537*aca3beaaSApple OSS Distributions 
1538*aca3beaaSApple OSS Distributions 		if (ssa->sin_family != AF_INET ||
1539*aca3beaaSApple OSS Distributions 		    ssa->sin_len != sizeof(struct sockaddr_in)) {
1540*aca3beaaSApple OSS Distributions 			return EINVAL;
1541*aca3beaaSApple OSS Distributions 		}
1542*aca3beaaSApple OSS Distributions 
1543*aca3beaaSApple OSS Distributions 		ifnet_head_lock_shared();
1544*aca3beaaSApple OSS Distributions 		if (gsr.gsr_interface == 0 ||
1545*aca3beaaSApple OSS Distributions 		    (u_int)if_index < gsr.gsr_interface) {
1546*aca3beaaSApple OSS Distributions 			ifnet_head_done();
1547*aca3beaaSApple OSS Distributions 			return EADDRNOTAVAIL;
1548*aca3beaaSApple OSS Distributions 		}
1549*aca3beaaSApple OSS Distributions 
1550*aca3beaaSApple OSS Distributions 		ifp = ifindex2ifnet[gsr.gsr_interface];
1551*aca3beaaSApple OSS Distributions 		ifnet_head_done();
1552*aca3beaaSApple OSS Distributions 
1553*aca3beaaSApple OSS Distributions 		if (ifp == NULL) {
1554*aca3beaaSApple OSS Distributions 			return EADDRNOTAVAIL;
1555*aca3beaaSApple OSS Distributions 		}
1556*aca3beaaSApple OSS Distributions 
1557*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == MCAST_BLOCK_SOURCE) {
1558*aca3beaaSApple OSS Distributions 			doblock = 1;
1559*aca3beaaSApple OSS Distributions 		}
1560*aca3beaaSApple OSS Distributions 		break;
1561*aca3beaaSApple OSS Distributions 
1562*aca3beaaSApple OSS Distributions 	default:
1563*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: unknown sopt_name %d\n",
1564*aca3beaaSApple OSS Distributions 		    __func__, sopt->sopt_name));
1565*aca3beaaSApple OSS Distributions 		return EOPNOTSUPP;
1566*aca3beaaSApple OSS Distributions 	}
1567*aca3beaaSApple OSS Distributions 
1568*aca3beaaSApple OSS Distributions 	if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
1569*aca3beaaSApple OSS Distributions 		return EINVAL;
1570*aca3beaaSApple OSS Distributions 	}
1571*aca3beaaSApple OSS Distributions 
1572*aca3beaaSApple OSS Distributions 	/*
1573*aca3beaaSApple OSS Distributions 	 * Check if we are actually a member of this group.
1574*aca3beaaSApple OSS Distributions 	 */
1575*aca3beaaSApple OSS Distributions 	imo = inp_findmoptions(inp);
1576*aca3beaaSApple OSS Distributions 	if (imo == NULL) {
1577*aca3beaaSApple OSS Distributions 		return ENOMEM;
1578*aca3beaaSApple OSS Distributions 	}
1579*aca3beaaSApple OSS Distributions 
1580*aca3beaaSApple OSS Distributions 	IMO_LOCK(imo);
1581*aca3beaaSApple OSS Distributions 	idx = imo_match_group(imo, ifp, gsa);
1582*aca3beaaSApple OSS Distributions 	if (idx == (size_t)-1 || imo->imo_mfilters == NULL) {
1583*aca3beaaSApple OSS Distributions 		error = EADDRNOTAVAIL;
1584*aca3beaaSApple OSS Distributions 		goto out_imo_locked;
1585*aca3beaaSApple OSS Distributions 	}
1586*aca3beaaSApple OSS Distributions 
1587*aca3beaaSApple OSS Distributions 	VERIFY(imo->imo_mfilters != NULL);
1588*aca3beaaSApple OSS Distributions 	imf = &imo->imo_mfilters[idx];
1589*aca3beaaSApple OSS Distributions 	inm = imo->imo_membership[idx];
1590*aca3beaaSApple OSS Distributions 
1591*aca3beaaSApple OSS Distributions 	/*
1592*aca3beaaSApple OSS Distributions 	 * Attempting to use the delta-based API on an
1593*aca3beaaSApple OSS Distributions 	 * non exclusive-mode membership is an error.
1594*aca3beaaSApple OSS Distributions 	 */
1595*aca3beaaSApple OSS Distributions 	fmode = imf->imf_st[0];
1596*aca3beaaSApple OSS Distributions 	if (fmode != MCAST_EXCLUDE) {
1597*aca3beaaSApple OSS Distributions 		error = EINVAL;
1598*aca3beaaSApple OSS Distributions 		goto out_imo_locked;
1599*aca3beaaSApple OSS Distributions 	}
1600*aca3beaaSApple OSS Distributions 
1601*aca3beaaSApple OSS Distributions 	/*
1602*aca3beaaSApple OSS Distributions 	 * Deal with error cases up-front:
1603*aca3beaaSApple OSS Distributions 	 *  Asked to block, but already blocked; or
1604*aca3beaaSApple OSS Distributions 	 *  Asked to unblock, but nothing to unblock.
1605*aca3beaaSApple OSS Distributions 	 * If adding a new block entry, allocate it.
1606*aca3beaaSApple OSS Distributions 	 */
1607*aca3beaaSApple OSS Distributions 	ims = imo_match_source(imo, idx, ssa);
1608*aca3beaaSApple OSS Distributions 	if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
1609*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(ssa->sin_addr,
1610*aca3beaaSApple OSS Distributions 		    ("%s: source %s %spresent\n", __func__,
1611*aca3beaaSApple OSS Distributions 		    _igmp_inet_buf, doblock ? "" : "not "));
1612*aca3beaaSApple OSS Distributions 		error = EADDRNOTAVAIL;
1613*aca3beaaSApple OSS Distributions 		goto out_imo_locked;
1614*aca3beaaSApple OSS Distributions 	}
1615*aca3beaaSApple OSS Distributions 
1616*aca3beaaSApple OSS Distributions 	/*
1617*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at socket layer.
1618*aca3beaaSApple OSS Distributions 	 */
1619*aca3beaaSApple OSS Distributions 	if (doblock) {
1620*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: %s source\n", __func__, "block"));
1621*aca3beaaSApple OSS Distributions 		ims = imf_graft(imf, fmode, ssa);
1622*aca3beaaSApple OSS Distributions 		if (ims == NULL) {
1623*aca3beaaSApple OSS Distributions 			error = ENOMEM;
1624*aca3beaaSApple OSS Distributions 		}
1625*aca3beaaSApple OSS Distributions 	} else {
1626*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: %s source\n", __func__, "allow"));
1627*aca3beaaSApple OSS Distributions 		error = imf_prune(imf, ssa);
1628*aca3beaaSApple OSS Distributions 	}
1629*aca3beaaSApple OSS Distributions 
1630*aca3beaaSApple OSS Distributions 	if (error) {
1631*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: merge imf state failed\n", __func__));
1632*aca3beaaSApple OSS Distributions 		goto out_imf_rollback;
1633*aca3beaaSApple OSS Distributions 	}
1634*aca3beaaSApple OSS Distributions 
1635*aca3beaaSApple OSS Distributions 	/*
1636*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at IGMP layer.
1637*aca3beaaSApple OSS Distributions 	 */
1638*aca3beaaSApple OSS Distributions 	INM_LOCK(inm);
1639*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
1640*aca3beaaSApple OSS Distributions 	error = inm_merge(inm, imf);
1641*aca3beaaSApple OSS Distributions 	if (error) {
1642*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: failed to merge inm state\n", __func__));
1643*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
1644*aca3beaaSApple OSS Distributions 		goto out_imf_rollback;
1645*aca3beaaSApple OSS Distributions 	}
1646*aca3beaaSApple OSS Distributions 
1647*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
1648*aca3beaaSApple OSS Distributions 	error = igmp_change_state(inm, &itp);
1649*aca3beaaSApple OSS Distributions 	INM_UNLOCK(inm);
1650*aca3beaaSApple OSS Distributions #if IGMP_DEBUG
1651*aca3beaaSApple OSS Distributions 	if (error) {
1652*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
1653*aca3beaaSApple OSS Distributions 	}
1654*aca3beaaSApple OSS Distributions #endif
1655*aca3beaaSApple OSS Distributions 
1656*aca3beaaSApple OSS Distributions out_imf_rollback:
1657*aca3beaaSApple OSS Distributions 	if (error) {
1658*aca3beaaSApple OSS Distributions 		imf_rollback(imf);
1659*aca3beaaSApple OSS Distributions 	} else {
1660*aca3beaaSApple OSS Distributions 		imf_commit(imf);
1661*aca3beaaSApple OSS Distributions 	}
1662*aca3beaaSApple OSS Distributions 
1663*aca3beaaSApple OSS Distributions 	imf_reap(imf);
1664*aca3beaaSApple OSS Distributions 
1665*aca3beaaSApple OSS Distributions out_imo_locked:
1666*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(imo);
1667*aca3beaaSApple OSS Distributions 	IMO_REMREF(imo);        /* from inp_findmoptions() */
1668*aca3beaaSApple OSS Distributions 
1669*aca3beaaSApple OSS Distributions 	/* schedule timer now that we've dropped the lock(s) */
1670*aca3beaaSApple OSS Distributions 	igmp_set_fast_timeout(&itp);
1671*aca3beaaSApple OSS Distributions 
1672*aca3beaaSApple OSS Distributions 	return error;
1673*aca3beaaSApple OSS Distributions }
1674*aca3beaaSApple OSS Distributions 
1675*aca3beaaSApple OSS Distributions /*
1676*aca3beaaSApple OSS Distributions  * Given an inpcb, return its multicast options structure pointer.
1677*aca3beaaSApple OSS Distributions  *
1678*aca3beaaSApple OSS Distributions  * Caller is responsible for locking the inpcb, and releasing the
1679*aca3beaaSApple OSS Distributions  * extra reference held on the imo, upon a successful return.
1680*aca3beaaSApple OSS Distributions  */
1681*aca3beaaSApple OSS Distributions static struct ip_moptions *
inp_findmoptions(struct inpcb * inp)1682*aca3beaaSApple OSS Distributions inp_findmoptions(struct inpcb *inp)
1683*aca3beaaSApple OSS Distributions {
1684*aca3beaaSApple OSS Distributions 	struct ip_moptions       *imo;
1685*aca3beaaSApple OSS Distributions 	struct in_multi         **immp;
1686*aca3beaaSApple OSS Distributions 	struct in_mfilter        *imfp;
1687*aca3beaaSApple OSS Distributions 	size_t                    idx;
1688*aca3beaaSApple OSS Distributions 
1689*aca3beaaSApple OSS Distributions 	if ((imo = inp->inp_moptions) != NULL) {
1690*aca3beaaSApple OSS Distributions 		IMO_ADDREF(imo);        /* for caller */
1691*aca3beaaSApple OSS Distributions 		return imo;
1692*aca3beaaSApple OSS Distributions 	}
1693*aca3beaaSApple OSS Distributions 
1694*aca3beaaSApple OSS Distributions 	imo = ip_allocmoptions(Z_WAITOK);
1695*aca3beaaSApple OSS Distributions 	if (imo == NULL) {
1696*aca3beaaSApple OSS Distributions 		return NULL;
1697*aca3beaaSApple OSS Distributions 	}
1698*aca3beaaSApple OSS Distributions 
1699*aca3beaaSApple OSS Distributions 	immp = kalloc_type(struct in_multi *, IP_MIN_MEMBERSHIPS,
1700*aca3beaaSApple OSS Distributions 	    Z_WAITOK | Z_ZERO | Z_NOFAIL);
1701*aca3beaaSApple OSS Distributions 	imfp = kalloc_type(struct in_mfilter, IP_MIN_MEMBERSHIPS,
1702*aca3beaaSApple OSS Distributions 	    Z_WAITOK | Z_ZERO | Z_NOFAIL);
1703*aca3beaaSApple OSS Distributions 
1704*aca3beaaSApple OSS Distributions 	imo->imo_multicast_ifp = NULL;
1705*aca3beaaSApple OSS Distributions 	imo->imo_multicast_addr.s_addr = INADDR_ANY;
1706*aca3beaaSApple OSS Distributions 	imo->imo_multicast_vif = -1;
1707*aca3beaaSApple OSS Distributions 	imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
1708*aca3beaaSApple OSS Distributions 	imo->imo_multicast_loop = !!in_mcast_loop;
1709*aca3beaaSApple OSS Distributions 	imo->imo_num_memberships = 0;
1710*aca3beaaSApple OSS Distributions 	imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
1711*aca3beaaSApple OSS Distributions 	imo->imo_membership = immp;
1712*aca3beaaSApple OSS Distributions 
1713*aca3beaaSApple OSS Distributions 	/* Initialize per-group source filters. */
1714*aca3beaaSApple OSS Distributions 	for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) {
1715*aca3beaaSApple OSS Distributions 		imf_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
1716*aca3beaaSApple OSS Distributions 	}
1717*aca3beaaSApple OSS Distributions 
1718*aca3beaaSApple OSS Distributions 	imo->imo_mfilters = imfp;
1719*aca3beaaSApple OSS Distributions 	inp->inp_moptions = imo; /* keep reference from ip_allocmoptions() */
1720*aca3beaaSApple OSS Distributions 	IMO_ADDREF(imo);        /* for caller */
1721*aca3beaaSApple OSS Distributions 
1722*aca3beaaSApple OSS Distributions 	return imo;
1723*aca3beaaSApple OSS Distributions }
1724*aca3beaaSApple OSS Distributions /*
1725*aca3beaaSApple OSS Distributions  * Atomically get source filters on a socket for an IPv4 multicast group.
1726*aca3beaaSApple OSS Distributions  */
1727*aca3beaaSApple OSS Distributions static int
inp_get_source_filters(struct inpcb * inp,struct sockopt * sopt)1728*aca3beaaSApple OSS Distributions inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
1729*aca3beaaSApple OSS Distributions {
1730*aca3beaaSApple OSS Distributions 	struct __msfilterreq64  msfr = {}, msfr64;
1731*aca3beaaSApple OSS Distributions 	struct __msfilterreq32  msfr32;
1732*aca3beaaSApple OSS Distributions 	struct sockaddr_in      *gsa;
1733*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
1734*aca3beaaSApple OSS Distributions 	struct ip_moptions      *imo;
1735*aca3beaaSApple OSS Distributions 	struct in_mfilter       *imf;
1736*aca3beaaSApple OSS Distributions 	struct ip_msource       *ims;
1737*aca3beaaSApple OSS Distributions 	struct in_msource       *lims;
1738*aca3beaaSApple OSS Distributions 	struct sockaddr_in      *psin;
1739*aca3beaaSApple OSS Distributions 	struct sockaddr_storage *ptss;
1740*aca3beaaSApple OSS Distributions 	struct sockaddr_storage *tss;
1741*aca3beaaSApple OSS Distributions 	int                      error;
1742*aca3beaaSApple OSS Distributions 	size_t                   idx;
1743*aca3beaaSApple OSS Distributions 	uint32_t                 nsrcs, ncsrcs;
1744*aca3beaaSApple OSS Distributions 	user_addr_t              tmp_ptr;
1745*aca3beaaSApple OSS Distributions 
1746*aca3beaaSApple OSS Distributions 	imo = inp->inp_moptions;
1747*aca3beaaSApple OSS Distributions 	VERIFY(imo != NULL);
1748*aca3beaaSApple OSS Distributions 
1749*aca3beaaSApple OSS Distributions 	int is_64bit_proc = IS_64BIT_PROCESS(current_proc());
1750*aca3beaaSApple OSS Distributions 
1751*aca3beaaSApple OSS Distributions 	if (is_64bit_proc) {
1752*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &msfr64,
1753*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq64),
1754*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq64));
1755*aca3beaaSApple OSS Distributions 		if (error) {
1756*aca3beaaSApple OSS Distributions 			return error;
1757*aca3beaaSApple OSS Distributions 		}
1758*aca3beaaSApple OSS Distributions 		/* we never use msfr.msfr_srcs; */
1759*aca3beaaSApple OSS Distributions 		memcpy(&msfr, &msfr64, sizeof(msfr64));
1760*aca3beaaSApple OSS Distributions 	} else {
1761*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &msfr32,
1762*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq32),
1763*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq32));
1764*aca3beaaSApple OSS Distributions 		if (error) {
1765*aca3beaaSApple OSS Distributions 			return error;
1766*aca3beaaSApple OSS Distributions 		}
1767*aca3beaaSApple OSS Distributions 		/* we never use msfr.msfr_srcs; */
1768*aca3beaaSApple OSS Distributions 		memcpy(&msfr, &msfr32, sizeof(msfr32));
1769*aca3beaaSApple OSS Distributions 	}
1770*aca3beaaSApple OSS Distributions 
1771*aca3beaaSApple OSS Distributions 	ifnet_head_lock_shared();
1772*aca3beaaSApple OSS Distributions 	if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
1773*aca3beaaSApple OSS Distributions 		ifnet_head_done();
1774*aca3beaaSApple OSS Distributions 		return EADDRNOTAVAIL;
1775*aca3beaaSApple OSS Distributions 	}
1776*aca3beaaSApple OSS Distributions 
1777*aca3beaaSApple OSS Distributions 	ifp = ifindex2ifnet[msfr.msfr_ifindex];
1778*aca3beaaSApple OSS Distributions 	ifnet_head_done();
1779*aca3beaaSApple OSS Distributions 
1780*aca3beaaSApple OSS Distributions 	if (ifp == NULL) {
1781*aca3beaaSApple OSS Distributions 		return EADDRNOTAVAIL;
1782*aca3beaaSApple OSS Distributions 	}
1783*aca3beaaSApple OSS Distributions 
1784*aca3beaaSApple OSS Distributions 	if ((size_t) msfr.msfr_nsrcs >
1785*aca3beaaSApple OSS Distributions 	    UINT32_MAX / sizeof(struct sockaddr_storage)) {
1786*aca3beaaSApple OSS Distributions 		msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage);
1787*aca3beaaSApple OSS Distributions 	}
1788*aca3beaaSApple OSS Distributions 
1789*aca3beaaSApple OSS Distributions 	if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) {
1790*aca3beaaSApple OSS Distributions 		msfr.msfr_nsrcs = in_mcast_maxsocksrc;
1791*aca3beaaSApple OSS Distributions 	}
1792*aca3beaaSApple OSS Distributions 
1793*aca3beaaSApple OSS Distributions 	IMO_LOCK(imo);
1794*aca3beaaSApple OSS Distributions 	/*
1795*aca3beaaSApple OSS Distributions 	 * Lookup group on the socket.
1796*aca3beaaSApple OSS Distributions 	 */
1797*aca3beaaSApple OSS Distributions 	gsa = (struct sockaddr_in *)&msfr.msfr_group;
1798*aca3beaaSApple OSS Distributions 
1799*aca3beaaSApple OSS Distributions 	idx = imo_match_group(imo, ifp, gsa);
1800*aca3beaaSApple OSS Distributions 	if (idx == (size_t)-1 || imo->imo_mfilters == NULL) {
1801*aca3beaaSApple OSS Distributions 		IMO_UNLOCK(imo);
1802*aca3beaaSApple OSS Distributions 		return EADDRNOTAVAIL;
1803*aca3beaaSApple OSS Distributions 	}
1804*aca3beaaSApple OSS Distributions 	imf = &imo->imo_mfilters[idx];
1805*aca3beaaSApple OSS Distributions 
1806*aca3beaaSApple OSS Distributions 	/*
1807*aca3beaaSApple OSS Distributions 	 * Ignore memberships which are in limbo.
1808*aca3beaaSApple OSS Distributions 	 */
1809*aca3beaaSApple OSS Distributions 	if (imf->imf_st[1] == MCAST_UNDEFINED) {
1810*aca3beaaSApple OSS Distributions 		IMO_UNLOCK(imo);
1811*aca3beaaSApple OSS Distributions 		return EAGAIN;
1812*aca3beaaSApple OSS Distributions 	}
1813*aca3beaaSApple OSS Distributions 	msfr.msfr_fmode = imf->imf_st[1];
1814*aca3beaaSApple OSS Distributions 
1815*aca3beaaSApple OSS Distributions 	/*
1816*aca3beaaSApple OSS Distributions 	 * If the user specified a buffer, copy out the source filter
1817*aca3beaaSApple OSS Distributions 	 * entries to userland gracefully.
1818*aca3beaaSApple OSS Distributions 	 * We only copy out the number of entries which userland
1819*aca3beaaSApple OSS Distributions 	 * has asked for, but we always tell userland how big the
1820*aca3beaaSApple OSS Distributions 	 * buffer really needs to be.
1821*aca3beaaSApple OSS Distributions 	 */
1822*aca3beaaSApple OSS Distributions 
1823*aca3beaaSApple OSS Distributions 	if (is_64bit_proc) {
1824*aca3beaaSApple OSS Distributions 		tmp_ptr = CAST_USER_ADDR_T(msfr64.msfr_srcs);
1825*aca3beaaSApple OSS Distributions 	} else {
1826*aca3beaaSApple OSS Distributions 		tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
1827*aca3beaaSApple OSS Distributions 	}
1828*aca3beaaSApple OSS Distributions 
1829*aca3beaaSApple OSS Distributions 	tss = NULL;
1830*aca3beaaSApple OSS Distributions 	if (tmp_ptr != USER_ADDR_NULL && msfr.msfr_nsrcs > 0) {
1831*aca3beaaSApple OSS Distributions 		tss = kalloc_data((size_t)msfr.msfr_nsrcs * sizeof(*tss),
1832*aca3beaaSApple OSS Distributions 		    Z_WAITOK | Z_ZERO);
1833*aca3beaaSApple OSS Distributions 		if (tss == NULL) {
1834*aca3beaaSApple OSS Distributions 			IMO_UNLOCK(imo);
1835*aca3beaaSApple OSS Distributions 			return ENOBUFS;
1836*aca3beaaSApple OSS Distributions 		}
1837*aca3beaaSApple OSS Distributions 	}
1838*aca3beaaSApple OSS Distributions 
1839*aca3beaaSApple OSS Distributions 	/*
1840*aca3beaaSApple OSS Distributions 	 * Count number of sources in-mode at t0.
1841*aca3beaaSApple OSS Distributions 	 * If buffer space exists and remains, copy out source entries.
1842*aca3beaaSApple OSS Distributions 	 */
1843*aca3beaaSApple OSS Distributions 	nsrcs = msfr.msfr_nsrcs;
1844*aca3beaaSApple OSS Distributions 	ncsrcs = 0;
1845*aca3beaaSApple OSS Distributions 	ptss = tss;
1846*aca3beaaSApple OSS Distributions 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1847*aca3beaaSApple OSS Distributions 		lims = (struct in_msource *)ims;
1848*aca3beaaSApple OSS Distributions 		if (lims->imsl_st[0] == MCAST_UNDEFINED ||
1849*aca3beaaSApple OSS Distributions 		    lims->imsl_st[0] != imf->imf_st[0]) {
1850*aca3beaaSApple OSS Distributions 			continue;
1851*aca3beaaSApple OSS Distributions 		}
1852*aca3beaaSApple OSS Distributions 		if (tss != NULL && nsrcs > 0) {
1853*aca3beaaSApple OSS Distributions 			psin = (struct sockaddr_in *)ptss;
1854*aca3beaaSApple OSS Distributions 			psin->sin_family = AF_INET;
1855*aca3beaaSApple OSS Distributions 			psin->sin_len = sizeof(struct sockaddr_in);
1856*aca3beaaSApple OSS Distributions 			psin->sin_addr.s_addr = htonl(lims->ims_haddr);
1857*aca3beaaSApple OSS Distributions 			psin->sin_port = 0;
1858*aca3beaaSApple OSS Distributions 			++ptss;
1859*aca3beaaSApple OSS Distributions 			--nsrcs;
1860*aca3beaaSApple OSS Distributions 			++ncsrcs;
1861*aca3beaaSApple OSS Distributions 		}
1862*aca3beaaSApple OSS Distributions 	}
1863*aca3beaaSApple OSS Distributions 
1864*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(imo);
1865*aca3beaaSApple OSS Distributions 
1866*aca3beaaSApple OSS Distributions 	if (tss != NULL) {
1867*aca3beaaSApple OSS Distributions 		error = copyout(tss, CAST_USER_ADDR_T(tmp_ptr), ncsrcs * sizeof(*tss));
1868*aca3beaaSApple OSS Distributions 		kfree_data(tss, (size_t)msfr.msfr_nsrcs * sizeof(*tss));
1869*aca3beaaSApple OSS Distributions 		if (error) {
1870*aca3beaaSApple OSS Distributions 			return error;
1871*aca3beaaSApple OSS Distributions 		}
1872*aca3beaaSApple OSS Distributions 	}
1873*aca3beaaSApple OSS Distributions 
1874*aca3beaaSApple OSS Distributions 	msfr.msfr_nsrcs = ncsrcs;
1875*aca3beaaSApple OSS Distributions 	if (is_64bit_proc) {
1876*aca3beaaSApple OSS Distributions 		msfr64.msfr_ifindex = msfr.msfr_ifindex;
1877*aca3beaaSApple OSS Distributions 		msfr64.msfr_fmode   = msfr.msfr_fmode;
1878*aca3beaaSApple OSS Distributions 		msfr64.msfr_nsrcs   = msfr.msfr_nsrcs;
1879*aca3beaaSApple OSS Distributions 		memcpy(&msfr64.msfr_group, &msfr.msfr_group,
1880*aca3beaaSApple OSS Distributions 		    sizeof(struct sockaddr_storage));
1881*aca3beaaSApple OSS Distributions 		error = sooptcopyout(sopt, &msfr64,
1882*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq64));
1883*aca3beaaSApple OSS Distributions 	} else {
1884*aca3beaaSApple OSS Distributions 		msfr32.msfr_ifindex = msfr.msfr_ifindex;
1885*aca3beaaSApple OSS Distributions 		msfr32.msfr_fmode   = msfr.msfr_fmode;
1886*aca3beaaSApple OSS Distributions 		msfr32.msfr_nsrcs   = msfr.msfr_nsrcs;
1887*aca3beaaSApple OSS Distributions 		memcpy(&msfr32.msfr_group, &msfr.msfr_group,
1888*aca3beaaSApple OSS Distributions 		    sizeof(struct sockaddr_storage));
1889*aca3beaaSApple OSS Distributions 		error = sooptcopyout(sopt, &msfr32,
1890*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq32));
1891*aca3beaaSApple OSS Distributions 	}
1892*aca3beaaSApple OSS Distributions 
1893*aca3beaaSApple OSS Distributions 	return error;
1894*aca3beaaSApple OSS Distributions }
1895*aca3beaaSApple OSS Distributions 
1896*aca3beaaSApple OSS Distributions /*
1897*aca3beaaSApple OSS Distributions  * Return the IP multicast options in response to user getsockopt().
1898*aca3beaaSApple OSS Distributions  */
1899*aca3beaaSApple OSS Distributions int
inp_getmoptions(struct inpcb * inp,struct sockopt * sopt)1900*aca3beaaSApple OSS Distributions inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
1901*aca3beaaSApple OSS Distributions {
1902*aca3beaaSApple OSS Distributions 	struct ip_mreqn          mreqn;
1903*aca3beaaSApple OSS Distributions 	struct ip_moptions      *imo;
1904*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
1905*aca3beaaSApple OSS Distributions 	struct in_ifaddr        *ia;
1906*aca3beaaSApple OSS Distributions 	int                      error, optval;
1907*aca3beaaSApple OSS Distributions 	unsigned int             ifindex;
1908*aca3beaaSApple OSS Distributions 	u_char                   coptval;
1909*aca3beaaSApple OSS Distributions 
1910*aca3beaaSApple OSS Distributions 	imo = inp->inp_moptions;
1911*aca3beaaSApple OSS Distributions 	/*
1912*aca3beaaSApple OSS Distributions 	 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
1913*aca3beaaSApple OSS Distributions 	 * or is a divert socket, reject it.
1914*aca3beaaSApple OSS Distributions 	 */
1915*aca3beaaSApple OSS Distributions 	if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT ||
1916*aca3beaaSApple OSS Distributions 	    (SOCK_TYPE(inp->inp_socket) != SOCK_RAW &&
1917*aca3beaaSApple OSS Distributions 	    SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) {
1918*aca3beaaSApple OSS Distributions 		return EOPNOTSUPP;
1919*aca3beaaSApple OSS Distributions 	}
1920*aca3beaaSApple OSS Distributions 
1921*aca3beaaSApple OSS Distributions 	error = 0;
1922*aca3beaaSApple OSS Distributions 	switch (sopt->sopt_name) {
1923*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_IF:
1924*aca3beaaSApple OSS Distributions 		memset(&mreqn, 0, sizeof(struct ip_mreqn));
1925*aca3beaaSApple OSS Distributions 		if (imo != NULL) {
1926*aca3beaaSApple OSS Distributions 			IMO_LOCK(imo);
1927*aca3beaaSApple OSS Distributions 			ifp = imo->imo_multicast_ifp;
1928*aca3beaaSApple OSS Distributions 			if (!in_nullhost(imo->imo_multicast_addr)) {
1929*aca3beaaSApple OSS Distributions 				mreqn.imr_address = imo->imo_multicast_addr;
1930*aca3beaaSApple OSS Distributions 			} else if (ifp != NULL) {
1931*aca3beaaSApple OSS Distributions 				mreqn.imr_ifindex = ifp->if_index;
1932*aca3beaaSApple OSS Distributions 				IFP_TO_IA(ifp, ia);
1933*aca3beaaSApple OSS Distributions 				if (ia != NULL) {
1934*aca3beaaSApple OSS Distributions 					IFA_LOCK_SPIN(&ia->ia_ifa);
1935*aca3beaaSApple OSS Distributions 					mreqn.imr_address =
1936*aca3beaaSApple OSS Distributions 					    IA_SIN(ia)->sin_addr;
1937*aca3beaaSApple OSS Distributions 					IFA_UNLOCK(&ia->ia_ifa);
1938*aca3beaaSApple OSS Distributions 					IFA_REMREF(&ia->ia_ifa);
1939*aca3beaaSApple OSS Distributions 				}
1940*aca3beaaSApple OSS Distributions 			}
1941*aca3beaaSApple OSS Distributions 			IMO_UNLOCK(imo);
1942*aca3beaaSApple OSS Distributions 		}
1943*aca3beaaSApple OSS Distributions 		if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
1944*aca3beaaSApple OSS Distributions 			error = sooptcopyout(sopt, &mreqn,
1945*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreqn));
1946*aca3beaaSApple OSS Distributions 		} else {
1947*aca3beaaSApple OSS Distributions 			error = sooptcopyout(sopt, &mreqn.imr_address,
1948*aca3beaaSApple OSS Distributions 			    sizeof(struct in_addr));
1949*aca3beaaSApple OSS Distributions 		}
1950*aca3beaaSApple OSS Distributions 		break;
1951*aca3beaaSApple OSS Distributions 
1952*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_IFINDEX:
1953*aca3beaaSApple OSS Distributions 		if (imo != NULL) {
1954*aca3beaaSApple OSS Distributions 			IMO_LOCK(imo);
1955*aca3beaaSApple OSS Distributions 		}
1956*aca3beaaSApple OSS Distributions 		if (imo == NULL || imo->imo_multicast_ifp == NULL) {
1957*aca3beaaSApple OSS Distributions 			ifindex = 0;
1958*aca3beaaSApple OSS Distributions 		} else {
1959*aca3beaaSApple OSS Distributions 			ifindex = imo->imo_multicast_ifp->if_index;
1960*aca3beaaSApple OSS Distributions 		}
1961*aca3beaaSApple OSS Distributions 		if (imo != NULL) {
1962*aca3beaaSApple OSS Distributions 			IMO_UNLOCK(imo);
1963*aca3beaaSApple OSS Distributions 		}
1964*aca3beaaSApple OSS Distributions 		error = sooptcopyout(sopt, &ifindex, sizeof(ifindex));
1965*aca3beaaSApple OSS Distributions 		break;
1966*aca3beaaSApple OSS Distributions 
1967*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_TTL:
1968*aca3beaaSApple OSS Distributions 		if (imo == NULL) {
1969*aca3beaaSApple OSS Distributions 			optval = coptval = IP_DEFAULT_MULTICAST_TTL;
1970*aca3beaaSApple OSS Distributions 		} else {
1971*aca3beaaSApple OSS Distributions 			IMO_LOCK(imo);
1972*aca3beaaSApple OSS Distributions 			optval = coptval = imo->imo_multicast_ttl;
1973*aca3beaaSApple OSS Distributions 			IMO_UNLOCK(imo);
1974*aca3beaaSApple OSS Distributions 		}
1975*aca3beaaSApple OSS Distributions 		if (sopt->sopt_valsize == sizeof(u_char)) {
1976*aca3beaaSApple OSS Distributions 			error = sooptcopyout(sopt, &coptval, sizeof(u_char));
1977*aca3beaaSApple OSS Distributions 		} else {
1978*aca3beaaSApple OSS Distributions 			error = sooptcopyout(sopt, &optval, sizeof(int));
1979*aca3beaaSApple OSS Distributions 		}
1980*aca3beaaSApple OSS Distributions 		break;
1981*aca3beaaSApple OSS Distributions 
1982*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_LOOP:
1983*aca3beaaSApple OSS Distributions 		if (imo == 0) {
1984*aca3beaaSApple OSS Distributions 			optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
1985*aca3beaaSApple OSS Distributions 		} else {
1986*aca3beaaSApple OSS Distributions 			IMO_LOCK(imo);
1987*aca3beaaSApple OSS Distributions 			optval = coptval = imo->imo_multicast_loop;
1988*aca3beaaSApple OSS Distributions 			IMO_UNLOCK(imo);
1989*aca3beaaSApple OSS Distributions 		}
1990*aca3beaaSApple OSS Distributions 		if (sopt->sopt_valsize == sizeof(u_char)) {
1991*aca3beaaSApple OSS Distributions 			error = sooptcopyout(sopt, &coptval, sizeof(u_char));
1992*aca3beaaSApple OSS Distributions 		} else {
1993*aca3beaaSApple OSS Distributions 			error = sooptcopyout(sopt, &optval, sizeof(int));
1994*aca3beaaSApple OSS Distributions 		}
1995*aca3beaaSApple OSS Distributions 		break;
1996*aca3beaaSApple OSS Distributions 
1997*aca3beaaSApple OSS Distributions 	case IP_MSFILTER:
1998*aca3beaaSApple OSS Distributions 		if (imo == NULL) {
1999*aca3beaaSApple OSS Distributions 			error = EADDRNOTAVAIL;
2000*aca3beaaSApple OSS Distributions 		} else {
2001*aca3beaaSApple OSS Distributions 			error = inp_get_source_filters(inp, sopt);
2002*aca3beaaSApple OSS Distributions 		}
2003*aca3beaaSApple OSS Distributions 		break;
2004*aca3beaaSApple OSS Distributions 
2005*aca3beaaSApple OSS Distributions 	default:
2006*aca3beaaSApple OSS Distributions 		error = ENOPROTOOPT;
2007*aca3beaaSApple OSS Distributions 		break;
2008*aca3beaaSApple OSS Distributions 	}
2009*aca3beaaSApple OSS Distributions 
2010*aca3beaaSApple OSS Distributions 	return error;
2011*aca3beaaSApple OSS Distributions }
2012*aca3beaaSApple OSS Distributions 
2013*aca3beaaSApple OSS Distributions /*
2014*aca3beaaSApple OSS Distributions  * Look up the ifnet to use for a multicast group membership,
2015*aca3beaaSApple OSS Distributions  * given the IPv4 address of an interface, and the IPv4 group address.
2016*aca3beaaSApple OSS Distributions  *
2017*aca3beaaSApple OSS Distributions  * This routine exists to support legacy multicast applications
2018*aca3beaaSApple OSS Distributions  * which do not understand that multicast memberships are scoped to
2019*aca3beaaSApple OSS Distributions  * specific physical links in the networking stack, or which need
2020*aca3beaaSApple OSS Distributions  * to join link-scope groups before IPv4 addresses are configured.
2021*aca3beaaSApple OSS Distributions  *
2022*aca3beaaSApple OSS Distributions  * If inp is non-NULL and is bound to an interface, use this socket's
2023*aca3beaaSApple OSS Distributions  * inp_boundif for any required routing table lookup.
2024*aca3beaaSApple OSS Distributions  *
2025*aca3beaaSApple OSS Distributions  * If the route lookup fails, attempt to use the first non-loopback
2026*aca3beaaSApple OSS Distributions  * interface with multicast capability in the system as a
2027*aca3beaaSApple OSS Distributions  * last resort. The legacy IPv4 ASM API requires that we do
2028*aca3beaaSApple OSS Distributions  * this in order to allow groups to be joined when the routing
2029*aca3beaaSApple OSS Distributions  * table has not yet been populated during boot.
2030*aca3beaaSApple OSS Distributions  *
2031*aca3beaaSApple OSS Distributions  * Returns NULL if no ifp could be found.
2032*aca3beaaSApple OSS Distributions  *
2033*aca3beaaSApple OSS Distributions  */
2034*aca3beaaSApple OSS Distributions static struct ifnet *
inp_lookup_mcast_ifp(const struct inpcb * inp,const struct sockaddr_in * gsin,const struct in_addr ina)2035*aca3beaaSApple OSS Distributions inp_lookup_mcast_ifp(const struct inpcb *inp,
2036*aca3beaaSApple OSS Distributions     const struct sockaddr_in *gsin, const struct in_addr ina)
2037*aca3beaaSApple OSS Distributions {
2038*aca3beaaSApple OSS Distributions 	struct ifnet    *ifp;
2039*aca3beaaSApple OSS Distributions 	unsigned int     ifindex = 0;
2040*aca3beaaSApple OSS Distributions 
2041*aca3beaaSApple OSS Distributions 	VERIFY(gsin->sin_family == AF_INET);
2042*aca3beaaSApple OSS Distributions 	VERIFY(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr)));
2043*aca3beaaSApple OSS Distributions 
2044*aca3beaaSApple OSS Distributions 	ifp = NULL;
2045*aca3beaaSApple OSS Distributions 	if (!in_nullhost(ina)) {
2046*aca3beaaSApple OSS Distributions 		struct in_addr new_ina;
2047*aca3beaaSApple OSS Distributions 		memcpy(&new_ina, &ina, sizeof(struct in_addr));
2048*aca3beaaSApple OSS Distributions 		ifp = ip_multicast_if(&new_ina, &ifindex);
2049*aca3beaaSApple OSS Distributions 	} else {
2050*aca3beaaSApple OSS Distributions 		struct route ro;
2051*aca3beaaSApple OSS Distributions 		unsigned int ifscope = IFSCOPE_NONE;
2052*aca3beaaSApple OSS Distributions 
2053*aca3beaaSApple OSS Distributions 		if (inp != NULL && (inp->inp_flags & INP_BOUND_IF)) {
2054*aca3beaaSApple OSS Distributions 			ifscope = inp->inp_boundifp->if_index;
2055*aca3beaaSApple OSS Distributions 		}
2056*aca3beaaSApple OSS Distributions 
2057*aca3beaaSApple OSS Distributions 		bzero(&ro, sizeof(ro));
2058*aca3beaaSApple OSS Distributions 		memcpy(&ro.ro_dst, gsin, sizeof(struct sockaddr_in));
2059*aca3beaaSApple OSS Distributions 		rtalloc_scoped_ign(&ro, 0, ifscope);
2060*aca3beaaSApple OSS Distributions 		if (ro.ro_rt != NULL) {
2061*aca3beaaSApple OSS Distributions 			ifp = ro.ro_rt->rt_ifp;
2062*aca3beaaSApple OSS Distributions 			VERIFY(ifp != NULL);
2063*aca3beaaSApple OSS Distributions 		} else {
2064*aca3beaaSApple OSS Distributions 			struct in_ifaddr *ia;
2065*aca3beaaSApple OSS Distributions 			struct ifnet *mifp;
2066*aca3beaaSApple OSS Distributions 
2067*aca3beaaSApple OSS Distributions 			mifp = NULL;
2068*aca3beaaSApple OSS Distributions 			lck_rw_lock_shared(&in_ifaddr_rwlock);
2069*aca3beaaSApple OSS Distributions 			TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
2070*aca3beaaSApple OSS Distributions 				IFA_LOCK_SPIN(&ia->ia_ifa);
2071*aca3beaaSApple OSS Distributions 				mifp = ia->ia_ifp;
2072*aca3beaaSApple OSS Distributions 				IFA_UNLOCK(&ia->ia_ifa);
2073*aca3beaaSApple OSS Distributions 				if (!(mifp->if_flags & IFF_LOOPBACK) &&
2074*aca3beaaSApple OSS Distributions 				    (mifp->if_flags & IFF_MULTICAST)) {
2075*aca3beaaSApple OSS Distributions 					ifp = mifp;
2076*aca3beaaSApple OSS Distributions 					break;
2077*aca3beaaSApple OSS Distributions 				}
2078*aca3beaaSApple OSS Distributions 			}
2079*aca3beaaSApple OSS Distributions 			lck_rw_done(&in_ifaddr_rwlock);
2080*aca3beaaSApple OSS Distributions 		}
2081*aca3beaaSApple OSS Distributions 		ROUTE_RELEASE(&ro);
2082*aca3beaaSApple OSS Distributions 	}
2083*aca3beaaSApple OSS Distributions 
2084*aca3beaaSApple OSS Distributions 	return ifp;
2085*aca3beaaSApple OSS Distributions }
2086*aca3beaaSApple OSS Distributions 
2087*aca3beaaSApple OSS Distributions /*
2088*aca3beaaSApple OSS Distributions  * Join an IPv4 multicast group, possibly with a source.
2089*aca3beaaSApple OSS Distributions  *
2090*aca3beaaSApple OSS Distributions  * NB: sopt->sopt_val might point to the kernel address space. This means that
2091*aca3beaaSApple OSS Distributions  * we were called by the IPv6 stack due to the presence of an IPv6 v4 mapped
2092*aca3beaaSApple OSS Distributions  * address. In this scenario, sopt_p points to kernproc and sooptcopyin() will
2093*aca3beaaSApple OSS Distributions  * just issue an in-kernel memcpy.
2094*aca3beaaSApple OSS Distributions  */
2095*aca3beaaSApple OSS Distributions int
inp_join_group(struct inpcb * inp,struct sockopt * sopt)2096*aca3beaaSApple OSS Distributions inp_join_group(struct inpcb *inp, struct sockopt *sopt)
2097*aca3beaaSApple OSS Distributions {
2098*aca3beaaSApple OSS Distributions 	struct group_source_req          gsr;
2099*aca3beaaSApple OSS Distributions 	struct sockaddr_in              *gsa, *ssa;
2100*aca3beaaSApple OSS Distributions 	struct ifnet                    *ifp;
2101*aca3beaaSApple OSS Distributions 	struct in_mfilter               *imf;
2102*aca3beaaSApple OSS Distributions 	struct ip_moptions              *imo;
2103*aca3beaaSApple OSS Distributions 	struct in_multi                 *inm = NULL;
2104*aca3beaaSApple OSS Distributions 	struct in_msource               *lims;
2105*aca3beaaSApple OSS Distributions 	size_t                           idx;
2106*aca3beaaSApple OSS Distributions 	int                              error, is_new;
2107*aca3beaaSApple OSS Distributions 	struct igmp_tparams              itp;
2108*aca3beaaSApple OSS Distributions 
2109*aca3beaaSApple OSS Distributions 	bzero(&itp, sizeof(itp));
2110*aca3beaaSApple OSS Distributions 	ifp = NULL;
2111*aca3beaaSApple OSS Distributions 	imf = NULL;
2112*aca3beaaSApple OSS Distributions 	error = 0;
2113*aca3beaaSApple OSS Distributions 	is_new = 0;
2114*aca3beaaSApple OSS Distributions 
2115*aca3beaaSApple OSS Distributions 	memset(&gsr, 0, sizeof(struct group_source_req));
2116*aca3beaaSApple OSS Distributions 	gsa = (struct sockaddr_in *)&gsr.gsr_group;
2117*aca3beaaSApple OSS Distributions 	gsa->sin_family = AF_UNSPEC;
2118*aca3beaaSApple OSS Distributions 	ssa = (struct sockaddr_in *)&gsr.gsr_source;
2119*aca3beaaSApple OSS Distributions 	ssa->sin_family = AF_UNSPEC;
2120*aca3beaaSApple OSS Distributions 
2121*aca3beaaSApple OSS Distributions 	switch (sopt->sopt_name) {
2122*aca3beaaSApple OSS Distributions 	case IP_ADD_MEMBERSHIP:
2123*aca3beaaSApple OSS Distributions 	case IP_ADD_SOURCE_MEMBERSHIP: {
2124*aca3beaaSApple OSS Distributions 		struct ip_mreq_source    mreqs;
2125*aca3beaaSApple OSS Distributions 
2126*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == IP_ADD_MEMBERSHIP) {
2127*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &mreqs,
2128*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq),
2129*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq));
2130*aca3beaaSApple OSS Distributions 			/*
2131*aca3beaaSApple OSS Distributions 			 * Do argument switcharoo from ip_mreq into
2132*aca3beaaSApple OSS Distributions 			 * ip_mreq_source to avoid using two instances.
2133*aca3beaaSApple OSS Distributions 			 */
2134*aca3beaaSApple OSS Distributions 			mreqs.imr_interface = mreqs.imr_sourceaddr;
2135*aca3beaaSApple OSS Distributions 			mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
2136*aca3beaaSApple OSS Distributions 		} else if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
2137*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &mreqs,
2138*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq_source),
2139*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq_source));
2140*aca3beaaSApple OSS Distributions 		}
2141*aca3beaaSApple OSS Distributions 		if (error) {
2142*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: error copyin IP_ADD_MEMBERSHIP/"
2143*aca3beaaSApple OSS Distributions 			    "IP_ADD_SOURCE_MEMBERSHIP %d err=%d\n",
2144*aca3beaaSApple OSS Distributions 			    __func__, sopt->sopt_name, error));
2145*aca3beaaSApple OSS Distributions 			return error;
2146*aca3beaaSApple OSS Distributions 		}
2147*aca3beaaSApple OSS Distributions 
2148*aca3beaaSApple OSS Distributions 		gsa->sin_family = AF_INET;
2149*aca3beaaSApple OSS Distributions 		gsa->sin_len = sizeof(struct sockaddr_in);
2150*aca3beaaSApple OSS Distributions 		gsa->sin_addr = mreqs.imr_multiaddr;
2151*aca3beaaSApple OSS Distributions 
2152*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) {
2153*aca3beaaSApple OSS Distributions 			ssa->sin_family = AF_INET;
2154*aca3beaaSApple OSS Distributions 			ssa->sin_len = sizeof(struct sockaddr_in);
2155*aca3beaaSApple OSS Distributions 			ssa->sin_addr = mreqs.imr_sourceaddr;
2156*aca3beaaSApple OSS Distributions 		}
2157*aca3beaaSApple OSS Distributions 
2158*aca3beaaSApple OSS Distributions 		if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2159*aca3beaaSApple OSS Distributions 			return EINVAL;
2160*aca3beaaSApple OSS Distributions 		}
2161*aca3beaaSApple OSS Distributions 
2162*aca3beaaSApple OSS Distributions 		ifp = inp_lookup_mcast_ifp(inp, gsa, mreqs.imr_interface);
2163*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(mreqs.imr_interface,
2164*aca3beaaSApple OSS Distributions 		    ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__,
2165*aca3beaaSApple OSS Distributions 		    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
2166*aca3beaaSApple OSS Distributions 		break;
2167*aca3beaaSApple OSS Distributions 	}
2168*aca3beaaSApple OSS Distributions 
2169*aca3beaaSApple OSS Distributions 	case MCAST_JOIN_GROUP:
2170*aca3beaaSApple OSS Distributions 	case MCAST_JOIN_SOURCE_GROUP:
2171*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == MCAST_JOIN_GROUP) {
2172*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &gsr,
2173*aca3beaaSApple OSS Distributions 			    sizeof(struct group_req),
2174*aca3beaaSApple OSS Distributions 			    sizeof(struct group_req));
2175*aca3beaaSApple OSS Distributions 		} else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
2176*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &gsr,
2177*aca3beaaSApple OSS Distributions 			    sizeof(struct group_source_req),
2178*aca3beaaSApple OSS Distributions 			    sizeof(struct group_source_req));
2179*aca3beaaSApple OSS Distributions 		}
2180*aca3beaaSApple OSS Distributions 		if (error) {
2181*aca3beaaSApple OSS Distributions 			return error;
2182*aca3beaaSApple OSS Distributions 		}
2183*aca3beaaSApple OSS Distributions 
2184*aca3beaaSApple OSS Distributions 		if (gsa->sin_family != AF_INET ||
2185*aca3beaaSApple OSS Distributions 		    gsa->sin_len != sizeof(struct sockaddr_in)) {
2186*aca3beaaSApple OSS Distributions 			return EINVAL;
2187*aca3beaaSApple OSS Distributions 		}
2188*aca3beaaSApple OSS Distributions 
2189*aca3beaaSApple OSS Distributions 		/*
2190*aca3beaaSApple OSS Distributions 		 * Overwrite the port field if present, as the sockaddr
2191*aca3beaaSApple OSS Distributions 		 * being copied in may be matched with a binary comparison.
2192*aca3beaaSApple OSS Distributions 		 */
2193*aca3beaaSApple OSS Distributions 		gsa->sin_port = 0;
2194*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
2195*aca3beaaSApple OSS Distributions 			if (ssa->sin_family != AF_INET ||
2196*aca3beaaSApple OSS Distributions 			    ssa->sin_len != sizeof(struct sockaddr_in)) {
2197*aca3beaaSApple OSS Distributions 				return EINVAL;
2198*aca3beaaSApple OSS Distributions 			}
2199*aca3beaaSApple OSS Distributions 			ssa->sin_port = 0;
2200*aca3beaaSApple OSS Distributions 		}
2201*aca3beaaSApple OSS Distributions 
2202*aca3beaaSApple OSS Distributions 		if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2203*aca3beaaSApple OSS Distributions 			return EINVAL;
2204*aca3beaaSApple OSS Distributions 		}
2205*aca3beaaSApple OSS Distributions 
2206*aca3beaaSApple OSS Distributions 		ifnet_head_lock_shared();
2207*aca3beaaSApple OSS Distributions 		if (gsr.gsr_interface == 0 ||
2208*aca3beaaSApple OSS Distributions 		    (u_int)if_index < gsr.gsr_interface) {
2209*aca3beaaSApple OSS Distributions 			ifnet_head_done();
2210*aca3beaaSApple OSS Distributions 			return EADDRNOTAVAIL;
2211*aca3beaaSApple OSS Distributions 		}
2212*aca3beaaSApple OSS Distributions 		ifp = ifindex2ifnet[gsr.gsr_interface];
2213*aca3beaaSApple OSS Distributions 		ifnet_head_done();
2214*aca3beaaSApple OSS Distributions 		if (ifp == NULL) {
2215*aca3beaaSApple OSS Distributions 			return EADDRNOTAVAIL;
2216*aca3beaaSApple OSS Distributions 		}
2217*aca3beaaSApple OSS Distributions 		break;
2218*aca3beaaSApple OSS Distributions 
2219*aca3beaaSApple OSS Distributions 	default:
2220*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: unknown sopt_name %d\n",
2221*aca3beaaSApple OSS Distributions 		    __func__, sopt->sopt_name));
2222*aca3beaaSApple OSS Distributions 		return EOPNOTSUPP;
2223*aca3beaaSApple OSS Distributions 	}
2224*aca3beaaSApple OSS Distributions 
2225*aca3beaaSApple OSS Distributions 	if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
2226*aca3beaaSApple OSS Distributions 		return EADDRNOTAVAIL;
2227*aca3beaaSApple OSS Distributions 	}
2228*aca3beaaSApple OSS Distributions 
2229*aca3beaaSApple OSS Distributions 	INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_total);
2230*aca3beaaSApple OSS Distributions 	/*
2231*aca3beaaSApple OSS Distributions 	 * TBD: revisit the criteria for non-OS initiated joins
2232*aca3beaaSApple OSS Distributions 	 */
2233*aca3beaaSApple OSS Distributions 	if (inp->inp_lport == htons(5353)) {
2234*aca3beaaSApple OSS Distributions 		INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_os_total);
2235*aca3beaaSApple OSS Distributions 	}
2236*aca3beaaSApple OSS Distributions 
2237*aca3beaaSApple OSS Distributions 	imo = inp_findmoptions(inp);
2238*aca3beaaSApple OSS Distributions 	if (imo == NULL) {
2239*aca3beaaSApple OSS Distributions 		return ENOMEM;
2240*aca3beaaSApple OSS Distributions 	}
2241*aca3beaaSApple OSS Distributions 
2242*aca3beaaSApple OSS Distributions 	IMO_LOCK(imo);
2243*aca3beaaSApple OSS Distributions 	idx = imo_match_group(imo, ifp, gsa);
2244*aca3beaaSApple OSS Distributions 	if (idx == (size_t)-1) {
2245*aca3beaaSApple OSS Distributions 		is_new = 1;
2246*aca3beaaSApple OSS Distributions 	} else {
2247*aca3beaaSApple OSS Distributions 		inm = imo->imo_membership[idx];
2248*aca3beaaSApple OSS Distributions 		imf = &imo->imo_mfilters[idx];
2249*aca3beaaSApple OSS Distributions 		if (ssa->sin_family != AF_UNSPEC) {
2250*aca3beaaSApple OSS Distributions 			/*
2251*aca3beaaSApple OSS Distributions 			 * MCAST_JOIN_SOURCE_GROUP on an exclusive membership
2252*aca3beaaSApple OSS Distributions 			 * is an error. On an existing inclusive membership,
2253*aca3beaaSApple OSS Distributions 			 * it just adds the source to the filter list.
2254*aca3beaaSApple OSS Distributions 			 */
2255*aca3beaaSApple OSS Distributions 			if (imf->imf_st[1] != MCAST_INCLUDE) {
2256*aca3beaaSApple OSS Distributions 				error = EINVAL;
2257*aca3beaaSApple OSS Distributions 				goto out_imo_locked;
2258*aca3beaaSApple OSS Distributions 			}
2259*aca3beaaSApple OSS Distributions 			/*
2260*aca3beaaSApple OSS Distributions 			 * Throw out duplicates.
2261*aca3beaaSApple OSS Distributions 			 *
2262*aca3beaaSApple OSS Distributions 			 * XXX FIXME: This makes a naive assumption that
2263*aca3beaaSApple OSS Distributions 			 * even if entries exist for *ssa in this imf,
2264*aca3beaaSApple OSS Distributions 			 * they will be rejected as dupes, even if they
2265*aca3beaaSApple OSS Distributions 			 * are not valid in the current mode (in-mode).
2266*aca3beaaSApple OSS Distributions 			 *
2267*aca3beaaSApple OSS Distributions 			 * in_msource is transactioned just as for anything
2268*aca3beaaSApple OSS Distributions 			 * else in SSM -- but note naive use of inm_graft()
2269*aca3beaaSApple OSS Distributions 			 * below for allocating new filter entries.
2270*aca3beaaSApple OSS Distributions 			 *
2271*aca3beaaSApple OSS Distributions 			 * This is only an issue if someone mixes the
2272*aca3beaaSApple OSS Distributions 			 * full-state SSM API with the delta-based API,
2273*aca3beaaSApple OSS Distributions 			 * which is discouraged in the relevant RFCs.
2274*aca3beaaSApple OSS Distributions 			 */
2275*aca3beaaSApple OSS Distributions 			lims = imo_match_source(imo, idx, ssa);
2276*aca3beaaSApple OSS Distributions 			if (lims != NULL /*&&
2277*aca3beaaSApple OSS Distributions 			                  *  lims->imsl_st[1] == MCAST_INCLUDE*/) {
2278*aca3beaaSApple OSS Distributions 				error = EADDRNOTAVAIL;
2279*aca3beaaSApple OSS Distributions 				goto out_imo_locked;
2280*aca3beaaSApple OSS Distributions 			}
2281*aca3beaaSApple OSS Distributions 		} else {
2282*aca3beaaSApple OSS Distributions 			/*
2283*aca3beaaSApple OSS Distributions 			 * MCAST_JOIN_GROUP on an existing exclusive
2284*aca3beaaSApple OSS Distributions 			 * membership is an error; return EADDRINUSE
2285*aca3beaaSApple OSS Distributions 			 * to preserve 4.4BSD API idempotence, and
2286*aca3beaaSApple OSS Distributions 			 * avoid tedious detour to code below.
2287*aca3beaaSApple OSS Distributions 			 * NOTE: This is bending RFC 3678 a bit.
2288*aca3beaaSApple OSS Distributions 			 *
2289*aca3beaaSApple OSS Distributions 			 * On an existing inclusive membership, this is also
2290*aca3beaaSApple OSS Distributions 			 * an error; if you want to change filter mode,
2291*aca3beaaSApple OSS Distributions 			 * you must use the userland API setsourcefilter().
2292*aca3beaaSApple OSS Distributions 			 * XXX We don't reject this for imf in UNDEFINED
2293*aca3beaaSApple OSS Distributions 			 * state at t1, because allocation of a filter
2294*aca3beaaSApple OSS Distributions 			 * is atomic with allocation of a membership.
2295*aca3beaaSApple OSS Distributions 			 */
2296*aca3beaaSApple OSS Distributions 			error = EINVAL;
2297*aca3beaaSApple OSS Distributions 			/* See comments above for EADDRINUSE */
2298*aca3beaaSApple OSS Distributions 			if (imf->imf_st[1] == MCAST_EXCLUDE) {
2299*aca3beaaSApple OSS Distributions 				error = EADDRINUSE;
2300*aca3beaaSApple OSS Distributions 			}
2301*aca3beaaSApple OSS Distributions 			goto out_imo_locked;
2302*aca3beaaSApple OSS Distributions 		}
2303*aca3beaaSApple OSS Distributions 	}
2304*aca3beaaSApple OSS Distributions 
2305*aca3beaaSApple OSS Distributions 	/*
2306*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at socket layer.
2307*aca3beaaSApple OSS Distributions 	 */
2308*aca3beaaSApple OSS Distributions 
2309*aca3beaaSApple OSS Distributions 	if (is_new) {
2310*aca3beaaSApple OSS Distributions 		if (imo->imo_num_memberships == imo->imo_max_memberships) {
2311*aca3beaaSApple OSS Distributions 			error = imo_grow(imo, 0);
2312*aca3beaaSApple OSS Distributions 			if (error) {
2313*aca3beaaSApple OSS Distributions 				goto out_imo_locked;
2314*aca3beaaSApple OSS Distributions 			}
2315*aca3beaaSApple OSS Distributions 		}
2316*aca3beaaSApple OSS Distributions 		/*
2317*aca3beaaSApple OSS Distributions 		 * Allocate the new slot upfront so we can deal with
2318*aca3beaaSApple OSS Distributions 		 * grafting the new source filter in same code path
2319*aca3beaaSApple OSS Distributions 		 * as for join-source on existing membership.
2320*aca3beaaSApple OSS Distributions 		 */
2321*aca3beaaSApple OSS Distributions 		idx = imo->imo_num_memberships;
2322*aca3beaaSApple OSS Distributions 		imo->imo_membership[idx] = NULL;
2323*aca3beaaSApple OSS Distributions 		imo->imo_num_memberships++;
2324*aca3beaaSApple OSS Distributions 		VERIFY(imo->imo_mfilters != NULL);
2325*aca3beaaSApple OSS Distributions 		imf = &imo->imo_mfilters[idx];
2326*aca3beaaSApple OSS Distributions 		VERIFY(RB_EMPTY(&imf->imf_sources));
2327*aca3beaaSApple OSS Distributions 	}
2328*aca3beaaSApple OSS Distributions 
2329*aca3beaaSApple OSS Distributions 	/*
2330*aca3beaaSApple OSS Distributions 	 * Graft new source into filter list for this inpcb's
2331*aca3beaaSApple OSS Distributions 	 * membership of the group. The in_multi may not have
2332*aca3beaaSApple OSS Distributions 	 * been allocated yet if this is a new membership, however,
2333*aca3beaaSApple OSS Distributions 	 * the in_mfilter slot will be allocated and must be initialized.
2334*aca3beaaSApple OSS Distributions 	 */
2335*aca3beaaSApple OSS Distributions 	if (ssa->sin_family != AF_UNSPEC) {
2336*aca3beaaSApple OSS Distributions 		/* Membership starts in IN mode */
2337*aca3beaaSApple OSS Distributions 		if (is_new) {
2338*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: new join w/source\n", __func__));
2339*aca3beaaSApple OSS Distributions 			imf_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE);
2340*aca3beaaSApple OSS Distributions 		} else {
2341*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: %s source\n", __func__, "allow"));
2342*aca3beaaSApple OSS Distributions 		}
2343*aca3beaaSApple OSS Distributions 		lims = imf_graft(imf, MCAST_INCLUDE, ssa);
2344*aca3beaaSApple OSS Distributions 		if (lims == NULL) {
2345*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: merge imf state failed\n",
2346*aca3beaaSApple OSS Distributions 			    __func__));
2347*aca3beaaSApple OSS Distributions 			error = ENOMEM;
2348*aca3beaaSApple OSS Distributions 			goto out_imo_free;
2349*aca3beaaSApple OSS Distributions 		}
2350*aca3beaaSApple OSS Distributions 	} else {
2351*aca3beaaSApple OSS Distributions 		/* No address specified; Membership starts in EX mode */
2352*aca3beaaSApple OSS Distributions 		if (is_new) {
2353*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: new join w/o source\n", __func__));
2354*aca3beaaSApple OSS Distributions 			imf_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE);
2355*aca3beaaSApple OSS Distributions 		}
2356*aca3beaaSApple OSS Distributions 	}
2357*aca3beaaSApple OSS Distributions 
2358*aca3beaaSApple OSS Distributions 	/*
2359*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at IGMP layer.
2360*aca3beaaSApple OSS Distributions 	 */
2361*aca3beaaSApple OSS Distributions 	if (is_new) {
2362*aca3beaaSApple OSS Distributions 		VERIFY(inm == NULL);
2363*aca3beaaSApple OSS Distributions 		error = in_joingroup(ifp, &gsa->sin_addr, imf, &inm);
2364*aca3beaaSApple OSS Distributions 
2365*aca3beaaSApple OSS Distributions 		VERIFY(inm != NULL || error != 0);
2366*aca3beaaSApple OSS Distributions 		if (error) {
2367*aca3beaaSApple OSS Distributions 			goto out_imo_free;
2368*aca3beaaSApple OSS Distributions 		}
2369*aca3beaaSApple OSS Distributions 		imo->imo_membership[idx] = inm; /* from in_joingroup() */
2370*aca3beaaSApple OSS Distributions 	} else {
2371*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: merge inm state\n", __func__));
2372*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
2373*aca3beaaSApple OSS Distributions 		error = inm_merge(inm, imf);
2374*aca3beaaSApple OSS Distributions 		if (error) {
2375*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: failed to merge inm state\n",
2376*aca3beaaSApple OSS Distributions 			    __func__));
2377*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
2378*aca3beaaSApple OSS Distributions 			goto out_imf_rollback;
2379*aca3beaaSApple OSS Distributions 		}
2380*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
2381*aca3beaaSApple OSS Distributions 		error = igmp_change_state(inm, &itp);
2382*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
2383*aca3beaaSApple OSS Distributions 		if (error) {
2384*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: failed igmp downcall\n",
2385*aca3beaaSApple OSS Distributions 			    __func__));
2386*aca3beaaSApple OSS Distributions 			goto out_imf_rollback;
2387*aca3beaaSApple OSS Distributions 		}
2388*aca3beaaSApple OSS Distributions 	}
2389*aca3beaaSApple OSS Distributions 
2390*aca3beaaSApple OSS Distributions out_imf_rollback:
2391*aca3beaaSApple OSS Distributions 	if (error) {
2392*aca3beaaSApple OSS Distributions 		imf_rollback(imf);
2393*aca3beaaSApple OSS Distributions 		if (is_new) {
2394*aca3beaaSApple OSS Distributions 			imf_purge(imf);
2395*aca3beaaSApple OSS Distributions 		} else {
2396*aca3beaaSApple OSS Distributions 			imf_reap(imf);
2397*aca3beaaSApple OSS Distributions 		}
2398*aca3beaaSApple OSS Distributions 	} else {
2399*aca3beaaSApple OSS Distributions 		imf_commit(imf);
2400*aca3beaaSApple OSS Distributions 	}
2401*aca3beaaSApple OSS Distributions 
2402*aca3beaaSApple OSS Distributions out_imo_free:
2403*aca3beaaSApple OSS Distributions 	if (error && is_new) {
2404*aca3beaaSApple OSS Distributions 		VERIFY(inm == NULL);
2405*aca3beaaSApple OSS Distributions 		imo->imo_membership[idx] = NULL;
2406*aca3beaaSApple OSS Distributions 		--imo->imo_num_memberships;
2407*aca3beaaSApple OSS Distributions 	}
2408*aca3beaaSApple OSS Distributions 
2409*aca3beaaSApple OSS Distributions out_imo_locked:
2410*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(imo);
2411*aca3beaaSApple OSS Distributions 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2412*aca3beaaSApple OSS Distributions 
2413*aca3beaaSApple OSS Distributions 	/* schedule timer now that we've dropped the lock(s) */
2414*aca3beaaSApple OSS Distributions 	igmp_set_fast_timeout(&itp);
2415*aca3beaaSApple OSS Distributions 
2416*aca3beaaSApple OSS Distributions 	return error;
2417*aca3beaaSApple OSS Distributions }
2418*aca3beaaSApple OSS Distributions 
2419*aca3beaaSApple OSS Distributions /*
2420*aca3beaaSApple OSS Distributions  * Leave an IPv4 multicast group on an inpcb, possibly with a source.
2421*aca3beaaSApple OSS Distributions  *
2422*aca3beaaSApple OSS Distributions  * NB: sopt->sopt_val might point to the kernel address space. Refer to the
2423*aca3beaaSApple OSS Distributions  * block comment on top of inp_join_group() for more information.
2424*aca3beaaSApple OSS Distributions  */
2425*aca3beaaSApple OSS Distributions int
inp_leave_group(struct inpcb * inp,struct sockopt * sopt)2426*aca3beaaSApple OSS Distributions inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
2427*aca3beaaSApple OSS Distributions {
2428*aca3beaaSApple OSS Distributions 	struct group_source_req          gsr;
2429*aca3beaaSApple OSS Distributions 	struct ip_mreq_source            mreqs;
2430*aca3beaaSApple OSS Distributions 	struct sockaddr_in              *gsa, *ssa;
2431*aca3beaaSApple OSS Distributions 	struct ifnet                    *ifp;
2432*aca3beaaSApple OSS Distributions 	struct in_mfilter               *imf;
2433*aca3beaaSApple OSS Distributions 	struct ip_moptions              *imo;
2434*aca3beaaSApple OSS Distributions 	struct in_msource               *ims;
2435*aca3beaaSApple OSS Distributions 	struct in_multi                 *inm = NULL;
2436*aca3beaaSApple OSS Distributions 	size_t                           idx;
2437*aca3beaaSApple OSS Distributions 	int                              error, is_final;
2438*aca3beaaSApple OSS Distributions 	unsigned int                     ifindex = 0;
2439*aca3beaaSApple OSS Distributions 	struct igmp_tparams              itp;
2440*aca3beaaSApple OSS Distributions 
2441*aca3beaaSApple OSS Distributions 	bzero(&itp, sizeof(itp));
2442*aca3beaaSApple OSS Distributions 	ifp = NULL;
2443*aca3beaaSApple OSS Distributions 	error = 0;
2444*aca3beaaSApple OSS Distributions 	is_final = 1;
2445*aca3beaaSApple OSS Distributions 
2446*aca3beaaSApple OSS Distributions 	memset(&gsr, 0, sizeof(struct group_source_req));
2447*aca3beaaSApple OSS Distributions 	gsa = (struct sockaddr_in *)&gsr.gsr_group;
2448*aca3beaaSApple OSS Distributions 	ssa = (struct sockaddr_in *)&gsr.gsr_source;
2449*aca3beaaSApple OSS Distributions 
2450*aca3beaaSApple OSS Distributions 	switch (sopt->sopt_name) {
2451*aca3beaaSApple OSS Distributions 	case IP_DROP_MEMBERSHIP:
2452*aca3beaaSApple OSS Distributions 	case IP_DROP_SOURCE_MEMBERSHIP:
2453*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == IP_DROP_MEMBERSHIP) {
2454*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &mreqs,
2455*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq),
2456*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq));
2457*aca3beaaSApple OSS Distributions 			/*
2458*aca3beaaSApple OSS Distributions 			 * Swap interface and sourceaddr arguments,
2459*aca3beaaSApple OSS Distributions 			 * as ip_mreq and ip_mreq_source are laid
2460*aca3beaaSApple OSS Distributions 			 * out differently.
2461*aca3beaaSApple OSS Distributions 			 */
2462*aca3beaaSApple OSS Distributions 			mreqs.imr_interface = mreqs.imr_sourceaddr;
2463*aca3beaaSApple OSS Distributions 			mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
2464*aca3beaaSApple OSS Distributions 		} else if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
2465*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &mreqs,
2466*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq_source),
2467*aca3beaaSApple OSS Distributions 			    sizeof(struct ip_mreq_source));
2468*aca3beaaSApple OSS Distributions 		}
2469*aca3beaaSApple OSS Distributions 		if (error) {
2470*aca3beaaSApple OSS Distributions 			return error;
2471*aca3beaaSApple OSS Distributions 		}
2472*aca3beaaSApple OSS Distributions 
2473*aca3beaaSApple OSS Distributions 		gsa->sin_family = AF_INET;
2474*aca3beaaSApple OSS Distributions 		gsa->sin_len = sizeof(struct sockaddr_in);
2475*aca3beaaSApple OSS Distributions 		gsa->sin_addr = mreqs.imr_multiaddr;
2476*aca3beaaSApple OSS Distributions 
2477*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
2478*aca3beaaSApple OSS Distributions 			ssa->sin_family = AF_INET;
2479*aca3beaaSApple OSS Distributions 			ssa->sin_len = sizeof(struct sockaddr_in);
2480*aca3beaaSApple OSS Distributions 			ssa->sin_addr = mreqs.imr_sourceaddr;
2481*aca3beaaSApple OSS Distributions 		}
2482*aca3beaaSApple OSS Distributions 		/*
2483*aca3beaaSApple OSS Distributions 		 * Attempt to look up hinted ifp from interface address.
2484*aca3beaaSApple OSS Distributions 		 * Fallthrough with null ifp iff lookup fails, to
2485*aca3beaaSApple OSS Distributions 		 * preserve 4.4BSD mcast API idempotence.
2486*aca3beaaSApple OSS Distributions 		 * XXX NOTE WELL: The RFC 3678 API is preferred because
2487*aca3beaaSApple OSS Distributions 		 * using an IPv4 address as a key is racy.
2488*aca3beaaSApple OSS Distributions 		 */
2489*aca3beaaSApple OSS Distributions 		if (!in_nullhost(mreqs.imr_interface)) {
2490*aca3beaaSApple OSS Distributions 			ifp = ip_multicast_if(&mreqs.imr_interface, &ifindex);
2491*aca3beaaSApple OSS Distributions 		}
2492*aca3beaaSApple OSS Distributions 
2493*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(mreqs.imr_interface,
2494*aca3beaaSApple OSS Distributions 		    ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__,
2495*aca3beaaSApple OSS Distributions 		    _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
2496*aca3beaaSApple OSS Distributions 
2497*aca3beaaSApple OSS Distributions 		break;
2498*aca3beaaSApple OSS Distributions 
2499*aca3beaaSApple OSS Distributions 	case MCAST_LEAVE_GROUP:
2500*aca3beaaSApple OSS Distributions 	case MCAST_LEAVE_SOURCE_GROUP:
2501*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == MCAST_LEAVE_GROUP) {
2502*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &gsr,
2503*aca3beaaSApple OSS Distributions 			    sizeof(struct group_req),
2504*aca3beaaSApple OSS Distributions 			    sizeof(struct group_req));
2505*aca3beaaSApple OSS Distributions 		} else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2506*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &gsr,
2507*aca3beaaSApple OSS Distributions 			    sizeof(struct group_source_req),
2508*aca3beaaSApple OSS Distributions 			    sizeof(struct group_source_req));
2509*aca3beaaSApple OSS Distributions 		}
2510*aca3beaaSApple OSS Distributions 		if (error) {
2511*aca3beaaSApple OSS Distributions 			return error;
2512*aca3beaaSApple OSS Distributions 		}
2513*aca3beaaSApple OSS Distributions 
2514*aca3beaaSApple OSS Distributions 		if (gsa->sin_family != AF_INET ||
2515*aca3beaaSApple OSS Distributions 		    gsa->sin_len != sizeof(struct sockaddr_in)) {
2516*aca3beaaSApple OSS Distributions 			return EINVAL;
2517*aca3beaaSApple OSS Distributions 		}
2518*aca3beaaSApple OSS Distributions 
2519*aca3beaaSApple OSS Distributions 		if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2520*aca3beaaSApple OSS Distributions 			if (ssa->sin_family != AF_INET ||
2521*aca3beaaSApple OSS Distributions 			    ssa->sin_len != sizeof(struct sockaddr_in)) {
2522*aca3beaaSApple OSS Distributions 				return EINVAL;
2523*aca3beaaSApple OSS Distributions 			}
2524*aca3beaaSApple OSS Distributions 		}
2525*aca3beaaSApple OSS Distributions 
2526*aca3beaaSApple OSS Distributions 		ifnet_head_lock_shared();
2527*aca3beaaSApple OSS Distributions 		if (gsr.gsr_interface == 0 ||
2528*aca3beaaSApple OSS Distributions 		    (u_int)if_index < gsr.gsr_interface) {
2529*aca3beaaSApple OSS Distributions 			ifnet_head_done();
2530*aca3beaaSApple OSS Distributions 			return EADDRNOTAVAIL;
2531*aca3beaaSApple OSS Distributions 		}
2532*aca3beaaSApple OSS Distributions 
2533*aca3beaaSApple OSS Distributions 		ifp = ifindex2ifnet[gsr.gsr_interface];
2534*aca3beaaSApple OSS Distributions 		ifnet_head_done();
2535*aca3beaaSApple OSS Distributions 		if (ifp == NULL) {
2536*aca3beaaSApple OSS Distributions 			return EADDRNOTAVAIL;
2537*aca3beaaSApple OSS Distributions 		}
2538*aca3beaaSApple OSS Distributions 		break;
2539*aca3beaaSApple OSS Distributions 
2540*aca3beaaSApple OSS Distributions 	default:
2541*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: unknown sopt_name %d\n",
2542*aca3beaaSApple OSS Distributions 		    __func__, sopt->sopt_name));
2543*aca3beaaSApple OSS Distributions 		return EOPNOTSUPP;
2544*aca3beaaSApple OSS Distributions 	}
2545*aca3beaaSApple OSS Distributions 
2546*aca3beaaSApple OSS Distributions 	if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2547*aca3beaaSApple OSS Distributions 		return EINVAL;
2548*aca3beaaSApple OSS Distributions 	}
2549*aca3beaaSApple OSS Distributions 
2550*aca3beaaSApple OSS Distributions 	/*
2551*aca3beaaSApple OSS Distributions 	 * Find the membership in the membership array.
2552*aca3beaaSApple OSS Distributions 	 */
2553*aca3beaaSApple OSS Distributions 	imo = inp_findmoptions(inp);
2554*aca3beaaSApple OSS Distributions 	if (imo == NULL) {
2555*aca3beaaSApple OSS Distributions 		return ENOMEM;
2556*aca3beaaSApple OSS Distributions 	}
2557*aca3beaaSApple OSS Distributions 
2558*aca3beaaSApple OSS Distributions 	IMO_LOCK(imo);
2559*aca3beaaSApple OSS Distributions 	idx = imo_match_group(imo, ifp, gsa);
2560*aca3beaaSApple OSS Distributions 	if (idx == (size_t)-1) {
2561*aca3beaaSApple OSS Distributions 		error = EADDRNOTAVAIL;
2562*aca3beaaSApple OSS Distributions 		goto out_locked;
2563*aca3beaaSApple OSS Distributions 	}
2564*aca3beaaSApple OSS Distributions 	inm = imo->imo_membership[idx];
2565*aca3beaaSApple OSS Distributions 	if (inm == NULL) {
2566*aca3beaaSApple OSS Distributions 		error = EINVAL;
2567*aca3beaaSApple OSS Distributions 		goto out_locked;
2568*aca3beaaSApple OSS Distributions 	}
2569*aca3beaaSApple OSS Distributions 	imf = &imo->imo_mfilters[idx];
2570*aca3beaaSApple OSS Distributions 
2571*aca3beaaSApple OSS Distributions 	if (ssa->sin_family != AF_UNSPEC) {
2572*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: opt=%d is_final=0\n", __func__,
2573*aca3beaaSApple OSS Distributions 		    sopt->sopt_name));
2574*aca3beaaSApple OSS Distributions 		is_final = 0;
2575*aca3beaaSApple OSS Distributions 	}
2576*aca3beaaSApple OSS Distributions 
2577*aca3beaaSApple OSS Distributions 	/*
2578*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at socket layer.
2579*aca3beaaSApple OSS Distributions 	 */
2580*aca3beaaSApple OSS Distributions 
2581*aca3beaaSApple OSS Distributions 	/*
2582*aca3beaaSApple OSS Distributions 	 * If we were instructed only to leave a given source, do so.
2583*aca3beaaSApple OSS Distributions 	 * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
2584*aca3beaaSApple OSS Distributions 	 */
2585*aca3beaaSApple OSS Distributions 	if (is_final) {
2586*aca3beaaSApple OSS Distributions 		imf_leave(imf);
2587*aca3beaaSApple OSS Distributions 	} else {
2588*aca3beaaSApple OSS Distributions 		if (imf->imf_st[0] == MCAST_EXCLUDE) {
2589*aca3beaaSApple OSS Distributions 			error = EADDRNOTAVAIL;
2590*aca3beaaSApple OSS Distributions 			goto out_locked;
2591*aca3beaaSApple OSS Distributions 		}
2592*aca3beaaSApple OSS Distributions 		ims = imo_match_source(imo, idx, ssa);
2593*aca3beaaSApple OSS Distributions 		if (ims == NULL) {
2594*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(ssa->sin_addr,
2595*aca3beaaSApple OSS Distributions 			    ("%s: source %s %spresent\n", __func__,
2596*aca3beaaSApple OSS Distributions 			    _igmp_inet_buf, "not "));
2597*aca3beaaSApple OSS Distributions 			error = EADDRNOTAVAIL;
2598*aca3beaaSApple OSS Distributions 			goto out_locked;
2599*aca3beaaSApple OSS Distributions 		}
2600*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: %s source\n", __func__, "block"));
2601*aca3beaaSApple OSS Distributions 		error = imf_prune(imf, ssa);
2602*aca3beaaSApple OSS Distributions 		if (error) {
2603*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: merge imf state failed\n",
2604*aca3beaaSApple OSS Distributions 			    __func__));
2605*aca3beaaSApple OSS Distributions 			goto out_locked;
2606*aca3beaaSApple OSS Distributions 		}
2607*aca3beaaSApple OSS Distributions 	}
2608*aca3beaaSApple OSS Distributions 
2609*aca3beaaSApple OSS Distributions 	/*
2610*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at IGMP layer.
2611*aca3beaaSApple OSS Distributions 	 */
2612*aca3beaaSApple OSS Distributions 	if (is_final) {
2613*aca3beaaSApple OSS Distributions 		/*
2614*aca3beaaSApple OSS Distributions 		 * Give up the multicast address record to which
2615*aca3beaaSApple OSS Distributions 		 * the membership points.  Reference held in imo
2616*aca3beaaSApple OSS Distributions 		 * will be released below.
2617*aca3beaaSApple OSS Distributions 		 */
2618*aca3beaaSApple OSS Distributions 		(void) in_leavegroup(inm, imf);
2619*aca3beaaSApple OSS Distributions 	} else {
2620*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: merge inm state\n", __func__));
2621*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
2622*aca3beaaSApple OSS Distributions 		error = inm_merge(inm, imf);
2623*aca3beaaSApple OSS Distributions 		if (error) {
2624*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: failed to merge inm state\n",
2625*aca3beaaSApple OSS Distributions 			    __func__));
2626*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
2627*aca3beaaSApple OSS Distributions 			goto out_imf_rollback;
2628*aca3beaaSApple OSS Distributions 		}
2629*aca3beaaSApple OSS Distributions 
2630*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
2631*aca3beaaSApple OSS Distributions 		error = igmp_change_state(inm, &itp);
2632*aca3beaaSApple OSS Distributions 		if (error) {
2633*aca3beaaSApple OSS Distributions 			IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
2634*aca3beaaSApple OSS Distributions 		}
2635*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
2636*aca3beaaSApple OSS Distributions 	}
2637*aca3beaaSApple OSS Distributions 
2638*aca3beaaSApple OSS Distributions out_imf_rollback:
2639*aca3beaaSApple OSS Distributions 	if (error) {
2640*aca3beaaSApple OSS Distributions 		imf_rollback(imf);
2641*aca3beaaSApple OSS Distributions 	} else {
2642*aca3beaaSApple OSS Distributions 		imf_commit(imf);
2643*aca3beaaSApple OSS Distributions 	}
2644*aca3beaaSApple OSS Distributions 
2645*aca3beaaSApple OSS Distributions 	imf_reap(imf);
2646*aca3beaaSApple OSS Distributions 
2647*aca3beaaSApple OSS Distributions 	if (is_final) {
2648*aca3beaaSApple OSS Distributions 		/* Remove the gap in the membership array and filter array. */
2649*aca3beaaSApple OSS Distributions 		VERIFY(inm == imo->imo_membership[idx]);
2650*aca3beaaSApple OSS Distributions 
2651*aca3beaaSApple OSS Distributions 		INM_REMREF(inm);
2652*aca3beaaSApple OSS Distributions 
2653*aca3beaaSApple OSS Distributions 		for (++idx; idx < imo->imo_num_memberships; ++idx) {
2654*aca3beaaSApple OSS Distributions 			imo->imo_membership[idx - 1] = imo->imo_membership[idx];
2655*aca3beaaSApple OSS Distributions 			imo->imo_mfilters[idx - 1] = imo->imo_mfilters[idx];
2656*aca3beaaSApple OSS Distributions 		}
2657*aca3beaaSApple OSS Distributions 		imo->imo_num_memberships--;
2658*aca3beaaSApple OSS Distributions 
2659*aca3beaaSApple OSS Distributions 		/* Re-initialize the now unused tail of the list */
2660*aca3beaaSApple OSS Distributions 		imo->imo_membership[imo->imo_num_memberships] = NULL;
2661*aca3beaaSApple OSS Distributions 		imf_init(&imo->imo_mfilters[imo->imo_num_memberships], MCAST_UNDEFINED, MCAST_EXCLUDE);
2662*aca3beaaSApple OSS Distributions 	}
2663*aca3beaaSApple OSS Distributions 
2664*aca3beaaSApple OSS Distributions out_locked:
2665*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(imo);
2666*aca3beaaSApple OSS Distributions 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2667*aca3beaaSApple OSS Distributions 
2668*aca3beaaSApple OSS Distributions 	/* schedule timer now that we've dropped the lock(s) */
2669*aca3beaaSApple OSS Distributions 	igmp_set_fast_timeout(&itp);
2670*aca3beaaSApple OSS Distributions 
2671*aca3beaaSApple OSS Distributions 	return error;
2672*aca3beaaSApple OSS Distributions }
2673*aca3beaaSApple OSS Distributions 
2674*aca3beaaSApple OSS Distributions /*
2675*aca3beaaSApple OSS Distributions  * Select the interface for transmitting IPv4 multicast datagrams.
2676*aca3beaaSApple OSS Distributions  *
2677*aca3beaaSApple OSS Distributions  * Either an instance of struct in_addr or an instance of struct ip_mreqn
2678*aca3beaaSApple OSS Distributions  * may be passed to this socket option. An address of INADDR_ANY or an
2679*aca3beaaSApple OSS Distributions  * interface index of 0 is used to remove a previous selection.
2680*aca3beaaSApple OSS Distributions  * When no interface is selected, one is chosen for every send.
2681*aca3beaaSApple OSS Distributions  */
2682*aca3beaaSApple OSS Distributions static int
inp_set_multicast_if(struct inpcb * inp,struct sockopt * sopt)2683*aca3beaaSApple OSS Distributions inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
2684*aca3beaaSApple OSS Distributions {
2685*aca3beaaSApple OSS Distributions 	struct in_addr           addr;
2686*aca3beaaSApple OSS Distributions 	struct ip_mreqn          mreqn;
2687*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
2688*aca3beaaSApple OSS Distributions 	struct ip_moptions      *imo;
2689*aca3beaaSApple OSS Distributions 	int                      error = 0;
2690*aca3beaaSApple OSS Distributions 	unsigned int             ifindex = 0;
2691*aca3beaaSApple OSS Distributions 
2692*aca3beaaSApple OSS Distributions 	bzero(&addr, sizeof(addr));
2693*aca3beaaSApple OSS Distributions 	if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
2694*aca3beaaSApple OSS Distributions 		/*
2695*aca3beaaSApple OSS Distributions 		 * An interface index was specified using the
2696*aca3beaaSApple OSS Distributions 		 * Linux-derived ip_mreqn structure.
2697*aca3beaaSApple OSS Distributions 		 */
2698*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn),
2699*aca3beaaSApple OSS Distributions 		    sizeof(struct ip_mreqn));
2700*aca3beaaSApple OSS Distributions 		if (error) {
2701*aca3beaaSApple OSS Distributions 			return error;
2702*aca3beaaSApple OSS Distributions 		}
2703*aca3beaaSApple OSS Distributions 
2704*aca3beaaSApple OSS Distributions 		ifnet_head_lock_shared();
2705*aca3beaaSApple OSS Distributions 		if (mreqn.imr_ifindex < 0 || if_index < mreqn.imr_ifindex) {
2706*aca3beaaSApple OSS Distributions 			ifnet_head_done();
2707*aca3beaaSApple OSS Distributions 			return EINVAL;
2708*aca3beaaSApple OSS Distributions 		}
2709*aca3beaaSApple OSS Distributions 
2710*aca3beaaSApple OSS Distributions 		if (mreqn.imr_ifindex == 0) {
2711*aca3beaaSApple OSS Distributions 			ifp = NULL;
2712*aca3beaaSApple OSS Distributions 		} else {
2713*aca3beaaSApple OSS Distributions 			ifp = ifindex2ifnet[mreqn.imr_ifindex];
2714*aca3beaaSApple OSS Distributions 			if (ifp == NULL) {
2715*aca3beaaSApple OSS Distributions 				ifnet_head_done();
2716*aca3beaaSApple OSS Distributions 				return EADDRNOTAVAIL;
2717*aca3beaaSApple OSS Distributions 			}
2718*aca3beaaSApple OSS Distributions 		}
2719*aca3beaaSApple OSS Distributions 		ifnet_head_done();
2720*aca3beaaSApple OSS Distributions 	} else {
2721*aca3beaaSApple OSS Distributions 		/*
2722*aca3beaaSApple OSS Distributions 		 * An interface was specified by IPv4 address.
2723*aca3beaaSApple OSS Distributions 		 * This is the traditional BSD usage.
2724*aca3beaaSApple OSS Distributions 		 */
2725*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &addr, sizeof(struct in_addr),
2726*aca3beaaSApple OSS Distributions 		    sizeof(struct in_addr));
2727*aca3beaaSApple OSS Distributions 		if (error) {
2728*aca3beaaSApple OSS Distributions 			return error;
2729*aca3beaaSApple OSS Distributions 		}
2730*aca3beaaSApple OSS Distributions 		if (in_nullhost(addr)) {
2731*aca3beaaSApple OSS Distributions 			ifp = NULL;
2732*aca3beaaSApple OSS Distributions 		} else {
2733*aca3beaaSApple OSS Distributions 			ifp = ip_multicast_if(&addr, &ifindex);
2734*aca3beaaSApple OSS Distributions 			if (ifp == NULL) {
2735*aca3beaaSApple OSS Distributions 				IGMP_INET_PRINTF(addr,
2736*aca3beaaSApple OSS Distributions 				    ("%s: can't find ifp for addr=%s\n",
2737*aca3beaaSApple OSS Distributions 				    __func__, _igmp_inet_buf));
2738*aca3beaaSApple OSS Distributions 				return EADDRNOTAVAIL;
2739*aca3beaaSApple OSS Distributions 			}
2740*aca3beaaSApple OSS Distributions 		}
2741*aca3beaaSApple OSS Distributions 	}
2742*aca3beaaSApple OSS Distributions 
2743*aca3beaaSApple OSS Distributions 	/* Reject interfaces which do not support multicast. */
2744*aca3beaaSApple OSS Distributions 	if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0) {
2745*aca3beaaSApple OSS Distributions 		return EOPNOTSUPP;
2746*aca3beaaSApple OSS Distributions 	}
2747*aca3beaaSApple OSS Distributions 
2748*aca3beaaSApple OSS Distributions 	imo = inp_findmoptions(inp);
2749*aca3beaaSApple OSS Distributions 	if (imo == NULL) {
2750*aca3beaaSApple OSS Distributions 		return ENOMEM;
2751*aca3beaaSApple OSS Distributions 	}
2752*aca3beaaSApple OSS Distributions 
2753*aca3beaaSApple OSS Distributions 	IMO_LOCK(imo);
2754*aca3beaaSApple OSS Distributions 	imo->imo_multicast_ifp = ifp;
2755*aca3beaaSApple OSS Distributions 	if (ifindex) {
2756*aca3beaaSApple OSS Distributions 		imo->imo_multicast_addr = addr;
2757*aca3beaaSApple OSS Distributions 	} else {
2758*aca3beaaSApple OSS Distributions 		imo->imo_multicast_addr.s_addr = INADDR_ANY;
2759*aca3beaaSApple OSS Distributions 	}
2760*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(imo);
2761*aca3beaaSApple OSS Distributions 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2762*aca3beaaSApple OSS Distributions 
2763*aca3beaaSApple OSS Distributions 	return 0;
2764*aca3beaaSApple OSS Distributions }
2765*aca3beaaSApple OSS Distributions 
2766*aca3beaaSApple OSS Distributions /*
2767*aca3beaaSApple OSS Distributions  * Atomically set source filters on a socket for an IPv4 multicast group.
2768*aca3beaaSApple OSS Distributions  */
2769*aca3beaaSApple OSS Distributions static int
inp_set_source_filters(struct inpcb * inp,struct sockopt * sopt)2770*aca3beaaSApple OSS Distributions inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
2771*aca3beaaSApple OSS Distributions {
2772*aca3beaaSApple OSS Distributions 	struct __msfilterreq64   msfr = {}, msfr64;
2773*aca3beaaSApple OSS Distributions 	struct __msfilterreq32   msfr32;
2774*aca3beaaSApple OSS Distributions 	struct sockaddr_in      *gsa;
2775*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
2776*aca3beaaSApple OSS Distributions 	struct in_mfilter       *imf;
2777*aca3beaaSApple OSS Distributions 	struct ip_moptions      *imo;
2778*aca3beaaSApple OSS Distributions 	struct in_multi         *inm;
2779*aca3beaaSApple OSS Distributions 	size_t                   idx;
2780*aca3beaaSApple OSS Distributions 	int                      error;
2781*aca3beaaSApple OSS Distributions 	uint64_t                 tmp_ptr;
2782*aca3beaaSApple OSS Distributions 	struct igmp_tparams      itp;
2783*aca3beaaSApple OSS Distributions 
2784*aca3beaaSApple OSS Distributions 	bzero(&itp, sizeof(itp));
2785*aca3beaaSApple OSS Distributions 
2786*aca3beaaSApple OSS Distributions 	int is_64bit_proc = IS_64BIT_PROCESS(current_proc());
2787*aca3beaaSApple OSS Distributions 
2788*aca3beaaSApple OSS Distributions 	if (is_64bit_proc) {
2789*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &msfr64,
2790*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq64),
2791*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq64));
2792*aca3beaaSApple OSS Distributions 		if (error) {
2793*aca3beaaSApple OSS Distributions 			return error;
2794*aca3beaaSApple OSS Distributions 		}
2795*aca3beaaSApple OSS Distributions 		/* we never use msfr.msfr_srcs; */
2796*aca3beaaSApple OSS Distributions 		memcpy(&msfr, &msfr64, sizeof(msfr64));
2797*aca3beaaSApple OSS Distributions 	} else {
2798*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &msfr32,
2799*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq32),
2800*aca3beaaSApple OSS Distributions 		    sizeof(struct __msfilterreq32));
2801*aca3beaaSApple OSS Distributions 		if (error) {
2802*aca3beaaSApple OSS Distributions 			return error;
2803*aca3beaaSApple OSS Distributions 		}
2804*aca3beaaSApple OSS Distributions 		/* we never use msfr.msfr_srcs; */
2805*aca3beaaSApple OSS Distributions 		memcpy(&msfr, &msfr32, sizeof(msfr32));
2806*aca3beaaSApple OSS Distributions 	}
2807*aca3beaaSApple OSS Distributions 
2808*aca3beaaSApple OSS Distributions 	if ((size_t) msfr.msfr_nsrcs >
2809*aca3beaaSApple OSS Distributions 	    UINT32_MAX / sizeof(struct sockaddr_storage)) {
2810*aca3beaaSApple OSS Distributions 		msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage);
2811*aca3beaaSApple OSS Distributions 	}
2812*aca3beaaSApple OSS Distributions 
2813*aca3beaaSApple OSS Distributions 	if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) {
2814*aca3beaaSApple OSS Distributions 		return ENOBUFS;
2815*aca3beaaSApple OSS Distributions 	}
2816*aca3beaaSApple OSS Distributions 
2817*aca3beaaSApple OSS Distributions 	if ((msfr.msfr_fmode != MCAST_EXCLUDE &&
2818*aca3beaaSApple OSS Distributions 	    msfr.msfr_fmode != MCAST_INCLUDE)) {
2819*aca3beaaSApple OSS Distributions 		return EINVAL;
2820*aca3beaaSApple OSS Distributions 	}
2821*aca3beaaSApple OSS Distributions 
2822*aca3beaaSApple OSS Distributions 	if (msfr.msfr_group.ss_family != AF_INET ||
2823*aca3beaaSApple OSS Distributions 	    msfr.msfr_group.ss_len != sizeof(struct sockaddr_in)) {
2824*aca3beaaSApple OSS Distributions 		return EINVAL;
2825*aca3beaaSApple OSS Distributions 	}
2826*aca3beaaSApple OSS Distributions 
2827*aca3beaaSApple OSS Distributions 	gsa = (struct sockaddr_in *)&msfr.msfr_group;
2828*aca3beaaSApple OSS Distributions 	if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) {
2829*aca3beaaSApple OSS Distributions 		return EINVAL;
2830*aca3beaaSApple OSS Distributions 	}
2831*aca3beaaSApple OSS Distributions 
2832*aca3beaaSApple OSS Distributions 	gsa->sin_port = 0;      /* ignore port */
2833*aca3beaaSApple OSS Distributions 
2834*aca3beaaSApple OSS Distributions 	ifnet_head_lock_shared();
2835*aca3beaaSApple OSS Distributions 	if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
2836*aca3beaaSApple OSS Distributions 		ifnet_head_done();
2837*aca3beaaSApple OSS Distributions 		return EADDRNOTAVAIL;
2838*aca3beaaSApple OSS Distributions 	}
2839*aca3beaaSApple OSS Distributions 
2840*aca3beaaSApple OSS Distributions 	ifp = ifindex2ifnet[msfr.msfr_ifindex];
2841*aca3beaaSApple OSS Distributions 	ifnet_head_done();
2842*aca3beaaSApple OSS Distributions 	if (ifp == NULL) {
2843*aca3beaaSApple OSS Distributions 		return EADDRNOTAVAIL;
2844*aca3beaaSApple OSS Distributions 	}
2845*aca3beaaSApple OSS Distributions 
2846*aca3beaaSApple OSS Distributions 	/*
2847*aca3beaaSApple OSS Distributions 	 * Check if this socket is a member of this group.
2848*aca3beaaSApple OSS Distributions 	 */
2849*aca3beaaSApple OSS Distributions 	imo = inp_findmoptions(inp);
2850*aca3beaaSApple OSS Distributions 	if (imo == NULL) {
2851*aca3beaaSApple OSS Distributions 		return ENOMEM;
2852*aca3beaaSApple OSS Distributions 	}
2853*aca3beaaSApple OSS Distributions 
2854*aca3beaaSApple OSS Distributions 	IMO_LOCK(imo);
2855*aca3beaaSApple OSS Distributions 	idx = imo_match_group(imo, ifp, gsa);
2856*aca3beaaSApple OSS Distributions 	if (idx == (size_t)-1 || imo->imo_mfilters == NULL) {
2857*aca3beaaSApple OSS Distributions 		error = EADDRNOTAVAIL;
2858*aca3beaaSApple OSS Distributions 		goto out_imo_locked;
2859*aca3beaaSApple OSS Distributions 	}
2860*aca3beaaSApple OSS Distributions 	inm = imo->imo_membership[idx];
2861*aca3beaaSApple OSS Distributions 	imf = &imo->imo_mfilters[idx];
2862*aca3beaaSApple OSS Distributions 
2863*aca3beaaSApple OSS Distributions 	/*
2864*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at socket layer.
2865*aca3beaaSApple OSS Distributions 	 */
2866*aca3beaaSApple OSS Distributions 
2867*aca3beaaSApple OSS Distributions 	imf->imf_st[1] = (uint8_t)msfr.msfr_fmode;
2868*aca3beaaSApple OSS Distributions 
2869*aca3beaaSApple OSS Distributions 	/*
2870*aca3beaaSApple OSS Distributions 	 * Apply any new source filters, if present.
2871*aca3beaaSApple OSS Distributions 	 * Make a copy of the user-space source vector so
2872*aca3beaaSApple OSS Distributions 	 * that we may copy them with a single copyin. This
2873*aca3beaaSApple OSS Distributions 	 * allows us to deal with page faults up-front.
2874*aca3beaaSApple OSS Distributions 	 */
2875*aca3beaaSApple OSS Distributions 	if (msfr.msfr_nsrcs > 0) {
2876*aca3beaaSApple OSS Distributions 		struct in_msource       *lims;
2877*aca3beaaSApple OSS Distributions 		struct sockaddr_in      *psin;
2878*aca3beaaSApple OSS Distributions 		struct sockaddr_storage *kss, *pkss;
2879*aca3beaaSApple OSS Distributions 		int                      i;
2880*aca3beaaSApple OSS Distributions 
2881*aca3beaaSApple OSS Distributions 		if (is_64bit_proc) {
2882*aca3beaaSApple OSS Distributions 			tmp_ptr = msfr64.msfr_srcs;
2883*aca3beaaSApple OSS Distributions 		} else {
2884*aca3beaaSApple OSS Distributions 			tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
2885*aca3beaaSApple OSS Distributions 		}
2886*aca3beaaSApple OSS Distributions 
2887*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: loading %lu source list entries\n",
2888*aca3beaaSApple OSS Distributions 		    __func__, (unsigned long)msfr.msfr_nsrcs));
2889*aca3beaaSApple OSS Distributions 		kss = kalloc_data((size_t)msfr.msfr_nsrcs * sizeof(*kss), Z_WAITOK);
2890*aca3beaaSApple OSS Distributions 		if (kss == NULL) {
2891*aca3beaaSApple OSS Distributions 			error = ENOMEM;
2892*aca3beaaSApple OSS Distributions 			goto out_imo_locked;
2893*aca3beaaSApple OSS Distributions 		}
2894*aca3beaaSApple OSS Distributions 		error = copyin(CAST_USER_ADDR_T(tmp_ptr), kss,
2895*aca3beaaSApple OSS Distributions 		    (size_t) msfr.msfr_nsrcs * sizeof(*kss));
2896*aca3beaaSApple OSS Distributions 		if (error) {
2897*aca3beaaSApple OSS Distributions 			kfree_data(kss, (size_t)msfr.msfr_nsrcs * sizeof(*kss));
2898*aca3beaaSApple OSS Distributions 			goto out_imo_locked;
2899*aca3beaaSApple OSS Distributions 		}
2900*aca3beaaSApple OSS Distributions 
2901*aca3beaaSApple OSS Distributions 		/*
2902*aca3beaaSApple OSS Distributions 		 * Mark all source filters as UNDEFINED at t1.
2903*aca3beaaSApple OSS Distributions 		 * Restore new group filter mode, as imf_leave()
2904*aca3beaaSApple OSS Distributions 		 * will set it to INCLUDE.
2905*aca3beaaSApple OSS Distributions 		 */
2906*aca3beaaSApple OSS Distributions 		imf_leave(imf);
2907*aca3beaaSApple OSS Distributions 		imf->imf_st[1] = (uint8_t)msfr.msfr_fmode;
2908*aca3beaaSApple OSS Distributions 
2909*aca3beaaSApple OSS Distributions 		/*
2910*aca3beaaSApple OSS Distributions 		 * Update socket layer filters at t1, lazy-allocating
2911*aca3beaaSApple OSS Distributions 		 * new entries. This saves a bunch of memory at the
2912*aca3beaaSApple OSS Distributions 		 * cost of one RB_FIND() per source entry; duplicate
2913*aca3beaaSApple OSS Distributions 		 * entries in the msfr_nsrcs vector are ignored.
2914*aca3beaaSApple OSS Distributions 		 * If we encounter an error, rollback transaction.
2915*aca3beaaSApple OSS Distributions 		 *
2916*aca3beaaSApple OSS Distributions 		 * XXX This too could be replaced with a set-symmetric
2917*aca3beaaSApple OSS Distributions 		 * difference like loop to avoid walking from root
2918*aca3beaaSApple OSS Distributions 		 * every time, as the key space is common.
2919*aca3beaaSApple OSS Distributions 		 */
2920*aca3beaaSApple OSS Distributions 		for (i = 0, pkss = kss; (u_int)i < msfr.msfr_nsrcs;
2921*aca3beaaSApple OSS Distributions 		    i++, pkss++) {
2922*aca3beaaSApple OSS Distributions 			psin = (struct sockaddr_in *)pkss;
2923*aca3beaaSApple OSS Distributions 			if (psin->sin_family != AF_INET) {
2924*aca3beaaSApple OSS Distributions 				error = EAFNOSUPPORT;
2925*aca3beaaSApple OSS Distributions 				break;
2926*aca3beaaSApple OSS Distributions 			}
2927*aca3beaaSApple OSS Distributions 			if (psin->sin_len != sizeof(struct sockaddr_in)) {
2928*aca3beaaSApple OSS Distributions 				error = EINVAL;
2929*aca3beaaSApple OSS Distributions 				break;
2930*aca3beaaSApple OSS Distributions 			}
2931*aca3beaaSApple OSS Distributions 			error = imf_get_source(imf, psin, &lims);
2932*aca3beaaSApple OSS Distributions 			if (error) {
2933*aca3beaaSApple OSS Distributions 				break;
2934*aca3beaaSApple OSS Distributions 			}
2935*aca3beaaSApple OSS Distributions 			lims->imsl_st[1] = imf->imf_st[1];
2936*aca3beaaSApple OSS Distributions 		}
2937*aca3beaaSApple OSS Distributions 		kfree_data(kss, (size_t)msfr.msfr_nsrcs * sizeof(*kss));
2938*aca3beaaSApple OSS Distributions 	}
2939*aca3beaaSApple OSS Distributions 
2940*aca3beaaSApple OSS Distributions 	if (error) {
2941*aca3beaaSApple OSS Distributions 		goto out_imf_rollback;
2942*aca3beaaSApple OSS Distributions 	}
2943*aca3beaaSApple OSS Distributions 
2944*aca3beaaSApple OSS Distributions 	/*
2945*aca3beaaSApple OSS Distributions 	 * Begin state merge transaction at IGMP layer.
2946*aca3beaaSApple OSS Distributions 	 */
2947*aca3beaaSApple OSS Distributions 	INM_LOCK(inm);
2948*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: merge inm state\n", __func__));
2949*aca3beaaSApple OSS Distributions 	error = inm_merge(inm, imf);
2950*aca3beaaSApple OSS Distributions 	if (error) {
2951*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: failed to merge inm state\n", __func__));
2952*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
2953*aca3beaaSApple OSS Distributions 		goto out_imf_rollback;
2954*aca3beaaSApple OSS Distributions 	}
2955*aca3beaaSApple OSS Distributions 
2956*aca3beaaSApple OSS Distributions 	IGMP_PRINTF(("%s: doing igmp downcall\n", __func__));
2957*aca3beaaSApple OSS Distributions 	error = igmp_change_state(inm, &itp);
2958*aca3beaaSApple OSS Distributions 	INM_UNLOCK(inm);
2959*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
2960*aca3beaaSApple OSS Distributions 	if (error) {
2961*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: failed igmp downcall\n", __func__));
2962*aca3beaaSApple OSS Distributions 	}
2963*aca3beaaSApple OSS Distributions #endif
2964*aca3beaaSApple OSS Distributions 
2965*aca3beaaSApple OSS Distributions out_imf_rollback:
2966*aca3beaaSApple OSS Distributions 	if (error) {
2967*aca3beaaSApple OSS Distributions 		imf_rollback(imf);
2968*aca3beaaSApple OSS Distributions 	} else {
2969*aca3beaaSApple OSS Distributions 		imf_commit(imf);
2970*aca3beaaSApple OSS Distributions 	}
2971*aca3beaaSApple OSS Distributions 
2972*aca3beaaSApple OSS Distributions 	imf_reap(imf);
2973*aca3beaaSApple OSS Distributions 
2974*aca3beaaSApple OSS Distributions out_imo_locked:
2975*aca3beaaSApple OSS Distributions 	IMO_UNLOCK(imo);
2976*aca3beaaSApple OSS Distributions 	IMO_REMREF(imo);        /* from inp_findmoptions() */
2977*aca3beaaSApple OSS Distributions 
2978*aca3beaaSApple OSS Distributions 	/* schedule timer now that we've dropped the lock(s) */
2979*aca3beaaSApple OSS Distributions 	igmp_set_fast_timeout(&itp);
2980*aca3beaaSApple OSS Distributions 
2981*aca3beaaSApple OSS Distributions 	return error;
2982*aca3beaaSApple OSS Distributions }
2983*aca3beaaSApple OSS Distributions 
2984*aca3beaaSApple OSS Distributions /*
2985*aca3beaaSApple OSS Distributions  * Set the IP multicast options in response to user setsockopt().
2986*aca3beaaSApple OSS Distributions  *
2987*aca3beaaSApple OSS Distributions  * Many of the socket options handled in this function duplicate the
2988*aca3beaaSApple OSS Distributions  * functionality of socket options in the regular unicast API. However,
2989*aca3beaaSApple OSS Distributions  * it is not possible to merge the duplicate code, because the idempotence
2990*aca3beaaSApple OSS Distributions  * of the IPv4 multicast part of the BSD Sockets API must be preserved;
2991*aca3beaaSApple OSS Distributions  * the effects of these options must be treated as separate and distinct.
2992*aca3beaaSApple OSS Distributions  */
2993*aca3beaaSApple OSS Distributions int
inp_setmoptions(struct inpcb * inp,struct sockopt * sopt)2994*aca3beaaSApple OSS Distributions inp_setmoptions(struct inpcb *inp, struct sockopt *sopt)
2995*aca3beaaSApple OSS Distributions {
2996*aca3beaaSApple OSS Distributions 	struct ip_moptions      *imo;
2997*aca3beaaSApple OSS Distributions 	int                      error;
2998*aca3beaaSApple OSS Distributions 	unsigned int             ifindex;
2999*aca3beaaSApple OSS Distributions 	struct ifnet            *ifp;
3000*aca3beaaSApple OSS Distributions 
3001*aca3beaaSApple OSS Distributions 	error = 0;
3002*aca3beaaSApple OSS Distributions 
3003*aca3beaaSApple OSS Distributions 	/*
3004*aca3beaaSApple OSS Distributions 	 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
3005*aca3beaaSApple OSS Distributions 	 * or is a divert socket, reject it.
3006*aca3beaaSApple OSS Distributions 	 */
3007*aca3beaaSApple OSS Distributions 	if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT ||
3008*aca3beaaSApple OSS Distributions 	    (SOCK_TYPE(inp->inp_socket) != SOCK_RAW &&
3009*aca3beaaSApple OSS Distributions 	    SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) {
3010*aca3beaaSApple OSS Distributions 		return EOPNOTSUPP;
3011*aca3beaaSApple OSS Distributions 	}
3012*aca3beaaSApple OSS Distributions 
3013*aca3beaaSApple OSS Distributions 	switch (sopt->sopt_name) {
3014*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_IF:
3015*aca3beaaSApple OSS Distributions 		error = inp_set_multicast_if(inp, sopt);
3016*aca3beaaSApple OSS Distributions 		break;
3017*aca3beaaSApple OSS Distributions 
3018*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_IFINDEX:
3019*aca3beaaSApple OSS Distributions 		/*
3020*aca3beaaSApple OSS Distributions 		 * Select the interface for outgoing multicast packets.
3021*aca3beaaSApple OSS Distributions 		 */
3022*aca3beaaSApple OSS Distributions 		error = sooptcopyin(sopt, &ifindex, sizeof(ifindex),
3023*aca3beaaSApple OSS Distributions 		    sizeof(ifindex));
3024*aca3beaaSApple OSS Distributions 		if (error) {
3025*aca3beaaSApple OSS Distributions 			break;
3026*aca3beaaSApple OSS Distributions 		}
3027*aca3beaaSApple OSS Distributions 
3028*aca3beaaSApple OSS Distributions 		imo = inp_findmoptions(inp);
3029*aca3beaaSApple OSS Distributions 		if (imo == NULL) {
3030*aca3beaaSApple OSS Distributions 			error = ENOMEM;
3031*aca3beaaSApple OSS Distributions 			break;
3032*aca3beaaSApple OSS Distributions 		}
3033*aca3beaaSApple OSS Distributions 		/*
3034*aca3beaaSApple OSS Distributions 		 * Index 0 is used to remove a previous selection.
3035*aca3beaaSApple OSS Distributions 		 * When no interface is selected, a default one is
3036*aca3beaaSApple OSS Distributions 		 * chosen every time a multicast packet is sent.
3037*aca3beaaSApple OSS Distributions 		 */
3038*aca3beaaSApple OSS Distributions 		if (ifindex == 0) {
3039*aca3beaaSApple OSS Distributions 			IMO_LOCK(imo);
3040*aca3beaaSApple OSS Distributions 			imo->imo_multicast_ifp = NULL;
3041*aca3beaaSApple OSS Distributions 			IMO_UNLOCK(imo);
3042*aca3beaaSApple OSS Distributions 			IMO_REMREF(imo);        /* from inp_findmoptions() */
3043*aca3beaaSApple OSS Distributions 			break;
3044*aca3beaaSApple OSS Distributions 		}
3045*aca3beaaSApple OSS Distributions 
3046*aca3beaaSApple OSS Distributions 		ifnet_head_lock_shared();
3047*aca3beaaSApple OSS Distributions 		/* Don't need to check is ifindex is < 0 since it's unsigned */
3048*aca3beaaSApple OSS Distributions 		if ((unsigned int)if_index < ifindex) {
3049*aca3beaaSApple OSS Distributions 			ifnet_head_done();
3050*aca3beaaSApple OSS Distributions 			IMO_REMREF(imo);        /* from inp_findmoptions() */
3051*aca3beaaSApple OSS Distributions 			error = ENXIO;  /* per IPV6_MULTICAST_IF */
3052*aca3beaaSApple OSS Distributions 			break;
3053*aca3beaaSApple OSS Distributions 		}
3054*aca3beaaSApple OSS Distributions 		ifp = ifindex2ifnet[ifindex];
3055*aca3beaaSApple OSS Distributions 		ifnet_head_done();
3056*aca3beaaSApple OSS Distributions 
3057*aca3beaaSApple OSS Distributions 		/* If it's detached or isn't a multicast interface, bail out */
3058*aca3beaaSApple OSS Distributions 		if (ifp == NULL || !(ifp->if_flags & IFF_MULTICAST)) {
3059*aca3beaaSApple OSS Distributions 			IMO_REMREF(imo);        /* from inp_findmoptions() */
3060*aca3beaaSApple OSS Distributions 			error = EADDRNOTAVAIL;
3061*aca3beaaSApple OSS Distributions 			break;
3062*aca3beaaSApple OSS Distributions 		}
3063*aca3beaaSApple OSS Distributions 		IMO_LOCK(imo);
3064*aca3beaaSApple OSS Distributions 		imo->imo_multicast_ifp = ifp;
3065*aca3beaaSApple OSS Distributions 		/*
3066*aca3beaaSApple OSS Distributions 		 * Clear out any remnants of past IP_MULTICAST_IF.  The addr
3067*aca3beaaSApple OSS Distributions 		 * isn't really used anywhere in the kernel; we could have
3068*aca3beaaSApple OSS Distributions 		 * iterated thru the addresses of the interface and pick one
3069*aca3beaaSApple OSS Distributions 		 * here, but that is redundant since ip_getmoptions() already
3070*aca3beaaSApple OSS Distributions 		 * takes care of that for INADDR_ANY.
3071*aca3beaaSApple OSS Distributions 		 */
3072*aca3beaaSApple OSS Distributions 		imo->imo_multicast_addr.s_addr = INADDR_ANY;
3073*aca3beaaSApple OSS Distributions 		IMO_UNLOCK(imo);
3074*aca3beaaSApple OSS Distributions 		IMO_REMREF(imo);        /* from inp_findmoptions() */
3075*aca3beaaSApple OSS Distributions 		break;
3076*aca3beaaSApple OSS Distributions 
3077*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_TTL: {
3078*aca3beaaSApple OSS Distributions 		u_char ttl;
3079*aca3beaaSApple OSS Distributions 
3080*aca3beaaSApple OSS Distributions 		/*
3081*aca3beaaSApple OSS Distributions 		 * Set the IP time-to-live for outgoing multicast packets.
3082*aca3beaaSApple OSS Distributions 		 * The original multicast API required a char argument,
3083*aca3beaaSApple OSS Distributions 		 * which is inconsistent with the rest of the socket API.
3084*aca3beaaSApple OSS Distributions 		 * We allow either a char or an int.
3085*aca3beaaSApple OSS Distributions 		 */
3086*aca3beaaSApple OSS Distributions 		if (sopt->sopt_valsize == sizeof(u_char)) {
3087*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &ttl, sizeof(u_char),
3088*aca3beaaSApple OSS Distributions 			    sizeof(u_char));
3089*aca3beaaSApple OSS Distributions 			if (error) {
3090*aca3beaaSApple OSS Distributions 				break;
3091*aca3beaaSApple OSS Distributions 			}
3092*aca3beaaSApple OSS Distributions 		} else {
3093*aca3beaaSApple OSS Distributions 			u_int ittl;
3094*aca3beaaSApple OSS Distributions 
3095*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &ittl, sizeof(u_int),
3096*aca3beaaSApple OSS Distributions 			    sizeof(u_int));
3097*aca3beaaSApple OSS Distributions 			if (error) {
3098*aca3beaaSApple OSS Distributions 				break;
3099*aca3beaaSApple OSS Distributions 			}
3100*aca3beaaSApple OSS Distributions 			if (ittl > 255) {
3101*aca3beaaSApple OSS Distributions 				error = EINVAL;
3102*aca3beaaSApple OSS Distributions 				break;
3103*aca3beaaSApple OSS Distributions 			}
3104*aca3beaaSApple OSS Distributions 			ttl = (u_char)ittl;
3105*aca3beaaSApple OSS Distributions 		}
3106*aca3beaaSApple OSS Distributions 		imo = inp_findmoptions(inp);
3107*aca3beaaSApple OSS Distributions 		if (imo == NULL) {
3108*aca3beaaSApple OSS Distributions 			error = ENOMEM;
3109*aca3beaaSApple OSS Distributions 			break;
3110*aca3beaaSApple OSS Distributions 		}
3111*aca3beaaSApple OSS Distributions 		IMO_LOCK(imo);
3112*aca3beaaSApple OSS Distributions 		imo->imo_multicast_ttl = ttl;
3113*aca3beaaSApple OSS Distributions 		IMO_UNLOCK(imo);
3114*aca3beaaSApple OSS Distributions 		IMO_REMREF(imo);        /* from inp_findmoptions() */
3115*aca3beaaSApple OSS Distributions 		break;
3116*aca3beaaSApple OSS Distributions 	}
3117*aca3beaaSApple OSS Distributions 
3118*aca3beaaSApple OSS Distributions 	case IP_MULTICAST_LOOP: {
3119*aca3beaaSApple OSS Distributions 		u_char loop;
3120*aca3beaaSApple OSS Distributions 
3121*aca3beaaSApple OSS Distributions 		/*
3122*aca3beaaSApple OSS Distributions 		 * Set the loopback flag for outgoing multicast packets.
3123*aca3beaaSApple OSS Distributions 		 * Must be zero or one.  The original multicast API required a
3124*aca3beaaSApple OSS Distributions 		 * char argument, which is inconsistent with the rest
3125*aca3beaaSApple OSS Distributions 		 * of the socket API.  We allow either a char or an int.
3126*aca3beaaSApple OSS Distributions 		 */
3127*aca3beaaSApple OSS Distributions 		if (sopt->sopt_valsize == sizeof(u_char)) {
3128*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &loop, sizeof(u_char),
3129*aca3beaaSApple OSS Distributions 			    sizeof(u_char));
3130*aca3beaaSApple OSS Distributions 			if (error) {
3131*aca3beaaSApple OSS Distributions 				break;
3132*aca3beaaSApple OSS Distributions 			}
3133*aca3beaaSApple OSS Distributions 		} else {
3134*aca3beaaSApple OSS Distributions 			u_int iloop;
3135*aca3beaaSApple OSS Distributions 
3136*aca3beaaSApple OSS Distributions 			error = sooptcopyin(sopt, &iloop, sizeof(u_int),
3137*aca3beaaSApple OSS Distributions 			    sizeof(u_int));
3138*aca3beaaSApple OSS Distributions 			if (error) {
3139*aca3beaaSApple OSS Distributions 				break;
3140*aca3beaaSApple OSS Distributions 			}
3141*aca3beaaSApple OSS Distributions 			loop = (u_char)iloop;
3142*aca3beaaSApple OSS Distributions 		}
3143*aca3beaaSApple OSS Distributions 		imo = inp_findmoptions(inp);
3144*aca3beaaSApple OSS Distributions 		if (imo == NULL) {
3145*aca3beaaSApple OSS Distributions 			error = ENOMEM;
3146*aca3beaaSApple OSS Distributions 			break;
3147*aca3beaaSApple OSS Distributions 		}
3148*aca3beaaSApple OSS Distributions 		IMO_LOCK(imo);
3149*aca3beaaSApple OSS Distributions 		imo->imo_multicast_loop = !!loop;
3150*aca3beaaSApple OSS Distributions 		IMO_UNLOCK(imo);
3151*aca3beaaSApple OSS Distributions 		IMO_REMREF(imo);        /* from inp_findmoptions() */
3152*aca3beaaSApple OSS Distributions 		break;
3153*aca3beaaSApple OSS Distributions 	}
3154*aca3beaaSApple OSS Distributions 
3155*aca3beaaSApple OSS Distributions 	case IP_ADD_MEMBERSHIP:
3156*aca3beaaSApple OSS Distributions 	case IP_ADD_SOURCE_MEMBERSHIP:
3157*aca3beaaSApple OSS Distributions 	case MCAST_JOIN_GROUP:
3158*aca3beaaSApple OSS Distributions 	case MCAST_JOIN_SOURCE_GROUP:
3159*aca3beaaSApple OSS Distributions 		error = inp_join_group(inp, sopt);
3160*aca3beaaSApple OSS Distributions 		break;
3161*aca3beaaSApple OSS Distributions 
3162*aca3beaaSApple OSS Distributions 	case IP_DROP_MEMBERSHIP:
3163*aca3beaaSApple OSS Distributions 	case IP_DROP_SOURCE_MEMBERSHIP:
3164*aca3beaaSApple OSS Distributions 	case MCAST_LEAVE_GROUP:
3165*aca3beaaSApple OSS Distributions 	case MCAST_LEAVE_SOURCE_GROUP:
3166*aca3beaaSApple OSS Distributions 		error = inp_leave_group(inp, sopt);
3167*aca3beaaSApple OSS Distributions 		break;
3168*aca3beaaSApple OSS Distributions 
3169*aca3beaaSApple OSS Distributions 	case IP_BLOCK_SOURCE:
3170*aca3beaaSApple OSS Distributions 	case IP_UNBLOCK_SOURCE:
3171*aca3beaaSApple OSS Distributions 	case MCAST_BLOCK_SOURCE:
3172*aca3beaaSApple OSS Distributions 	case MCAST_UNBLOCK_SOURCE:
3173*aca3beaaSApple OSS Distributions 		error = inp_block_unblock_source(inp, sopt);
3174*aca3beaaSApple OSS Distributions 		break;
3175*aca3beaaSApple OSS Distributions 
3176*aca3beaaSApple OSS Distributions 	case IP_MSFILTER:
3177*aca3beaaSApple OSS Distributions 		error = inp_set_source_filters(inp, sopt);
3178*aca3beaaSApple OSS Distributions 		break;
3179*aca3beaaSApple OSS Distributions 
3180*aca3beaaSApple OSS Distributions 	default:
3181*aca3beaaSApple OSS Distributions 		error = EOPNOTSUPP;
3182*aca3beaaSApple OSS Distributions 		break;
3183*aca3beaaSApple OSS Distributions 	}
3184*aca3beaaSApple OSS Distributions 
3185*aca3beaaSApple OSS Distributions 	return error;
3186*aca3beaaSApple OSS Distributions }
3187*aca3beaaSApple OSS Distributions 
3188*aca3beaaSApple OSS Distributions /*
3189*aca3beaaSApple OSS Distributions  * Expose IGMP's multicast filter mode and source list(s) to userland,
3190*aca3beaaSApple OSS Distributions  * keyed by (ifindex, group).
3191*aca3beaaSApple OSS Distributions  * The filter mode is written out as a uint32_t, followed by
3192*aca3beaaSApple OSS Distributions  * 0..n of struct in_addr.
3193*aca3beaaSApple OSS Distributions  * For use by ifmcstat(8).
3194*aca3beaaSApple OSS Distributions  */
3195*aca3beaaSApple OSS Distributions static int
3196*aca3beaaSApple OSS Distributions sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS
3197*aca3beaaSApple OSS Distributions {
3198*aca3beaaSApple OSS Distributions #pragma unused(oidp)
3199*aca3beaaSApple OSS Distributions 
3200*aca3beaaSApple OSS Distributions 	struct in_addr                   src = {}, group;
3201*aca3beaaSApple OSS Distributions 	struct ifnet                    *ifp;
3202*aca3beaaSApple OSS Distributions 	struct in_multi                 *inm;
3203*aca3beaaSApple OSS Distributions 	struct in_multistep             step;
3204*aca3beaaSApple OSS Distributions 	struct ip_msource               *ims;
3205*aca3beaaSApple OSS Distributions 	int                             *name;
3206*aca3beaaSApple OSS Distributions 	int                              retval = 0;
3207*aca3beaaSApple OSS Distributions 	u_int                            namelen;
3208*aca3beaaSApple OSS Distributions 	uint32_t                         fmode, ifindex;
3209*aca3beaaSApple OSS Distributions 
3210*aca3beaaSApple OSS Distributions 	name = (int *)arg1;
3211*aca3beaaSApple OSS Distributions 	namelen = (u_int)arg2;
3212*aca3beaaSApple OSS Distributions 
3213*aca3beaaSApple OSS Distributions 	if (req->newptr != USER_ADDR_NULL) {
3214*aca3beaaSApple OSS Distributions 		return EPERM;
3215*aca3beaaSApple OSS Distributions 	}
3216*aca3beaaSApple OSS Distributions 
3217*aca3beaaSApple OSS Distributions 	if (namelen != 2) {
3218*aca3beaaSApple OSS Distributions 		return EINVAL;
3219*aca3beaaSApple OSS Distributions 	}
3220*aca3beaaSApple OSS Distributions 
3221*aca3beaaSApple OSS Distributions 	ifindex = name[0];
3222*aca3beaaSApple OSS Distributions 	ifnet_head_lock_shared();
3223*aca3beaaSApple OSS Distributions 	if (ifindex <= 0 || ifindex > (u_int)if_index) {
3224*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: ifindex %u out of range\n",
3225*aca3beaaSApple OSS Distributions 		    __func__, ifindex));
3226*aca3beaaSApple OSS Distributions 		ifnet_head_done();
3227*aca3beaaSApple OSS Distributions 		return ENOENT;
3228*aca3beaaSApple OSS Distributions 	}
3229*aca3beaaSApple OSS Distributions 
3230*aca3beaaSApple OSS Distributions 	group.s_addr = name[1];
3231*aca3beaaSApple OSS Distributions 	if (!IN_MULTICAST(ntohl(group.s_addr))) {
3232*aca3beaaSApple OSS Distributions 		IGMP_INET_PRINTF(group,
3233*aca3beaaSApple OSS Distributions 		    ("%s: group %s is not multicast\n",
3234*aca3beaaSApple OSS Distributions 		    __func__, _igmp_inet_buf));
3235*aca3beaaSApple OSS Distributions 		ifnet_head_done();
3236*aca3beaaSApple OSS Distributions 		return EINVAL;
3237*aca3beaaSApple OSS Distributions 	}
3238*aca3beaaSApple OSS Distributions 
3239*aca3beaaSApple OSS Distributions 	ifp = ifindex2ifnet[ifindex];
3240*aca3beaaSApple OSS Distributions 	ifnet_head_done();
3241*aca3beaaSApple OSS Distributions 	if (ifp == NULL) {
3242*aca3beaaSApple OSS Distributions 		IGMP_PRINTF(("%s: no ifp for ifindex %u\n", __func__, ifindex));
3243*aca3beaaSApple OSS Distributions 		return ENOENT;
3244*aca3beaaSApple OSS Distributions 	}
3245*aca3beaaSApple OSS Distributions 
3246*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
3247*aca3beaaSApple OSS Distributions 	IN_FIRST_MULTI(step, inm);
3248*aca3beaaSApple OSS Distributions 	while (inm != NULL) {
3249*aca3beaaSApple OSS Distributions 		INM_LOCK(inm);
3250*aca3beaaSApple OSS Distributions 		if (inm->inm_ifp != ifp) {
3251*aca3beaaSApple OSS Distributions 			goto next;
3252*aca3beaaSApple OSS Distributions 		}
3253*aca3beaaSApple OSS Distributions 
3254*aca3beaaSApple OSS Distributions 		if (!in_hosteq(inm->inm_addr, group)) {
3255*aca3beaaSApple OSS Distributions 			goto next;
3256*aca3beaaSApple OSS Distributions 		}
3257*aca3beaaSApple OSS Distributions 
3258*aca3beaaSApple OSS Distributions 		fmode = inm->inm_st[1].iss_fmode;
3259*aca3beaaSApple OSS Distributions 		retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t));
3260*aca3beaaSApple OSS Distributions 		if (retval != 0) {
3261*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
3262*aca3beaaSApple OSS Distributions 			break;          /* abort */
3263*aca3beaaSApple OSS Distributions 		}
3264*aca3beaaSApple OSS Distributions 		RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
3265*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
3266*aca3beaaSApple OSS Distributions 			struct in_addr ina;
3267*aca3beaaSApple OSS Distributions 			ina.s_addr = htonl(ims->ims_haddr);
3268*aca3beaaSApple OSS Distributions 			IGMP_INET_PRINTF(ina,
3269*aca3beaaSApple OSS Distributions 			    ("%s: visit node %s\n", __func__, _igmp_inet_buf));
3270*aca3beaaSApple OSS Distributions #endif
3271*aca3beaaSApple OSS Distributions 			/*
3272*aca3beaaSApple OSS Distributions 			 * Only copy-out sources which are in-mode.
3273*aca3beaaSApple OSS Distributions 			 */
3274*aca3beaaSApple OSS Distributions 			if (fmode != ims_get_mode(inm, ims, 1)) {
3275*aca3beaaSApple OSS Distributions 				IGMP_PRINTF(("%s: skip non-in-mode\n",
3276*aca3beaaSApple OSS Distributions 				    __func__));
3277*aca3beaaSApple OSS Distributions 				continue; /* process next source */
3278*aca3beaaSApple OSS Distributions 			}
3279*aca3beaaSApple OSS Distributions 			src.s_addr = htonl(ims->ims_haddr);
3280*aca3beaaSApple OSS Distributions 			retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr));
3281*aca3beaaSApple OSS Distributions 			if (retval != 0) {
3282*aca3beaaSApple OSS Distributions 				break;  /* process next inm */
3283*aca3beaaSApple OSS Distributions 			}
3284*aca3beaaSApple OSS Distributions 		}
3285*aca3beaaSApple OSS Distributions next:
3286*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
3287*aca3beaaSApple OSS Distributions 		IN_NEXT_MULTI(step, inm);
3288*aca3beaaSApple OSS Distributions 	}
3289*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
3290*aca3beaaSApple OSS Distributions 
3291*aca3beaaSApple OSS Distributions 	return retval;
3292*aca3beaaSApple OSS Distributions }
3293*aca3beaaSApple OSS Distributions 
3294*aca3beaaSApple OSS Distributions /*
3295*aca3beaaSApple OSS Distributions  * XXX
3296*aca3beaaSApple OSS Distributions  * The whole multicast option thing needs to be re-thought.
3297*aca3beaaSApple OSS Distributions  * Several of these options are equally applicable to non-multicast
3298*aca3beaaSApple OSS Distributions  * transmission, and one (IP_MULTICAST_TTL) totally duplicates a
3299*aca3beaaSApple OSS Distributions  * standard option (IP_TTL).
3300*aca3beaaSApple OSS Distributions  */
3301*aca3beaaSApple OSS Distributions /*
3302*aca3beaaSApple OSS Distributions  * following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
3303*aca3beaaSApple OSS Distributions  */
3304*aca3beaaSApple OSS Distributions static struct ifnet *
ip_multicast_if(struct in_addr * a,unsigned int * ifindexp)3305*aca3beaaSApple OSS Distributions ip_multicast_if(struct in_addr *a, unsigned int *ifindexp)
3306*aca3beaaSApple OSS Distributions {
3307*aca3beaaSApple OSS Distributions 	unsigned int ifindex;
3308*aca3beaaSApple OSS Distributions 	struct ifnet *ifp;
3309*aca3beaaSApple OSS Distributions 
3310*aca3beaaSApple OSS Distributions 	if (ifindexp != NULL) {
3311*aca3beaaSApple OSS Distributions 		*ifindexp = 0;
3312*aca3beaaSApple OSS Distributions 	}
3313*aca3beaaSApple OSS Distributions 	if (ntohl(a->s_addr) >> 24 == 0) {
3314*aca3beaaSApple OSS Distributions 		ifindex = ntohl(a->s_addr) & 0xffffff;
3315*aca3beaaSApple OSS Distributions 		ifnet_head_lock_shared();
3316*aca3beaaSApple OSS Distributions 		/* Don't need to check is ifindex is < 0 since it's unsigned */
3317*aca3beaaSApple OSS Distributions 		if ((unsigned int)if_index < ifindex) {
3318*aca3beaaSApple OSS Distributions 			ifnet_head_done();
3319*aca3beaaSApple OSS Distributions 			return NULL;
3320*aca3beaaSApple OSS Distributions 		}
3321*aca3beaaSApple OSS Distributions 		ifp = ifindex2ifnet[ifindex];
3322*aca3beaaSApple OSS Distributions 		ifnet_head_done();
3323*aca3beaaSApple OSS Distributions 		if (ifp != NULL && ifindexp != NULL) {
3324*aca3beaaSApple OSS Distributions 			*ifindexp = ifindex;
3325*aca3beaaSApple OSS Distributions 		}
3326*aca3beaaSApple OSS Distributions 	} else {
3327*aca3beaaSApple OSS Distributions 		INADDR_TO_IFP(*a, ifp);
3328*aca3beaaSApple OSS Distributions 	}
3329*aca3beaaSApple OSS Distributions 	return ifp;
3330*aca3beaaSApple OSS Distributions }
3331*aca3beaaSApple OSS Distributions 
3332*aca3beaaSApple OSS Distributions void
in_multi_init(void)3333*aca3beaaSApple OSS Distributions in_multi_init(void)
3334*aca3beaaSApple OSS Distributions {
3335*aca3beaaSApple OSS Distributions 	TAILQ_INIT(&inm_trash_head);
3336*aca3beaaSApple OSS Distributions 
3337*aca3beaaSApple OSS Distributions 	vm_size_t inm_size = (inm_debug == 0) ? sizeof(struct in_multi) :
3338*aca3beaaSApple OSS Distributions 	    sizeof(struct in_multi_dbg);
3339*aca3beaaSApple OSS Distributions 	inm_zone = zone_create(INM_ZONE_NAME, inm_size, ZC_ZFREE_CLEARMEM);
3340*aca3beaaSApple OSS Distributions }
3341*aca3beaaSApple OSS Distributions 
3342*aca3beaaSApple OSS Distributions static struct in_multi *
in_multi_alloc(zalloc_flags_t how)3343*aca3beaaSApple OSS Distributions in_multi_alloc(zalloc_flags_t how)
3344*aca3beaaSApple OSS Distributions {
3345*aca3beaaSApple OSS Distributions 	struct in_multi *inm;
3346*aca3beaaSApple OSS Distributions 
3347*aca3beaaSApple OSS Distributions 	inm = zalloc_flags(inm_zone, how | Z_ZERO);
3348*aca3beaaSApple OSS Distributions 	if (inm != NULL) {
3349*aca3beaaSApple OSS Distributions 		lck_mtx_init(&inm->inm_lock, &in_multihead_lock_grp,
3350*aca3beaaSApple OSS Distributions 		    &in_multihead_lock_attr);
3351*aca3beaaSApple OSS Distributions 		inm->inm_debug |= IFD_ALLOC;
3352*aca3beaaSApple OSS Distributions 		if (inm_debug != 0) {
3353*aca3beaaSApple OSS Distributions 			inm->inm_debug |= IFD_DEBUG;
3354*aca3beaaSApple OSS Distributions 			inm->inm_trace = inm_trace;
3355*aca3beaaSApple OSS Distributions 		}
3356*aca3beaaSApple OSS Distributions 	}
3357*aca3beaaSApple OSS Distributions 	return inm;
3358*aca3beaaSApple OSS Distributions }
3359*aca3beaaSApple OSS Distributions 
3360*aca3beaaSApple OSS Distributions static void
in_multi_free(struct in_multi * inm)3361*aca3beaaSApple OSS Distributions in_multi_free(struct in_multi *inm)
3362*aca3beaaSApple OSS Distributions {
3363*aca3beaaSApple OSS Distributions 	INM_LOCK(inm);
3364*aca3beaaSApple OSS Distributions 	if (inm->inm_debug & IFD_ATTACHED) {
3365*aca3beaaSApple OSS Distributions 		panic("%s: attached inm=%p is being freed", __func__, inm);
3366*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3367*aca3beaaSApple OSS Distributions 	} else if (inm->inm_ifma != NULL) {
3368*aca3beaaSApple OSS Distributions 		panic("%s: ifma not NULL for inm=%p", __func__, inm);
3369*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3370*aca3beaaSApple OSS Distributions 	} else if (!(inm->inm_debug & IFD_ALLOC)) {
3371*aca3beaaSApple OSS Distributions 		panic("%s: inm %p cannot be freed", __func__, inm);
3372*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3373*aca3beaaSApple OSS Distributions 	} else if (inm->inm_refcount != 0) {
3374*aca3beaaSApple OSS Distributions 		panic("%s: non-zero refcount inm=%p", __func__, inm);
3375*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3376*aca3beaaSApple OSS Distributions 	} else if (inm->inm_reqcnt != 0) {
3377*aca3beaaSApple OSS Distributions 		panic("%s: non-zero reqcnt inm=%p", __func__, inm);
3378*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3379*aca3beaaSApple OSS Distributions 	}
3380*aca3beaaSApple OSS Distributions 
3381*aca3beaaSApple OSS Distributions 	/* Free any pending IGMPv3 state-change records */
3382*aca3beaaSApple OSS Distributions 	IF_DRAIN(&inm->inm_scq);
3383*aca3beaaSApple OSS Distributions 
3384*aca3beaaSApple OSS Distributions 	inm->inm_debug &= ~IFD_ALLOC;
3385*aca3beaaSApple OSS Distributions 	if ((inm->inm_debug & (IFD_DEBUG | IFD_TRASHED)) ==
3386*aca3beaaSApple OSS Distributions 	    (IFD_DEBUG | IFD_TRASHED)) {
3387*aca3beaaSApple OSS Distributions 		lck_mtx_lock(&inm_trash_lock);
3388*aca3beaaSApple OSS Distributions 		TAILQ_REMOVE(&inm_trash_head, (struct in_multi_dbg *)inm,
3389*aca3beaaSApple OSS Distributions 		    inm_trash_link);
3390*aca3beaaSApple OSS Distributions 		lck_mtx_unlock(&inm_trash_lock);
3391*aca3beaaSApple OSS Distributions 		inm->inm_debug &= ~IFD_TRASHED;
3392*aca3beaaSApple OSS Distributions 	}
3393*aca3beaaSApple OSS Distributions 	INM_UNLOCK(inm);
3394*aca3beaaSApple OSS Distributions 
3395*aca3beaaSApple OSS Distributions 	lck_mtx_destroy(&inm->inm_lock, &in_multihead_lock_grp);
3396*aca3beaaSApple OSS Distributions 	zfree(inm_zone, inm);
3397*aca3beaaSApple OSS Distributions }
3398*aca3beaaSApple OSS Distributions 
3399*aca3beaaSApple OSS Distributions static void
in_multi_attach(struct in_multi * inm)3400*aca3beaaSApple OSS Distributions in_multi_attach(struct in_multi *inm)
3401*aca3beaaSApple OSS Distributions {
3402*aca3beaaSApple OSS Distributions 	in_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE);
3403*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
3404*aca3beaaSApple OSS Distributions 
3405*aca3beaaSApple OSS Distributions 	if (inm->inm_debug & IFD_ATTACHED) {
3406*aca3beaaSApple OSS Distributions 		panic("%s: Attempt to attach an already attached inm=%p",
3407*aca3beaaSApple OSS Distributions 		    __func__, inm);
3408*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3409*aca3beaaSApple OSS Distributions 	} else if (inm->inm_debug & IFD_TRASHED) {
3410*aca3beaaSApple OSS Distributions 		panic("%s: Attempt to reattach a detached inm=%p",
3411*aca3beaaSApple OSS Distributions 		    __func__, inm);
3412*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3413*aca3beaaSApple OSS Distributions 	}
3414*aca3beaaSApple OSS Distributions 
3415*aca3beaaSApple OSS Distributions 	inm->inm_reqcnt++;
3416*aca3beaaSApple OSS Distributions 	VERIFY(inm->inm_reqcnt == 1);
3417*aca3beaaSApple OSS Distributions 	INM_ADDREF_LOCKED(inm);
3418*aca3beaaSApple OSS Distributions 	inm->inm_debug |= IFD_ATTACHED;
3419*aca3beaaSApple OSS Distributions 	/*
3420*aca3beaaSApple OSS Distributions 	 * Reattach case:  If debugging is enabled, take it
3421*aca3beaaSApple OSS Distributions 	 * out of the trash list and clear IFD_TRASHED.
3422*aca3beaaSApple OSS Distributions 	 */
3423*aca3beaaSApple OSS Distributions 	if ((inm->inm_debug & (IFD_DEBUG | IFD_TRASHED)) ==
3424*aca3beaaSApple OSS Distributions 	    (IFD_DEBUG | IFD_TRASHED)) {
3425*aca3beaaSApple OSS Distributions 		/* Become a regular mutex, just in case */
3426*aca3beaaSApple OSS Distributions 		INM_CONVERT_LOCK(inm);
3427*aca3beaaSApple OSS Distributions 		lck_mtx_lock(&inm_trash_lock);
3428*aca3beaaSApple OSS Distributions 		TAILQ_REMOVE(&inm_trash_head, (struct in_multi_dbg *)inm,
3429*aca3beaaSApple OSS Distributions 		    inm_trash_link);
3430*aca3beaaSApple OSS Distributions 		lck_mtx_unlock(&inm_trash_lock);
3431*aca3beaaSApple OSS Distributions 		inm->inm_debug &= ~IFD_TRASHED;
3432*aca3beaaSApple OSS Distributions 	}
3433*aca3beaaSApple OSS Distributions 
3434*aca3beaaSApple OSS Distributions 	LIST_INSERT_HEAD(&in_multihead, inm, inm_link);
3435*aca3beaaSApple OSS Distributions }
3436*aca3beaaSApple OSS Distributions 
3437*aca3beaaSApple OSS Distributions int
in_multi_detach(struct in_multi * inm)3438*aca3beaaSApple OSS Distributions in_multi_detach(struct in_multi *inm)
3439*aca3beaaSApple OSS Distributions {
3440*aca3beaaSApple OSS Distributions 	in_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE);
3441*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(inm);
3442*aca3beaaSApple OSS Distributions 
3443*aca3beaaSApple OSS Distributions 	if (inm->inm_reqcnt == 0) {
3444*aca3beaaSApple OSS Distributions 		panic("%s: inm=%p negative reqcnt", __func__, inm);
3445*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3446*aca3beaaSApple OSS Distributions 	}
3447*aca3beaaSApple OSS Distributions 
3448*aca3beaaSApple OSS Distributions 	--inm->inm_reqcnt;
3449*aca3beaaSApple OSS Distributions 	if (inm->inm_reqcnt > 0) {
3450*aca3beaaSApple OSS Distributions 		return 0;
3451*aca3beaaSApple OSS Distributions 	}
3452*aca3beaaSApple OSS Distributions 
3453*aca3beaaSApple OSS Distributions 	if (!(inm->inm_debug & IFD_ATTACHED)) {
3454*aca3beaaSApple OSS Distributions 		panic("%s: Attempt to detach an unattached record inm=%p",
3455*aca3beaaSApple OSS Distributions 		    __func__, inm);
3456*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3457*aca3beaaSApple OSS Distributions 	} else if (inm->inm_debug & IFD_TRASHED) {
3458*aca3beaaSApple OSS Distributions 		panic("%s: inm %p is already in trash list", __func__, inm);
3459*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3460*aca3beaaSApple OSS Distributions 	}
3461*aca3beaaSApple OSS Distributions 
3462*aca3beaaSApple OSS Distributions 	/*
3463*aca3beaaSApple OSS Distributions 	 * NOTE: Caller calls IFMA_REMREF
3464*aca3beaaSApple OSS Distributions 	 */
3465*aca3beaaSApple OSS Distributions 	inm->inm_debug &= ~IFD_ATTACHED;
3466*aca3beaaSApple OSS Distributions 	LIST_REMOVE(inm, inm_link);
3467*aca3beaaSApple OSS Distributions 
3468*aca3beaaSApple OSS Distributions 	if (inm->inm_debug & IFD_DEBUG) {
3469*aca3beaaSApple OSS Distributions 		/* Become a regular mutex, just in case */
3470*aca3beaaSApple OSS Distributions 		INM_CONVERT_LOCK(inm);
3471*aca3beaaSApple OSS Distributions 		lck_mtx_lock(&inm_trash_lock);
3472*aca3beaaSApple OSS Distributions 		TAILQ_INSERT_TAIL(&inm_trash_head,
3473*aca3beaaSApple OSS Distributions 		    (struct in_multi_dbg *)inm, inm_trash_link);
3474*aca3beaaSApple OSS Distributions 		lck_mtx_unlock(&inm_trash_lock);
3475*aca3beaaSApple OSS Distributions 		inm->inm_debug |= IFD_TRASHED;
3476*aca3beaaSApple OSS Distributions 	}
3477*aca3beaaSApple OSS Distributions 
3478*aca3beaaSApple OSS Distributions 	return 1;
3479*aca3beaaSApple OSS Distributions }
3480*aca3beaaSApple OSS Distributions 
3481*aca3beaaSApple OSS Distributions void
inm_addref(struct in_multi * inm,int locked)3482*aca3beaaSApple OSS Distributions inm_addref(struct in_multi *inm, int locked)
3483*aca3beaaSApple OSS Distributions {
3484*aca3beaaSApple OSS Distributions 	if (!locked) {
3485*aca3beaaSApple OSS Distributions 		INM_LOCK_SPIN(inm);
3486*aca3beaaSApple OSS Distributions 	} else {
3487*aca3beaaSApple OSS Distributions 		INM_LOCK_ASSERT_HELD(inm);
3488*aca3beaaSApple OSS Distributions 	}
3489*aca3beaaSApple OSS Distributions 
3490*aca3beaaSApple OSS Distributions 	if (++inm->inm_refcount == 0) {
3491*aca3beaaSApple OSS Distributions 		panic("%s: inm=%p wraparound refcnt", __func__, inm);
3492*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3493*aca3beaaSApple OSS Distributions 	} else if (inm->inm_trace != NULL) {
3494*aca3beaaSApple OSS Distributions 		(*inm->inm_trace)(inm, TRUE);
3495*aca3beaaSApple OSS Distributions 	}
3496*aca3beaaSApple OSS Distributions 	if (!locked) {
3497*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
3498*aca3beaaSApple OSS Distributions 	}
3499*aca3beaaSApple OSS Distributions }
3500*aca3beaaSApple OSS Distributions 
3501*aca3beaaSApple OSS Distributions void
inm_remref(struct in_multi * inm,int locked)3502*aca3beaaSApple OSS Distributions inm_remref(struct in_multi *inm, int locked)
3503*aca3beaaSApple OSS Distributions {
3504*aca3beaaSApple OSS Distributions 	struct ifmultiaddr *ifma;
3505*aca3beaaSApple OSS Distributions 	struct igmp_ifinfo *igi;
3506*aca3beaaSApple OSS Distributions 
3507*aca3beaaSApple OSS Distributions 	if (!locked) {
3508*aca3beaaSApple OSS Distributions 		INM_LOCK_SPIN(inm);
3509*aca3beaaSApple OSS Distributions 	} else {
3510*aca3beaaSApple OSS Distributions 		INM_LOCK_ASSERT_HELD(inm);
3511*aca3beaaSApple OSS Distributions 	}
3512*aca3beaaSApple OSS Distributions 
3513*aca3beaaSApple OSS Distributions 	if (inm->inm_refcount == 0 || (inm->inm_refcount == 1 && locked)) {
3514*aca3beaaSApple OSS Distributions 		panic("%s: inm=%p negative/missing refcnt", __func__, inm);
3515*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3516*aca3beaaSApple OSS Distributions 	} else if (inm->inm_trace != NULL) {
3517*aca3beaaSApple OSS Distributions 		(*inm->inm_trace)(inm, FALSE);
3518*aca3beaaSApple OSS Distributions 	}
3519*aca3beaaSApple OSS Distributions 
3520*aca3beaaSApple OSS Distributions 	--inm->inm_refcount;
3521*aca3beaaSApple OSS Distributions 	if (inm->inm_refcount > 0) {
3522*aca3beaaSApple OSS Distributions 		if (!locked) {
3523*aca3beaaSApple OSS Distributions 			INM_UNLOCK(inm);
3524*aca3beaaSApple OSS Distributions 		}
3525*aca3beaaSApple OSS Distributions 		return;
3526*aca3beaaSApple OSS Distributions 	}
3527*aca3beaaSApple OSS Distributions 
3528*aca3beaaSApple OSS Distributions 	/*
3529*aca3beaaSApple OSS Distributions 	 * Synchronization with in_getmulti().  In the event the inm has been
3530*aca3beaaSApple OSS Distributions 	 * detached, the underlying ifma would still be in the if_multiaddrs
3531*aca3beaaSApple OSS Distributions 	 * list, and thus can be looked up via if_addmulti().  At that point,
3532*aca3beaaSApple OSS Distributions 	 * the only way to find this inm is via ifma_protospec.  To avoid
3533*aca3beaaSApple OSS Distributions 	 * race conditions between the last inm_remref() of that inm and its
3534*aca3beaaSApple OSS Distributions 	 * use via ifma_protospec, in_multihead lock is used for serialization.
3535*aca3beaaSApple OSS Distributions 	 * In order to avoid violating the lock order, we must drop inm_lock
3536*aca3beaaSApple OSS Distributions 	 * before acquiring in_multihead lock.  To prevent the inm from being
3537*aca3beaaSApple OSS Distributions 	 * freed prematurely, we hold an extra reference.
3538*aca3beaaSApple OSS Distributions 	 */
3539*aca3beaaSApple OSS Distributions 	++inm->inm_refcount;
3540*aca3beaaSApple OSS Distributions 	INM_UNLOCK(inm);
3541*aca3beaaSApple OSS Distributions 	in_multihead_lock_shared();
3542*aca3beaaSApple OSS Distributions 	INM_LOCK_SPIN(inm);
3543*aca3beaaSApple OSS Distributions 	--inm->inm_refcount;
3544*aca3beaaSApple OSS Distributions 	if (inm->inm_refcount > 0) {
3545*aca3beaaSApple OSS Distributions 		/* We've lost the race, so abort since inm is still in use */
3546*aca3beaaSApple OSS Distributions 		INM_UNLOCK(inm);
3547*aca3beaaSApple OSS Distributions 		in_multihead_lock_done();
3548*aca3beaaSApple OSS Distributions 		/* If it was locked, return it as such */
3549*aca3beaaSApple OSS Distributions 		if (locked) {
3550*aca3beaaSApple OSS Distributions 			INM_LOCK(inm);
3551*aca3beaaSApple OSS Distributions 		}
3552*aca3beaaSApple OSS Distributions 		return;
3553*aca3beaaSApple OSS Distributions 	}
3554*aca3beaaSApple OSS Distributions 	inm_purge(inm);
3555*aca3beaaSApple OSS Distributions 	ifma = inm->inm_ifma;
3556*aca3beaaSApple OSS Distributions 	inm->inm_ifma = NULL;
3557*aca3beaaSApple OSS Distributions 	inm->inm_ifp = NULL;
3558*aca3beaaSApple OSS Distributions 	igi = inm->inm_igi;
3559*aca3beaaSApple OSS Distributions 	inm->inm_igi = NULL;
3560*aca3beaaSApple OSS Distributions 	INM_UNLOCK(inm);
3561*aca3beaaSApple OSS Distributions 	IFMA_LOCK_SPIN(ifma);
3562*aca3beaaSApple OSS Distributions 	ifma->ifma_protospec = NULL;
3563*aca3beaaSApple OSS Distributions 	IFMA_UNLOCK(ifma);
3564*aca3beaaSApple OSS Distributions 	in_multihead_lock_done();
3565*aca3beaaSApple OSS Distributions 
3566*aca3beaaSApple OSS Distributions 	in_multi_free(inm);
3567*aca3beaaSApple OSS Distributions 	if_delmulti_ifma(ifma);
3568*aca3beaaSApple OSS Distributions 	/* Release reference held to the underlying ifmultiaddr */
3569*aca3beaaSApple OSS Distributions 	IFMA_REMREF(ifma);
3570*aca3beaaSApple OSS Distributions 
3571*aca3beaaSApple OSS Distributions 	if (igi != NULL) {
3572*aca3beaaSApple OSS Distributions 		IGI_REMREF(igi);
3573*aca3beaaSApple OSS Distributions 	}
3574*aca3beaaSApple OSS Distributions }
3575*aca3beaaSApple OSS Distributions 
3576*aca3beaaSApple OSS Distributions static void
inm_trace(struct in_multi * inm,int refhold)3577*aca3beaaSApple OSS Distributions inm_trace(struct in_multi *inm, int refhold)
3578*aca3beaaSApple OSS Distributions {
3579*aca3beaaSApple OSS Distributions 	struct in_multi_dbg *inm_dbg = (struct in_multi_dbg *)inm;
3580*aca3beaaSApple OSS Distributions 	ctrace_t *tr;
3581*aca3beaaSApple OSS Distributions 	u_int32_t idx;
3582*aca3beaaSApple OSS Distributions 	u_int16_t *cnt;
3583*aca3beaaSApple OSS Distributions 
3584*aca3beaaSApple OSS Distributions 	if (!(inm->inm_debug & IFD_DEBUG)) {
3585*aca3beaaSApple OSS Distributions 		panic("%s: inm %p has no debug structure", __func__, inm);
3586*aca3beaaSApple OSS Distributions 		/* NOTREACHED */
3587*aca3beaaSApple OSS Distributions 	}
3588*aca3beaaSApple OSS Distributions 	if (refhold) {
3589*aca3beaaSApple OSS Distributions 		cnt = &inm_dbg->inm_refhold_cnt;
3590*aca3beaaSApple OSS Distributions 		tr = inm_dbg->inm_refhold;
3591*aca3beaaSApple OSS Distributions 	} else {
3592*aca3beaaSApple OSS Distributions 		cnt = &inm_dbg->inm_refrele_cnt;
3593*aca3beaaSApple OSS Distributions 		tr = inm_dbg->inm_refrele;
3594*aca3beaaSApple OSS Distributions 	}
3595*aca3beaaSApple OSS Distributions 
3596*aca3beaaSApple OSS Distributions 	idx = atomic_add_16_ov(cnt, 1) % INM_TRACE_HIST_SIZE;
3597*aca3beaaSApple OSS Distributions 	ctrace_record(&tr[idx]);
3598*aca3beaaSApple OSS Distributions }
3599*aca3beaaSApple OSS Distributions 
3600*aca3beaaSApple OSS Distributions void
in_multihead_lock_exclusive(void)3601*aca3beaaSApple OSS Distributions in_multihead_lock_exclusive(void)
3602*aca3beaaSApple OSS Distributions {
3603*aca3beaaSApple OSS Distributions 	lck_rw_lock_exclusive(&in_multihead_lock);
3604*aca3beaaSApple OSS Distributions }
3605*aca3beaaSApple OSS Distributions 
3606*aca3beaaSApple OSS Distributions void
in_multihead_lock_shared(void)3607*aca3beaaSApple OSS Distributions in_multihead_lock_shared(void)
3608*aca3beaaSApple OSS Distributions {
3609*aca3beaaSApple OSS Distributions 	lck_rw_lock_shared(&in_multihead_lock);
3610*aca3beaaSApple OSS Distributions }
3611*aca3beaaSApple OSS Distributions 
3612*aca3beaaSApple OSS Distributions void
in_multihead_lock_assert(int what)3613*aca3beaaSApple OSS Distributions in_multihead_lock_assert(int what)
3614*aca3beaaSApple OSS Distributions {
3615*aca3beaaSApple OSS Distributions #if !MACH_ASSERT
3616*aca3beaaSApple OSS Distributions #pragma unused(what)
3617*aca3beaaSApple OSS Distributions #endif
3618*aca3beaaSApple OSS Distributions 	LCK_RW_ASSERT(&in_multihead_lock, what);
3619*aca3beaaSApple OSS Distributions }
3620*aca3beaaSApple OSS Distributions 
3621*aca3beaaSApple OSS Distributions void
in_multihead_lock_done(void)3622*aca3beaaSApple OSS Distributions in_multihead_lock_done(void)
3623*aca3beaaSApple OSS Distributions {
3624*aca3beaaSApple OSS Distributions 	lck_rw_done(&in_multihead_lock);
3625*aca3beaaSApple OSS Distributions }
3626*aca3beaaSApple OSS Distributions 
3627*aca3beaaSApple OSS Distributions static struct ip_msource *
ipms_alloc(zalloc_flags_t how)3628*aca3beaaSApple OSS Distributions ipms_alloc(zalloc_flags_t how)
3629*aca3beaaSApple OSS Distributions {
3630*aca3beaaSApple OSS Distributions 	return zalloc_flags(ipms_zone, how | Z_ZERO);
3631*aca3beaaSApple OSS Distributions }
3632*aca3beaaSApple OSS Distributions 
3633*aca3beaaSApple OSS Distributions static void
ipms_free(struct ip_msource * ims)3634*aca3beaaSApple OSS Distributions ipms_free(struct ip_msource *ims)
3635*aca3beaaSApple OSS Distributions {
3636*aca3beaaSApple OSS Distributions 	zfree(ipms_zone, ims);
3637*aca3beaaSApple OSS Distributions }
3638*aca3beaaSApple OSS Distributions 
3639*aca3beaaSApple OSS Distributions static struct in_msource *
inms_alloc(zalloc_flags_t how)3640*aca3beaaSApple OSS Distributions inms_alloc(zalloc_flags_t how)
3641*aca3beaaSApple OSS Distributions {
3642*aca3beaaSApple OSS Distributions 	return zalloc_flags(inms_zone, how | Z_ZERO);
3643*aca3beaaSApple OSS Distributions }
3644*aca3beaaSApple OSS Distributions 
3645*aca3beaaSApple OSS Distributions static void
inms_free(struct in_msource * inms)3646*aca3beaaSApple OSS Distributions inms_free(struct in_msource *inms)
3647*aca3beaaSApple OSS Distributions {
3648*aca3beaaSApple OSS Distributions 	zfree(inms_zone, inms);
3649*aca3beaaSApple OSS Distributions }
3650*aca3beaaSApple OSS Distributions 
3651*aca3beaaSApple OSS Distributions #ifdef IGMP_DEBUG
3652*aca3beaaSApple OSS Distributions 
3653*aca3beaaSApple OSS Distributions static const char *inm_modestrs[] = { "un\n", "in", "ex" };
3654*aca3beaaSApple OSS Distributions 
3655*aca3beaaSApple OSS Distributions static const char *
inm_mode_str(const int mode)3656*aca3beaaSApple OSS Distributions inm_mode_str(const int mode)
3657*aca3beaaSApple OSS Distributions {
3658*aca3beaaSApple OSS Distributions 	if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) {
3659*aca3beaaSApple OSS Distributions 		return inm_modestrs[mode];
3660*aca3beaaSApple OSS Distributions 	}
3661*aca3beaaSApple OSS Distributions 	return "??";
3662*aca3beaaSApple OSS Distributions }
3663*aca3beaaSApple OSS Distributions 
3664*aca3beaaSApple OSS Distributions static const char *inm_statestrs[] = {
3665*aca3beaaSApple OSS Distributions 	"not-member\n",
3666*aca3beaaSApple OSS Distributions 	"silent\n",
3667*aca3beaaSApple OSS Distributions 	"reporting\n",
3668*aca3beaaSApple OSS Distributions 	"idle\n",
3669*aca3beaaSApple OSS Distributions 	"lazy\n",
3670*aca3beaaSApple OSS Distributions 	"sleeping\n",
3671*aca3beaaSApple OSS Distributions 	"awakening\n",
3672*aca3beaaSApple OSS Distributions 	"query-pending\n",
3673*aca3beaaSApple OSS Distributions 	"sg-query-pending\n",
3674*aca3beaaSApple OSS Distributions 	"leaving"
3675*aca3beaaSApple OSS Distributions };
3676*aca3beaaSApple OSS Distributions 
3677*aca3beaaSApple OSS Distributions static const char *
inm_state_str(const int state)3678*aca3beaaSApple OSS Distributions inm_state_str(const int state)
3679*aca3beaaSApple OSS Distributions {
3680*aca3beaaSApple OSS Distributions 	if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) {
3681*aca3beaaSApple OSS Distributions 		return inm_statestrs[state];
3682*aca3beaaSApple OSS Distributions 	}
3683*aca3beaaSApple OSS Distributions 	return "??";
3684*aca3beaaSApple OSS Distributions }
3685*aca3beaaSApple OSS Distributions 
3686*aca3beaaSApple OSS Distributions /*
3687*aca3beaaSApple OSS Distributions  * Dump an in_multi structure to the console.
3688*aca3beaaSApple OSS Distributions  */
3689*aca3beaaSApple OSS Distributions void
inm_print(const struct in_multi * inm)3690*aca3beaaSApple OSS Distributions inm_print(const struct in_multi *inm)
3691*aca3beaaSApple OSS Distributions {
3692*aca3beaaSApple OSS Distributions 	int t;
3693*aca3beaaSApple OSS Distributions 	char buf[MAX_IPv4_STR_LEN];
3694*aca3beaaSApple OSS Distributions 
3695*aca3beaaSApple OSS Distributions 	INM_LOCK_ASSERT_HELD(__DECONST(struct in_multi *, inm));
3696*aca3beaaSApple OSS Distributions 
3697*aca3beaaSApple OSS Distributions 	if (igmp_debug == 0) {
3698*aca3beaaSApple OSS Distributions 		return;
3699*aca3beaaSApple OSS Distributions 	}
3700*aca3beaaSApple OSS Distributions 
3701*aca3beaaSApple OSS Distributions 	inet_ntop(AF_INET, &inm->inm_addr, buf, sizeof(buf));
3702*aca3beaaSApple OSS Distributions 	printf("%s: --- begin inm 0x%llx ---\n", __func__,
3703*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm));
3704*aca3beaaSApple OSS Distributions 	printf("addr %s ifp 0x%llx(%s) ifma 0x%llx\n",
3705*aca3beaaSApple OSS Distributions 	    buf,
3706*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifp),
3707*aca3beaaSApple OSS Distributions 	    if_name(inm->inm_ifp),
3708*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifma));
3709*aca3beaaSApple OSS Distributions 	printf("timer %u state %s refcount %u scq.len %u\n",
3710*aca3beaaSApple OSS Distributions 	    inm->inm_timer,
3711*aca3beaaSApple OSS Distributions 	    inm_state_str(inm->inm_state),
3712*aca3beaaSApple OSS Distributions 	    inm->inm_refcount,
3713*aca3beaaSApple OSS Distributions 	    inm->inm_scq.ifq_len);
3714*aca3beaaSApple OSS Distributions 	printf("igi 0x%llx nsrc %lu sctimer %u scrv %u\n",
3715*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_igi),
3716*aca3beaaSApple OSS Distributions 	    inm->inm_nsrc,
3717*aca3beaaSApple OSS Distributions 	    inm->inm_sctimer,
3718*aca3beaaSApple OSS Distributions 	    inm->inm_scrv);
3719*aca3beaaSApple OSS Distributions 	for (t = 0; t < 2; t++) {
3720*aca3beaaSApple OSS Distributions 		printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t,
3721*aca3beaaSApple OSS Distributions 		    inm_mode_str(inm->inm_st[t].iss_fmode),
3722*aca3beaaSApple OSS Distributions 		    inm->inm_st[t].iss_asm,
3723*aca3beaaSApple OSS Distributions 		    inm->inm_st[t].iss_ex,
3724*aca3beaaSApple OSS Distributions 		    inm->inm_st[t].iss_in,
3725*aca3beaaSApple OSS Distributions 		    inm->inm_st[t].iss_rec);
3726*aca3beaaSApple OSS Distributions 	}
3727*aca3beaaSApple OSS Distributions 	printf("%s: --- end inm 0x%llx ---\n", __func__,
3728*aca3beaaSApple OSS Distributions 	    (uint64_t)VM_KERNEL_ADDRPERM(inm));
3729*aca3beaaSApple OSS Distributions }
3730*aca3beaaSApple OSS Distributions 
3731*aca3beaaSApple OSS Distributions #else
3732*aca3beaaSApple OSS Distributions 
3733*aca3beaaSApple OSS Distributions void
inm_print(__unused const struct in_multi * inm)3734*aca3beaaSApple OSS Distributions inm_print(__unused const struct in_multi *inm)
3735*aca3beaaSApple OSS Distributions {
3736*aca3beaaSApple OSS Distributions }
3737*aca3beaaSApple OSS Distributions 
3738*aca3beaaSApple OSS Distributions #endif
3739