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