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