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