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