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