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