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