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