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