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