xref: /netbsd-src/sys/netinet6/mld6.c (revision 760452d22f8440db3936c5c562afd9b32c35cc6d)
1*760452d2Sozaki-r /*	$NetBSD: mld6.c,v 1.101 2019/09/25 09:53:38 ozaki-r Exp $	*/
2e1f4f779Sitojun /*	$KAME: mld6.c,v 1.25 2001/01/16 14:14:18 itojun Exp $	*/
3cd3a345eSthorpej 
474d3c214Sitojun /*
574d3c214Sitojun  * Copyright (C) 1998 WIDE Project.
674d3c214Sitojun  * All rights reserved.
774d3c214Sitojun  *
874d3c214Sitojun  * Redistribution and use in source and binary forms, with or without
974d3c214Sitojun  * modification, are permitted provided that the following conditions
1074d3c214Sitojun  * are met:
1174d3c214Sitojun  * 1. Redistributions of source code must retain the above copyright
1274d3c214Sitojun  *    notice, this list of conditions and the following disclaimer.
1374d3c214Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
1474d3c214Sitojun  *    notice, this list of conditions and the following disclaimer in the
1574d3c214Sitojun  *    documentation and/or other materials provided with the distribution.
1674d3c214Sitojun  * 3. Neither the name of the project nor the names of its contributors
1774d3c214Sitojun  *    may be used to endorse or promote products derived from this software
1874d3c214Sitojun  *    without specific prior written permission.
1974d3c214Sitojun  *
2074d3c214Sitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2174d3c214Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2274d3c214Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2374d3c214Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2474d3c214Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2574d3c214Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2674d3c214Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2774d3c214Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2874d3c214Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2974d3c214Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3074d3c214Sitojun  * SUCH DAMAGE.
3174d3c214Sitojun  */
3274d3c214Sitojun 
3374d3c214Sitojun /*
3474d3c214Sitojun  * Copyright (c) 1992, 1993
3574d3c214Sitojun  *	The Regents of the University of California.  All rights reserved.
3674d3c214Sitojun  *
3774d3c214Sitojun  * This code is derived from software contributed to Berkeley by
3874d3c214Sitojun  * Stephen Deering of Stanford University.
3974d3c214Sitojun  *
4074d3c214Sitojun  * Redistribution and use in source and binary forms, with or without
4174d3c214Sitojun  * modification, are permitted provided that the following conditions
4274d3c214Sitojun  * are met:
4374d3c214Sitojun  * 1. Redistributions of source code must retain the above copyright
4474d3c214Sitojun  *    notice, this list of conditions and the following disclaimer.
4574d3c214Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
4674d3c214Sitojun  *    notice, this list of conditions and the following disclaimer in the
4774d3c214Sitojun  *    documentation and/or other materials provided with the distribution.
48aad01611Sagc  * 3. Neither the name of the University nor the names of its contributors
49aad01611Sagc  *    may be used to endorse or promote products derived from this software
50aad01611Sagc  *    without specific prior written permission.
51aad01611Sagc  *
52aad01611Sagc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53aad01611Sagc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54aad01611Sagc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55aad01611Sagc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56aad01611Sagc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57aad01611Sagc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58aad01611Sagc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59aad01611Sagc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60aad01611Sagc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61aad01611Sagc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62aad01611Sagc  * SUCH DAMAGE.
63aad01611Sagc  *
64aad01611Sagc  *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
65aad01611Sagc  */
66aad01611Sagc 
67aad01611Sagc /*
68aad01611Sagc  * Copyright (c) 1988 Stephen Deering.
69aad01611Sagc  *
70aad01611Sagc  * This code is derived from software contributed to Berkeley by
71aad01611Sagc  * Stephen Deering of Stanford University.
72aad01611Sagc  *
73aad01611Sagc  * Redistribution and use in source and binary forms, with or without
74aad01611Sagc  * modification, are permitted provided that the following conditions
75aad01611Sagc  * are met:
76aad01611Sagc  * 1. Redistributions of source code must retain the above copyright
77aad01611Sagc  *    notice, this list of conditions and the following disclaimer.
78aad01611Sagc  * 2. Redistributions in binary form must reproduce the above copyright
79aad01611Sagc  *    notice, this list of conditions and the following disclaimer in the
80aad01611Sagc  *    documentation and/or other materials provided with the distribution.
8174d3c214Sitojun  * 3. All advertising materials mentioning features or use of this software
8274d3c214Sitojun  *    must display the following acknowledgement:
8374d3c214Sitojun  *	This product includes software developed by the University of
8474d3c214Sitojun  *	California, Berkeley and its contributors.
8574d3c214Sitojun  * 4. Neither the name of the University nor the names of its contributors
8674d3c214Sitojun  *    may be used to endorse or promote products derived from this software
8774d3c214Sitojun  *    without specific prior written permission.
8874d3c214Sitojun  *
8974d3c214Sitojun  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
9074d3c214Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
9174d3c214Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
9274d3c214Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
9374d3c214Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
9474d3c214Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
9574d3c214Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
9674d3c214Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
9774d3c214Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
9874d3c214Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
9974d3c214Sitojun  * SUCH DAMAGE.
10074d3c214Sitojun  *
10174d3c214Sitojun  *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
10274d3c214Sitojun  */
10374d3c214Sitojun 
1044f2ad952Slukem #include <sys/cdefs.h>
105*760452d2Sozaki-r __KERNEL_RCSID(0, "$NetBSD: mld6.c,v 1.101 2019/09/25 09:53:38 ozaki-r Exp $");
1064f2ad952Slukem 
1071c4a50f1Spooka #ifdef _KERNEL_OPT
10874d3c214Sitojun #include "opt_inet.h"
1092526d8f6Sknakahara #include "opt_net_mpsafe.h"
1101c4a50f1Spooka #endif
11174d3c214Sitojun 
11274d3c214Sitojun #include <sys/param.h>
11374d3c214Sitojun #include <sys/systm.h>
11474d3c214Sitojun #include <sys/mbuf.h>
11574d3c214Sitojun #include <sys/socket.h>
11615e29e98Sad #include <sys/socketvar.h>
11774d3c214Sitojun #include <sys/syslog.h>
1188c2379fdSrpaulo #include <sys/sysctl.h>
1198c2379fdSrpaulo #include <sys/kernel.h>
1208c2379fdSrpaulo #include <sys/callout.h>
1213afd44cfStls #include <sys/cprng.h>
12273c95a6aSozaki-r #include <sys/rwlock.h>
12374d3c214Sitojun 
12474d3c214Sitojun #include <net/if.h>
12574d3c214Sitojun 
12674d3c214Sitojun #include <netinet/in.h>
12774d3c214Sitojun #include <netinet/in_var.h>
1288c2379fdSrpaulo #include <netinet6/in6_var.h>
12990736ab6Sitojun #include <netinet/ip6.h>
13074d3c214Sitojun #include <netinet6/ip6_var.h>
13178678b13Srpaulo #include <netinet6/scope6_var.h>
13290736ab6Sitojun #include <netinet/icmp6.h>
1330dd41b37Sthorpej #include <netinet6/icmp6_private.h>
13474d3c214Sitojun #include <netinet6/mld6_var.h>
13574d3c214Sitojun 
13673c95a6aSozaki-r static krwlock_t	in6_multilock __cacheline_aligned;
13773c95a6aSozaki-r 
1388c2379fdSrpaulo /*
13974d3c214Sitojun  * Protocol constants
14074d3c214Sitojun  */
14174d3c214Sitojun 
14274d3c214Sitojun /*
14374d3c214Sitojun  * time between repetitions of a node's initial report of interest in a
14474d3c214Sitojun  * multicast address(in seconds)
14574d3c214Sitojun  */
1467a574165Sitojun #define MLD_UNSOLICITED_REPORT_INTERVAL	10
14774d3c214Sitojun 
14874d3c214Sitojun static struct ip6_pktopts ip6_opts;
14974d3c214Sitojun 
1508c2379fdSrpaulo static void mld_start_listening(struct in6_multi *);
1518c2379fdSrpaulo static void mld_stop_listening(struct in6_multi *);
1528c2379fdSrpaulo 
153a39eb722Smaxv static struct mld_hdr *mld_allocbuf(struct mbuf **, struct in6_multi *, int);
1548c2379fdSrpaulo static void mld_sendpkt(struct in6_multi *, int, const struct in6_addr *);
1558c2379fdSrpaulo static void mld_starttimer(struct in6_multi *);
1568c2379fdSrpaulo static void mld_stoptimer(struct in6_multi *);
1578c2379fdSrpaulo static u_long mld_timerresid(struct in6_multi *);
15874d3c214Sitojun 
15973c95a6aSozaki-r static void in6m_ref(struct in6_multi *);
16073c95a6aSozaki-r static void in6m_unref(struct in6_multi *);
16173c95a6aSozaki-r static void in6m_destroy(struct in6_multi *);
16273c95a6aSozaki-r 
16374d3c214Sitojun void
mld_init(void)164c9395522Smatt mld_init(void)
16574d3c214Sitojun {
16674d3c214Sitojun 	static u_int8_t hbh_buf[8];
16774d3c214Sitojun 	struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
16874d3c214Sitojun 	u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
16974d3c214Sitojun 
17074d3c214Sitojun 	/* ip6h_nxt will be fill in later */
17174d3c214Sitojun 	hbh->ip6h_len = 0;	/* (8 >> 3) - 1 */
17274d3c214Sitojun 
17374d3c214Sitojun 	/* XXX: grotty hard coding... */
17474d3c214Sitojun 	hbh_buf[2] = IP6OPT_PADN;	/* 2 byte padding */
17574d3c214Sitojun 	hbh_buf[3] = 0;
17674d3c214Sitojun 	hbh_buf[4] = IP6OPT_RTALERT;
17774d3c214Sitojun 	hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
178e2cb8590Scegger 	memcpy(&hbh_buf[6], (void *)&rtalert_code, sizeof(u_int16_t));
17974d3c214Sitojun 
18074d3c214Sitojun 	ip6_opts.ip6po_hbh = hbh;
18174d3c214Sitojun 	/* We will specify the hoplimit by a multicast option. */
18274d3c214Sitojun 	ip6_opts.ip6po_hlim = -1;
1839daa8a6dSroy 	ip6_opts.ip6po_prefer_tempaddr = IP6PO_TEMPADDR_NOTPREFER;
18473c95a6aSozaki-r 
18573c95a6aSozaki-r 	rw_init(&in6_multilock);
18674d3c214Sitojun }
18774d3c214Sitojun 
1888c2379fdSrpaulo static void
mld_starttimer(struct in6_multi * in6m)18972cfe732Schristos mld_starttimer(struct in6_multi *in6m)
1908c2379fdSrpaulo {
1918c2379fdSrpaulo 	struct timeval now;
1928c2379fdSrpaulo 
19373c95a6aSozaki-r 	KASSERT(rw_write_held(&in6_multilock));
194*760452d2Sozaki-r 	KASSERTMSG(in6m->in6m_timer != IN6M_TIMER_UNDEF,
195*760452d2Sozaki-r 	    "in6m_timer=%d", in6m->in6m_timer);
196d5cdd84dSozaki-r 
1978c2379fdSrpaulo 	microtime(&now);
1988c2379fdSrpaulo 	in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz;
1998c2379fdSrpaulo 	in6m->in6m_timer_expire.tv_usec = now.tv_usec +
2008c2379fdSrpaulo 	    (in6m->in6m_timer % hz) * (1000000 / hz);
2018c2379fdSrpaulo 	if (in6m->in6m_timer_expire.tv_usec > 1000000) {
2028c2379fdSrpaulo 		in6m->in6m_timer_expire.tv_sec++;
2038c2379fdSrpaulo 		in6m->in6m_timer_expire.tv_usec -= 1000000;
2048c2379fdSrpaulo 	}
2058c2379fdSrpaulo 
2068c2379fdSrpaulo 	/* start or restart the timer */
2071e1c64c0Sjoerg 	callout_schedule(&in6m->in6m_timer_ch, in6m->in6m_timer);
2088c2379fdSrpaulo }
2098c2379fdSrpaulo 
21073c95a6aSozaki-r /*
21173c95a6aSozaki-r  * mld_stoptimer releases in6_multilock when calling callout_halt.
21273c95a6aSozaki-r  * The caller must ensure in6m won't be freed while releasing the lock.
21373c95a6aSozaki-r  */
2148c2379fdSrpaulo static void
mld_stoptimer(struct in6_multi * in6m)21572cfe732Schristos mld_stoptimer(struct in6_multi *in6m)
2168c2379fdSrpaulo {
21773c95a6aSozaki-r 
21873c95a6aSozaki-r 	KASSERT(rw_write_held(&in6_multilock));
21973c95a6aSozaki-r 
2208c2379fdSrpaulo 	if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
2218c2379fdSrpaulo 		return;
2228c2379fdSrpaulo 
22373c95a6aSozaki-r 	rw_exit(&in6_multilock);
22473c95a6aSozaki-r 
22573c95a6aSozaki-r 	callout_halt(&in6m->in6m_timer_ch, NULL);
22673c95a6aSozaki-r 
22773c95a6aSozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
2288c2379fdSrpaulo 
2298c2379fdSrpaulo 	in6m->in6m_timer = IN6M_TIMER_UNDEF;
2308c2379fdSrpaulo }
2318c2379fdSrpaulo 
2328c2379fdSrpaulo static void
mld_timeo(void * arg)2331e1c64c0Sjoerg mld_timeo(void *arg)
2348c2379fdSrpaulo {
2351e1c64c0Sjoerg 	struct in6_multi *in6m = arg;
23615e29e98Sad 
237*760452d2Sozaki-r 	KASSERTMSG(in6m->in6m_refcount > 0, "in6m_refcount=%d",
238*760452d2Sozaki-r 	    in6m->in6m_refcount);
23973c95a6aSozaki-r 
240121af298Sozaki-r 	KERNEL_LOCK_UNLESS_NET_MPSAFE();
24173c95a6aSozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
242d5cdd84dSozaki-r 	if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
243d5cdd84dSozaki-r 		goto out;
244d5cdd84dSozaki-r 
2458c2379fdSrpaulo 	in6m->in6m_timer = IN6M_TIMER_UNDEF;
2468c2379fdSrpaulo 
2478c2379fdSrpaulo 	switch (in6m->in6m_state) {
2488c2379fdSrpaulo 	case MLD_REPORTPENDING:
2498c2379fdSrpaulo 		mld_start_listening(in6m);
2508c2379fdSrpaulo 		break;
2518c2379fdSrpaulo 	default:
2528c2379fdSrpaulo 		mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
2538c2379fdSrpaulo 		break;
2548c2379fdSrpaulo 	}
2558c2379fdSrpaulo 
256d5cdd84dSozaki-r out:
25773c95a6aSozaki-r 	rw_exit(&in6_multilock);
258121af298Sozaki-r 	KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
2598c2379fdSrpaulo }
2608c2379fdSrpaulo 
2618c2379fdSrpaulo static u_long
mld_timerresid(struct in6_multi * in6m)26272cfe732Schristos mld_timerresid(struct in6_multi *in6m)
2638c2379fdSrpaulo {
2648c2379fdSrpaulo 	struct timeval now, diff;
2658c2379fdSrpaulo 
2668c2379fdSrpaulo 	microtime(&now);
2678c2379fdSrpaulo 
2688c2379fdSrpaulo 	if (now.tv_sec > in6m->in6m_timer_expire.tv_sec ||
2698c2379fdSrpaulo 	    (now.tv_sec == in6m->in6m_timer_expire.tv_sec &&
2708c2379fdSrpaulo 	    now.tv_usec > in6m->in6m_timer_expire.tv_usec)) {
2718c2379fdSrpaulo 		return (0);
2728c2379fdSrpaulo 	}
2738c2379fdSrpaulo 	diff = in6m->in6m_timer_expire;
2748c2379fdSrpaulo 	diff.tv_sec -= now.tv_sec;
2758c2379fdSrpaulo 	diff.tv_usec -= now.tv_usec;
2768c2379fdSrpaulo 	if (diff.tv_usec < 0) {
2778c2379fdSrpaulo 		diff.tv_sec--;
2788c2379fdSrpaulo 		diff.tv_usec += 1000000;
2798c2379fdSrpaulo 	}
2808c2379fdSrpaulo 
2818c2379fdSrpaulo 	/* return the remaining time in milliseconds */
28293955a2eSadrianp 	return diff.tv_sec * 1000 + diff.tv_usec / 1000;
2838c2379fdSrpaulo }
2848c2379fdSrpaulo 
2858c2379fdSrpaulo static void
mld_start_listening(struct in6_multi * in6m)28672cfe732Schristos mld_start_listening(struct in6_multi *in6m)
28774d3c214Sitojun {
28878678b13Srpaulo 	struct in6_addr all_in6;
28978678b13Srpaulo 
29073c95a6aSozaki-r 	KASSERT(rw_write_held(&in6_multilock));
29173c95a6aSozaki-r 
29274d3c214Sitojun 	/*
2931450d6e6Sitojun 	 * RFC2710 page 10:
29474d3c214Sitojun 	 * The node never sends a Report or Done for the link-scope all-nodes
29574d3c214Sitojun 	 * address.
29674d3c214Sitojun 	 * MLD messages are never sent for multicast addresses whose scope is 0
29774d3c214Sitojun 	 * (reserved) or 1 (node-local).
29874d3c214Sitojun 	 */
29978678b13Srpaulo 	all_in6 = in6addr_linklocal_allnodes;
30078678b13Srpaulo 	if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
30178678b13Srpaulo 		/* XXX: this should not happen! */
30278678b13Srpaulo 		in6m->in6m_timer = 0;
30378678b13Srpaulo 		in6m->in6m_state = MLD_OTHERLISTENER;
30478678b13Srpaulo 	}
30578678b13Srpaulo 	if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
30674d3c214Sitojun 	    IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
3078c2379fdSrpaulo 		in6m->in6m_timer = IN6M_TIMER_UNDEF;
3087a574165Sitojun 		in6m->in6m_state = MLD_OTHERLISTENER;
30974d3c214Sitojun 	} else {
3108c2379fdSrpaulo 		mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
3113afd44cfStls 		in6m->in6m_timer = cprng_fast32() %
3128c2379fdSrpaulo 		    (MLD_UNSOLICITED_REPORT_INTERVAL * hz);
3137a574165Sitojun 		in6m->in6m_state = MLD_IREPORTEDLAST;
3148c2379fdSrpaulo 
3158c2379fdSrpaulo 		mld_starttimer(in6m);
31674d3c214Sitojun 	}
31774d3c214Sitojun }
31874d3c214Sitojun 
3198c2379fdSrpaulo static void
mld_stop_listening(struct in6_multi * in6m)32072cfe732Schristos mld_stop_listening(struct in6_multi *in6m)
32174d3c214Sitojun {
32278678b13Srpaulo 	struct in6_addr allnode, allrouter;
32378678b13Srpaulo 
32473c95a6aSozaki-r 	KASSERT(rw_lock_held(&in6_multilock));
32573c95a6aSozaki-r 
32678678b13Srpaulo 	allnode = in6addr_linklocal_allnodes;
32778678b13Srpaulo 	if (in6_setscope(&allnode, in6m->in6m_ifp, NULL)) {
32878678b13Srpaulo 		/* XXX: this should not happen! */
32978678b13Srpaulo 		return;
33078678b13Srpaulo 	}
33178678b13Srpaulo 	allrouter = in6addr_linklocal_allrouters;
33278678b13Srpaulo 	if (in6_setscope(&allrouter, in6m->in6m_ifp, NULL)) {
33378678b13Srpaulo 		/* XXX impossible */
33478678b13Srpaulo 		return;
33578678b13Srpaulo 	}
33674d3c214Sitojun 
3377a574165Sitojun 	if (in6m->in6m_state == MLD_IREPORTEDLAST &&
33878678b13Srpaulo 	    (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode)) &&
33978678b13Srpaulo 	    IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
34078678b13Srpaulo 	    IPV6_ADDR_SCOPE_INTFACELOCAL) {
3418c2379fdSrpaulo 		mld_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
34278678b13Srpaulo 	}
34374d3c214Sitojun }
34474d3c214Sitojun 
34574d3c214Sitojun void
mld_input(struct mbuf * m,int off)34672cfe732Schristos mld_input(struct mbuf *m, int off)
34774d3c214Sitojun {
348423044e3Sdholland 	struct ip6_hdr *ip6;
3497a574165Sitojun 	struct mld_hdr *mldh;
350fe6d4275Sozaki-r 	struct ifnet *ifp;
3518c2379fdSrpaulo 	struct in6_multi *in6m = NULL;
35278678b13Srpaulo 	struct in6_addr mld_addr, all_in6;
35393955a2eSadrianp 	u_long timer = 0;	/* timer value in the MLD query header */
35400a9cf74Sozaki-r 	struct psref psref;
35574d3c214Sitojun 
35600a9cf74Sozaki-r 	ifp = m_get_rcvif_psref(m, &psref);
35757c38b28Sozaki-r 	if (__predict_false(ifp == NULL))
35857c38b28Sozaki-r 		goto out;
3597a574165Sitojun 	IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
3601450d6e6Sitojun 	if (mldh == NULL) {
3610dd41b37Sthorpej 		ICMP6_STATINC(ICMP6_STAT_TOOSHORT);
362fe6d4275Sozaki-r 		goto out_nodrop;
3631450d6e6Sitojun 	}
3641450d6e6Sitojun 
365a39eb722Smaxv 	ip6 = mtod(m, struct ip6_hdr *);
366a39eb722Smaxv 
367e1f4f779Sitojun 	/* source address validation */
368e1f4f779Sitojun 	if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
369e1f4f779Sitojun 		/*
3708c2379fdSrpaulo 		 * RFC3590 allows the IPv6 unspecified address as the source
3718c2379fdSrpaulo 		 * address of MLD report and done messages.  However, as this
3728c2379fdSrpaulo 		 * same document says, this special rule is for snooping
3738c2379fdSrpaulo 		 * switches and the RFC requires routers to discard MLD packets
3748c2379fdSrpaulo 		 * with the unspecified source address.  The RFC only talks
3758c2379fdSrpaulo 		 * about hosts receiving an MLD query or report in Security
3768c2379fdSrpaulo 		 * Considerations, but this is probably the correct intention.
3778c2379fdSrpaulo 		 * RFC3590 does not talk about other cases than link-local and
3788c2379fdSrpaulo 		 * the unspecified source addresses, but we believe the same
3798c2379fdSrpaulo 		 * rule should be applied.
3808c2379fdSrpaulo 		 * As a result, we only allow link-local addresses as the
3818c2379fdSrpaulo 		 * source address; otherwise, simply discard the packet.
382e1f4f779Sitojun 		 */
3838c2379fdSrpaulo #if 0
3848c2379fdSrpaulo 		/*
3858c2379fdSrpaulo 		 * XXX: do not log in an input path to avoid log flooding,
3868c2379fdSrpaulo 		 * though RFC3590 says "SHOULD log" if the source of a query
3878c2379fdSrpaulo 		 * is the unspecified address.
3888c2379fdSrpaulo 		 */
38928f4c24cSryo 		char ip6bufs[INET6_ADDRSTRLEN];
39028f4c24cSryo 		char ip6bufm[INET6_ADDRSTRLEN];
3918c2379fdSrpaulo 		log(LOG_INFO,
3928c2379fdSrpaulo 		    "mld_input: src %s is not link-local (grp=%s)\n",
39335561f6bSchristos 		    IN6_PRINT(ip6bufs,&ip6->ip6_src),
39435561f6bSchristos 		    IN6_PRINT(ip6bufm, &mldh->mld_addr));
3958c2379fdSrpaulo #endif
396fe6d4275Sozaki-r 		goto out;
397e1f4f779Sitojun 	}
398e1f4f779Sitojun 
39974d3c214Sitojun 	/*
40078678b13Srpaulo 	 * make a copy for local work (in6_setscope() may modify the 1st arg)
40178678b13Srpaulo 	 */
40278678b13Srpaulo 	mld_addr = mldh->mld_addr;
40378678b13Srpaulo 	if (in6_setscope(&mld_addr, ifp, NULL)) {
40478678b13Srpaulo 		/* XXX: this should not happen! */
405fe6d4275Sozaki-r 		goto out;
40678678b13Srpaulo 	}
40778678b13Srpaulo 
40878678b13Srpaulo 	/*
4098c2379fdSrpaulo 	 * In the MLD specification, there are 3 states and a flag.
41074d3c214Sitojun 	 *
41174d3c214Sitojun 	 * In Non-Listener state, we simply don't have a membership record.
41274d3c214Sitojun 	 * In Delaying Listener state, our timer is running (in6m->in6m_timer)
4138c2379fdSrpaulo 	 * In Idle Listener state, our timer is not running
4148c2379fdSrpaulo 	 * (in6m->in6m_timer==IN6M_TIMER_UNDEF)
41574d3c214Sitojun 	 *
4167a574165Sitojun 	 * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
4177a574165Sitojun 	 * we have heard a report from another member, or MLD_IREPORTEDLAST
41874d3c214Sitojun 	 * if we sent the last report.
41974d3c214Sitojun 	 */
4207a574165Sitojun 	switch (mldh->mld_type) {
421a403cbd4Sozaki-r 	case MLD_LISTENER_QUERY: {
42273c95a6aSozaki-r 		struct in6_multi *next;
42373c95a6aSozaki-r 
42474d3c214Sitojun 		if (ifp->if_flags & IFF_LOOPBACK)
42574d3c214Sitojun 			break;
42674d3c214Sitojun 
42778678b13Srpaulo 		if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
42878678b13Srpaulo 		    !IN6_IS_ADDR_MULTICAST(&mld_addr))
42974d3c214Sitojun 			break;	/* print error or log stat? */
43078678b13Srpaulo 
43178678b13Srpaulo 		all_in6 = in6addr_linklocal_allnodes;
43278678b13Srpaulo 		if (in6_setscope(&all_in6, ifp, NULL)) {
43378678b13Srpaulo 			/* XXX: this should not happen! */
43478678b13Srpaulo 			break;
43578678b13Srpaulo 		}
43674d3c214Sitojun 
43774d3c214Sitojun 		/*
43874d3c214Sitojun 		 * - Start the timers in all of our membership records
43974d3c214Sitojun 		 *   that the query applies to for the interface on
44074d3c214Sitojun 		 *   which the query arrived excl. those that belong
44174d3c214Sitojun 		 *   to the "all-nodes" group (ff02::1).
44274d3c214Sitojun 		 * - Restart any timer that is already running but has
443eb35daf5Srpaulo 		 *   a value longer than the requested timeout.
44474d3c214Sitojun 		 * - Use the value specified in the query message as
44574d3c214Sitojun 		 *   the maximum timeout.
44674d3c214Sitojun 		 */
4478c2379fdSrpaulo 		timer = ntohs(mldh->mld_maxdelay);
4488c2379fdSrpaulo 
44973c95a6aSozaki-r 		rw_enter(&in6_multilock, RW_WRITER);
45073c95a6aSozaki-r 		/*
45173c95a6aSozaki-r 		 * mld_stoptimer and mld_sendpkt release in6_multilock
45273c95a6aSozaki-r 		 * temporarily, so we have to prevent in6m from being freed
45373c95a6aSozaki-r 		 * while releasing the lock by having an extra reference to it.
45473c95a6aSozaki-r 		 *
45573c95a6aSozaki-r 		 * Also in6_purge_multi might remove items from the list of the
45673c95a6aSozaki-r 		 * ifp while releasing the lock. Fortunately in6_purge_multi is
45773c95a6aSozaki-r 		 * never executed as long as we have a psref of the ifp.
45873c95a6aSozaki-r 		 */
45973c95a6aSozaki-r 		LIST_FOREACH_SAFE(in6m, &ifp->if_multiaddrs, in6m_entry, next) {
46078678b13Srpaulo 			if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
46174d3c214Sitojun 			    IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
46274d3c214Sitojun 			    IPV6_ADDR_SCOPE_LINKLOCAL)
46374d3c214Sitojun 				continue;
46474d3c214Sitojun 
4658c2379fdSrpaulo 			if (in6m->in6m_state == MLD_REPORTPENDING)
4668c2379fdSrpaulo 				continue; /* we are not yet ready */
4678c2379fdSrpaulo 
4688c2379fdSrpaulo 			if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
4698c2379fdSrpaulo 			    !IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr))
4708c2379fdSrpaulo 				continue;
4718c2379fdSrpaulo 
47274d3c214Sitojun 			if (timer == 0) {
47373c95a6aSozaki-r 				in6m_ref(in6m);
47473c95a6aSozaki-r 
47574d3c214Sitojun 				/* send a report immediately */
4768c2379fdSrpaulo 				mld_stoptimer(in6m);
4778c2379fdSrpaulo 				mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
4787a574165Sitojun 				in6m->in6m_state = MLD_IREPORTEDLAST;
47973c95a6aSozaki-r 
48073c95a6aSozaki-r 				in6m_unref(in6m); /* May free in6m */
4818c2379fdSrpaulo 			} else if (in6m->in6m_timer == IN6M_TIMER_UNDEF ||
48293955a2eSadrianp 			    mld_timerresid(in6m) > timer) {
48393955a2eSadrianp 				in6m->in6m_timer =
4843afd44cfStls 				   1 + (cprng_fast32() % timer) * hz / 1000;
4858c2379fdSrpaulo 				mld_starttimer(in6m);
48674d3c214Sitojun 			}
48774d3c214Sitojun 		}
48873c95a6aSozaki-r 		rw_exit(&in6_multilock);
48974d3c214Sitojun 		break;
490a403cbd4Sozaki-r 	    }
49178678b13Srpaulo 
4927a574165Sitojun 	case MLD_LISTENER_REPORT:
49374d3c214Sitojun 		/*
49474d3c214Sitojun 		 * For fast leave to work, we have to know that we are the
49574d3c214Sitojun 		 * last person to send a report for this group.  Reports
49674d3c214Sitojun 		 * can potentially get looped back if we are a multicast
49774d3c214Sitojun 		 * router, so discard reports sourced by me.
49874d3c214Sitojun 		 * Note that it is impossible to check IFF_LOOPBACK flag of
49974d3c214Sitojun 		 * ifp for this purpose, since ip6_mloopback pass the physical
50074d3c214Sitojun 		 * interface to looutput.
50174d3c214Sitojun 		 */
50274d3c214Sitojun 		if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
50374d3c214Sitojun 			break;
50474d3c214Sitojun 
5057a574165Sitojun 		if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
50674d3c214Sitojun 			break;
50774d3c214Sitojun 
50874d3c214Sitojun 		/*
50974d3c214Sitojun 		 * If we belong to the group being reported, stop
51074d3c214Sitojun 		 * our timer for that group.
51174d3c214Sitojun 		 */
51273c95a6aSozaki-r 		rw_enter(&in6_multilock, RW_WRITER);
51340914f01Sozaki-r 		in6m = in6_lookup_multi(&mld_addr, ifp);
51474d3c214Sitojun 		if (in6m) {
51573c95a6aSozaki-r 			in6m_ref(in6m);
5168c2379fdSrpaulo 			mld_stoptimer(in6m); /* transit to idle state */
5177a574165Sitojun 			in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
51873c95a6aSozaki-r 			in6m_unref(in6m);
51973c95a6aSozaki-r 			in6m = NULL; /* in6m might be freed */
52074d3c214Sitojun 		}
52173c95a6aSozaki-r 		rw_exit(&in6_multilock);
52274d3c214Sitojun 		break;
52374d3c214Sitojun 	default:		/* this is impossible */
524b0e82d30Sitojun #if 0
5256d8d0d63Sitojun 		/*
5266d8d0d63Sitojun 		 * this case should be impossible because of filtering in
5276d8d0d63Sitojun 		 * icmp6_input().  But we explicitly disabled this part
5286d8d0d63Sitojun 		 * just in case.
5296d8d0d63Sitojun 		 */
5308c2379fdSrpaulo 		log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
531b0e82d30Sitojun #endif
53274d3c214Sitojun 		break;
53374d3c214Sitojun 	}
5341450d6e6Sitojun 
535fe6d4275Sozaki-r out:
5361450d6e6Sitojun 	m_freem(m);
537fe6d4275Sozaki-r out_nodrop:
53800a9cf74Sozaki-r 	m_put_rcvif_psref(ifp, &psref);
53974d3c214Sitojun }
54074d3c214Sitojun 
54173c95a6aSozaki-r /*
54273c95a6aSozaki-r  * XXX mld_sendpkt must be called with in6_multilock held and
54373c95a6aSozaki-r  * will release in6_multilock before calling ip6_output and
54473c95a6aSozaki-r  * returning to avoid locking against myself in ip6_output.
54573c95a6aSozaki-r  */
54674d3c214Sitojun static void
mld_sendpkt(struct in6_multi * in6m,int type,const struct in6_addr * dst)547a39eb722Smaxv mld_sendpkt(struct in6_multi *in6m, int type, const struct in6_addr *dst)
54874d3c214Sitojun {
5498c2379fdSrpaulo 	struct mbuf *mh;
5507a574165Sitojun 	struct mld_hdr *mldh;
5518c2379fdSrpaulo 	struct ip6_hdr *ip6 = NULL;
55274d3c214Sitojun 	struct ip6_moptions im6o;
5538c2379fdSrpaulo 	struct in6_ifaddr *ia = NULL;
55474d3c214Sitojun 	struct ifnet *ifp = in6m->in6m_ifp;
5556d8d0d63Sitojun 	int ignflags;
556a403cbd4Sozaki-r 	struct psref psref;
557a403cbd4Sozaki-r 	int bound;
55874d3c214Sitojun 
55973c95a6aSozaki-r 	KASSERT(rw_write_held(&in6_multilock));
56073c95a6aSozaki-r 
56174d3c214Sitojun 	/*
56274d3c214Sitojun 	 * At first, find a link local address on the outgoing interface
56374d3c214Sitojun 	 * to use as the source address of the MLD packet.
5646d8d0d63Sitojun 	 * We do not reject tentative addresses for MLD report to deal with
5656d8d0d63Sitojun 	 * the case where we first join a link-local address.
56674d3c214Sitojun 	 */
5676d8d0d63Sitojun 	ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
568a403cbd4Sozaki-r 	bound = curlwp_bind();
569a403cbd4Sozaki-r 	ia = in6ifa_ifpforlinklocal_psref(ifp, ignflags, &psref);
570a403cbd4Sozaki-r 	if (ia == NULL) {
571a403cbd4Sozaki-r 		curlwp_bindx(bound);
57274d3c214Sitojun 		return;
573a403cbd4Sozaki-r 	}
574a403cbd4Sozaki-r 	if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) {
575a403cbd4Sozaki-r 		ia6_release(ia, &psref);
5766d8d0d63Sitojun 		ia = NULL;
577a403cbd4Sozaki-r 	}
57874d3c214Sitojun 
5798c2379fdSrpaulo 	/* Allocate two mbufs to store IPv6 header and MLD header */
580a39eb722Smaxv 	mldh = mld_allocbuf(&mh, in6m, type);
581a403cbd4Sozaki-r 	if (mldh == NULL) {
582a403cbd4Sozaki-r 		ia6_release(ia, &psref);
583a403cbd4Sozaki-r 		curlwp_bindx(bound);
58474d3c214Sitojun 		return;
585a403cbd4Sozaki-r 	}
58674d3c214Sitojun 
5878c2379fdSrpaulo 	/* fill src/dst here */
58874d3c214Sitojun 	ip6 = mtod(mh, struct ip6_hdr *);
5896d8d0d63Sitojun 	ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
59074d3c214Sitojun 	ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
591a403cbd4Sozaki-r 	ia6_release(ia, &psref);
592a403cbd4Sozaki-r 	curlwp_bindx(bound);
59374d3c214Sitojun 
5947a574165Sitojun 	mldh->mld_addr = in6m->in6m_addr;
59578678b13Srpaulo 	in6_clearscope(&mldh->mld_addr); /* XXX */
5967a574165Sitojun 	mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
5977a574165Sitojun 	    sizeof(struct mld_hdr));
59874d3c214Sitojun 
59974d3c214Sitojun 	/* construct multicast option */
6008c2379fdSrpaulo 	memset(&im6o, 0, sizeof(im6o));
60143c5ab37Sozaki-r 	im6o.im6o_multicast_if_index = if_get_index(ifp);
60274d3c214Sitojun 	im6o.im6o_multicast_hlim = 1;
60374d3c214Sitojun 
60474d3c214Sitojun 	/*
60574d3c214Sitojun 	 * Request loopback of the report if we are acting as a multicast
60674d3c214Sitojun 	 * router, so that the process-level routing daemon can hear it.
60774d3c214Sitojun 	 */
60874d3c214Sitojun 	im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
60974d3c214Sitojun 
610a39eb722Smaxv 	/* increment output statistics */
6110dd41b37Sthorpej 	ICMP6_STATINC(ICMP6_STAT_OUTHIST + type);
61251a9c759Sitojun 	icmp6_ifstat_inc(ifp, ifs6_out_msg);
613ea861f01Sitojun 	switch (type) {
6147a574165Sitojun 	case MLD_LISTENER_QUERY:
61551a9c759Sitojun 		icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
616ea861f01Sitojun 		break;
6177a574165Sitojun 	case MLD_LISTENER_REPORT:
61851a9c759Sitojun 		icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
619ea861f01Sitojun 		break;
6207a574165Sitojun 	case MLD_LISTENER_DONE:
62151a9c759Sitojun 		icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
622ea861f01Sitojun 		break;
623ea861f01Sitojun 	}
6246d8d0d63Sitojun 
62573c95a6aSozaki-r 	/* XXX we cannot call ip6_output with holding in6_multilock */
62673c95a6aSozaki-r 	rw_exit(&in6_multilock);
62773c95a6aSozaki-r 
62890266995Sjonathan 	ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC,
6297f3d4048Splunky 	    &im6o, NULL, NULL);
63073c95a6aSozaki-r 
63173c95a6aSozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
632ea861f01Sitojun }
6338c2379fdSrpaulo 
6348c2379fdSrpaulo static struct mld_hdr *
mld_allocbuf(struct mbuf ** mh,struct in6_multi * in6m,int type)635a39eb722Smaxv mld_allocbuf(struct mbuf **mh, struct in6_multi *in6m, int type)
6368c2379fdSrpaulo {
6378c2379fdSrpaulo 	struct mbuf *md;
6388c2379fdSrpaulo 	struct mld_hdr *mldh;
6398c2379fdSrpaulo 	struct ip6_hdr *ip6;
6408c2379fdSrpaulo 
6418c2379fdSrpaulo 	/*
6428c2379fdSrpaulo 	 * Allocate mbufs to store ip6 header and MLD header.
6438c2379fdSrpaulo 	 * We allocate 2 mbufs and make chain in advance because
6448c2379fdSrpaulo 	 * it is more convenient when inserting the hop-by-hop option later.
6458c2379fdSrpaulo 	 */
6468c2379fdSrpaulo 	MGETHDR(*mh, M_DONTWAIT, MT_HEADER);
6478c2379fdSrpaulo 	if (*mh == NULL)
6488c2379fdSrpaulo 		return NULL;
6498c2379fdSrpaulo 	MGET(md, M_DONTWAIT, MT_DATA);
6508c2379fdSrpaulo 	if (md == NULL) {
6518c2379fdSrpaulo 		m_free(*mh);
6528c2379fdSrpaulo 		*mh = NULL;
6538c2379fdSrpaulo 		return NULL;
6548c2379fdSrpaulo 	}
6558c2379fdSrpaulo 	(*mh)->m_next = md;
6568c2379fdSrpaulo 	md->m_next = NULL;
6578c2379fdSrpaulo 
658d938d837Sozaki-r 	m_reset_rcvif((*mh));
659a39eb722Smaxv 	(*mh)->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
6608c2379fdSrpaulo 	(*mh)->m_len = sizeof(struct ip6_hdr);
6615b040abeSmaxv 	m_align(*mh, sizeof(struct ip6_hdr));
6628c2379fdSrpaulo 
6638c2379fdSrpaulo 	/* fill in the ip6 header */
6648c2379fdSrpaulo 	ip6 = mtod(*mh, struct ip6_hdr *);
6658c2379fdSrpaulo 	memset(ip6, 0, sizeof(*ip6));
6668c2379fdSrpaulo 	ip6->ip6_flow = 0;
6678c2379fdSrpaulo 	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
6688c2379fdSrpaulo 	ip6->ip6_vfc |= IPV6_VERSION;
6698c2379fdSrpaulo 	/* ip6_plen will be set later */
6708c2379fdSrpaulo 	ip6->ip6_nxt = IPPROTO_ICMPV6;
6718c2379fdSrpaulo 	/* ip6_hlim will be set by im6o.im6o_multicast_hlim */
6728c2379fdSrpaulo 	/* ip6_src/dst will be set by mld_sendpkt() or mld_sendbuf() */
6738c2379fdSrpaulo 
6748c2379fdSrpaulo 	/* fill in the MLD header as much as possible */
675a39eb722Smaxv 	md->m_len = sizeof(struct mld_hdr);
6768c2379fdSrpaulo 	mldh = mtod(md, struct mld_hdr *);
677a39eb722Smaxv 	memset(mldh, 0, sizeof(struct mld_hdr));
6788c2379fdSrpaulo 	mldh->mld_type = type;
6798c2379fdSrpaulo 	return mldh;
6808c2379fdSrpaulo }
6818c2379fdSrpaulo 
68273c95a6aSozaki-r static void
in6m_ref(struct in6_multi * in6m)68373c95a6aSozaki-r in6m_ref(struct in6_multi *in6m)
68473c95a6aSozaki-r {
68573c95a6aSozaki-r 
68673c95a6aSozaki-r 	KASSERT(rw_write_held(&in6_multilock));
68773c95a6aSozaki-r 	in6m->in6m_refcount++;
68873c95a6aSozaki-r }
68973c95a6aSozaki-r 
69073c95a6aSozaki-r static void
in6m_unref(struct in6_multi * in6m)69173c95a6aSozaki-r in6m_unref(struct in6_multi *in6m)
69273c95a6aSozaki-r {
69373c95a6aSozaki-r 
69473c95a6aSozaki-r 	KASSERT(rw_write_held(&in6_multilock));
69573c95a6aSozaki-r 	if (--in6m->in6m_refcount == 0)
69673c95a6aSozaki-r 		in6m_destroy(in6m);
69773c95a6aSozaki-r }
69873c95a6aSozaki-r 
6998c2379fdSrpaulo /*
7008c2379fdSrpaulo  * Add an address to the list of IP6 multicast addresses for a given interface.
7018c2379fdSrpaulo  */
7028c2379fdSrpaulo struct	in6_multi *
in6_addmulti(struct in6_addr * maddr6,struct ifnet * ifp,int * errorp,int timer)703a39eb722Smaxv in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, int *errorp,
704a39eb722Smaxv     int timer)
7058c2379fdSrpaulo {
706386d3978Sdyoung 	struct	sockaddr_in6 sin6;
7078c2379fdSrpaulo 	struct	in6_multi *in6m;
7088c2379fdSrpaulo 
7098c2379fdSrpaulo 	*errorp = 0;
7108c2379fdSrpaulo 
71173c95a6aSozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
7128c2379fdSrpaulo 	/*
7138c2379fdSrpaulo 	 * See if address already in list.
7148c2379fdSrpaulo 	 */
71540914f01Sozaki-r 	in6m = in6_lookup_multi(maddr6, ifp);
7168c2379fdSrpaulo 	if (in6m != NULL) {
7178c2379fdSrpaulo 		/*
718a39eb722Smaxv 		 * Found it; just increment the reference count.
7198c2379fdSrpaulo 		 */
7208c2379fdSrpaulo 		in6m->in6m_refcount++;
7218c2379fdSrpaulo 	} else {
7228c2379fdSrpaulo 		/*
7238c2379fdSrpaulo 		 * New address; allocate a new multicast record
7248c2379fdSrpaulo 		 * and link it into the interface's multicast list.
7258c2379fdSrpaulo 		 */
726a39eb722Smaxv 		in6m = malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT|M_ZERO);
7278c2379fdSrpaulo 		if (in6m == NULL) {
7288c2379fdSrpaulo 			*errorp = ENOBUFS;
72973c95a6aSozaki-r 			goto out;
7308c2379fdSrpaulo 		}
7318c2379fdSrpaulo 
7328c2379fdSrpaulo 		in6m->in6m_addr = *maddr6;
7338c2379fdSrpaulo 		in6m->in6m_ifp = ifp;
7348c2379fdSrpaulo 		in6m->in6m_refcount = 1;
7358c2379fdSrpaulo 		in6m->in6m_timer = IN6M_TIMER_UNDEF;
736a3e16650Sjoerg 		callout_init(&in6m->in6m_timer_ch, CALLOUT_MPSAFE);
737a3e16650Sjoerg 		callout_setfunc(&in6m->in6m_timer_ch, mld_timeo, in6m);
738a3e16650Sjoerg 
73900a9cf74Sozaki-r 		LIST_INSERT_HEAD(&ifp->if_multiaddrs, in6m, in6m_entry);
7408c2379fdSrpaulo 
7418c2379fdSrpaulo 		/*
7428c2379fdSrpaulo 		 * Ask the network driver to update its multicast reception
7438c2379fdSrpaulo 		 * filter appropriately for the new address.
7448c2379fdSrpaulo 		 */
745386d3978Sdyoung 		sockaddr_in6_init(&sin6, maddr6, 0, 0, 0);
746386d3978Sdyoung 		*errorp = if_mcast_op(ifp, SIOCADDMULTI, sin6tosa(&sin6));
7478c2379fdSrpaulo 		if (*errorp) {
748a3e16650Sjoerg 			callout_destroy(&in6m->in6m_timer_ch);
7498c2379fdSrpaulo 			LIST_REMOVE(in6m, in6m_entry);
7508c2379fdSrpaulo 			free(in6m, M_IPMADDR);
75173c95a6aSozaki-r 			in6m = NULL;
75273c95a6aSozaki-r 			goto out;
7538c2379fdSrpaulo 		}
7548c2379fdSrpaulo 
755941ce916Srpaulo 		in6m->in6m_timer = timer;
7568c2379fdSrpaulo 		if (in6m->in6m_timer > 0) {
7578c2379fdSrpaulo 			in6m->in6m_state = MLD_REPORTPENDING;
7588c2379fdSrpaulo 			mld_starttimer(in6m);
75973c95a6aSozaki-r 			goto out;
7608c2379fdSrpaulo 		}
7618c2379fdSrpaulo 
7628c2379fdSrpaulo 		/*
7638c2379fdSrpaulo 		 * Let MLD6 know that we have joined a new IP6 multicast
7648c2379fdSrpaulo 		 * group.
7658c2379fdSrpaulo 		 */
7668c2379fdSrpaulo 		mld_start_listening(in6m);
7678c2379fdSrpaulo 	}
76873c95a6aSozaki-r out:
76973c95a6aSozaki-r 	rw_exit(&in6_multilock);
77073c95a6aSozaki-r 	return in6m;
7718c2379fdSrpaulo }
7728c2379fdSrpaulo 
77373c95a6aSozaki-r static void
in6m_destroy(struct in6_multi * in6m)77473c95a6aSozaki-r in6m_destroy(struct in6_multi *in6m)
7758c2379fdSrpaulo {
77673c95a6aSozaki-r 	struct sockaddr_in6 sin6;
7778c2379fdSrpaulo 
77873c95a6aSozaki-r 	KASSERT(rw_write_held(&in6_multilock));
779*760452d2Sozaki-r 	KASSERTMSG(in6m->in6m_refcount == 0, "in6m_refcount=%d",
780*760452d2Sozaki-r 	    in6m->in6m_refcount);
7814f21a427Sozaki-r 
7828c2379fdSrpaulo 	/*
7832980d4c8Sozaki-r 	 * Unlink from list if it's listed.  This must be done before
7842980d4c8Sozaki-r 	 * mld_stop_listening because it releases in6_multilock and that allows
7852980d4c8Sozaki-r 	 * someone to look up the removing in6m from the list and add a
7862980d4c8Sozaki-r 	 * reference to the entry unexpectedly.
78760862c94Sozaki-r 	 */
7882980d4c8Sozaki-r 	if (in6_lookup_multi(&in6m->in6m_addr, in6m->in6m_ifp) != NULL)
78960862c94Sozaki-r 		LIST_REMOVE(in6m, in6m_entry);
79060862c94Sozaki-r 
79160862c94Sozaki-r 	/*
7928c2379fdSrpaulo 	 * No remaining claims to this record; let MLD6 know
7938c2379fdSrpaulo 	 * that we are leaving the multicast group.
7948c2379fdSrpaulo 	 */
7958c2379fdSrpaulo 	mld_stop_listening(in6m);
7968c2379fdSrpaulo 
7978c2379fdSrpaulo 	/*
7988c2379fdSrpaulo 	 * Delete all references of this multicasting group from
7998c2379fdSrpaulo 	 * the membership arrays
8008c2379fdSrpaulo 	 */
801549f799fSozaki-r 	in6_purge_mcast_references(in6m);
8028c2379fdSrpaulo 
8038c2379fdSrpaulo 	/*
8048c2379fdSrpaulo 	 * Notify the network driver to update its multicast
8058c2379fdSrpaulo 	 * reception filter.
8068c2379fdSrpaulo 	 */
807386d3978Sdyoung 	sockaddr_in6_init(&sin6, &in6m->in6m_addr, 0, 0, 0);
808386d3978Sdyoung 	if_mcast_op(in6m->in6m_ifp, SIOCDELMULTI, sin6tosa(&sin6));
809d5cdd84dSozaki-r 
810d5cdd84dSozaki-r 	/* Tell mld_timeo we're halting the timer */
811d5cdd84dSozaki-r 	in6m->in6m_timer = IN6M_TIMER_UNDEF;
812121af298Sozaki-r 
813b0a3f08aSozaki-r 	rw_exit(&in6_multilock);
8149e8d969cSozaki-r 	callout_halt(&in6m->in6m_timer_ch, NULL);
8151e1c64c0Sjoerg 	callout_destroy(&in6m->in6m_timer_ch);
816d5cdd84dSozaki-r 
8178c2379fdSrpaulo 	free(in6m, M_IPMADDR);
818b0a3f08aSozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
8198c2379fdSrpaulo }
82073c95a6aSozaki-r 
82173c95a6aSozaki-r /*
82273c95a6aSozaki-r  * Delete a multicast address record.
82373c95a6aSozaki-r  */
82473c95a6aSozaki-r void
in6_delmulti_locked(struct in6_multi * in6m)8250c65aee4Sozaki-r in6_delmulti_locked(struct in6_multi *in6m)
82673c95a6aSozaki-r {
82773c95a6aSozaki-r 
8280c65aee4Sozaki-r 	KASSERT(rw_write_held(&in6_multilock));
829*760452d2Sozaki-r 	KASSERTMSG(in6m->in6m_refcount > 0, "in6m_refcount=%d",
830*760452d2Sozaki-r 	    in6m->in6m_refcount);
83173c95a6aSozaki-r 
83273c95a6aSozaki-r 	/*
83373c95a6aSozaki-r 	 * The caller should have a reference to in6m. So we don't need to care
83473c95a6aSozaki-r 	 * of releasing the lock in mld_stoptimer.
83573c95a6aSozaki-r 	 */
83673c95a6aSozaki-r 	mld_stoptimer(in6m);
83773c95a6aSozaki-r 	if (--in6m->in6m_refcount == 0)
83873c95a6aSozaki-r 		in6m_destroy(in6m);
8390c65aee4Sozaki-r }
8400c65aee4Sozaki-r 
8410c65aee4Sozaki-r void
in6_delmulti(struct in6_multi * in6m)8420c65aee4Sozaki-r in6_delmulti(struct in6_multi *in6m)
8430c65aee4Sozaki-r {
8440c65aee4Sozaki-r 
8450c65aee4Sozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
8460c65aee4Sozaki-r 	in6_delmulti_locked(in6m);
84773c95a6aSozaki-r 	rw_exit(&in6_multilock);
8488c2379fdSrpaulo }
8498c2379fdSrpaulo 
85000a9cf74Sozaki-r /*
85100a9cf74Sozaki-r  * Look up the in6_multi record for a given IP6 multicast address
85200a9cf74Sozaki-r  * on a given interface. If no matching record is found, "in6m"
85300a9cf74Sozaki-r  * returns NULL.
85400a9cf74Sozaki-r  */
85500a9cf74Sozaki-r struct in6_multi *
in6_lookup_multi(const struct in6_addr * addr,const struct ifnet * ifp)85600a9cf74Sozaki-r in6_lookup_multi(const struct in6_addr *addr, const struct ifnet *ifp)
85700a9cf74Sozaki-r {
85800a9cf74Sozaki-r 	struct in6_multi *in6m;
85900a9cf74Sozaki-r 
86073c95a6aSozaki-r 	KASSERT(rw_lock_held(&in6_multilock));
86173c95a6aSozaki-r 
86200a9cf74Sozaki-r 	LIST_FOREACH(in6m, &ifp->if_multiaddrs, in6m_entry) {
86300a9cf74Sozaki-r 		if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, addr))
86400a9cf74Sozaki-r 			break;
86500a9cf74Sozaki-r 	}
86600a9cf74Sozaki-r 	return in6m;
86700a9cf74Sozaki-r }
86800a9cf74Sozaki-r 
8695e90b86bSozaki-r void
in6_lookup_and_delete_multi(const struct in6_addr * addr,const struct ifnet * ifp)8705e90b86bSozaki-r in6_lookup_and_delete_multi(const struct in6_addr *addr,
8715e90b86bSozaki-r     const struct ifnet *ifp)
8725e90b86bSozaki-r {
8735e90b86bSozaki-r 	struct in6_multi *in6m;
8745e90b86bSozaki-r 
8755e90b86bSozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
8765e90b86bSozaki-r 	in6m = in6_lookup_multi(addr, ifp);
8775e90b86bSozaki-r 	if (in6m != NULL)
8785e90b86bSozaki-r 		in6_delmulti_locked(in6m);
8795e90b86bSozaki-r 	rw_exit(&in6_multilock);
8805e90b86bSozaki-r }
8815e90b86bSozaki-r 
882ef30413fSozaki-r bool
in6_multi_group(const struct in6_addr * addr,const struct ifnet * ifp)883ef30413fSozaki-r in6_multi_group(const struct in6_addr *addr, const struct ifnet *ifp)
884ef30413fSozaki-r {
885ef30413fSozaki-r 	bool ingroup;
886ef30413fSozaki-r 
88773c95a6aSozaki-r 	rw_enter(&in6_multilock, RW_READER);
888ef30413fSozaki-r 	ingroup = in6_lookup_multi(addr, ifp) != NULL;
88973c95a6aSozaki-r 	rw_exit(&in6_multilock);
890ef30413fSozaki-r 
891ef30413fSozaki-r 	return ingroup;
892ef30413fSozaki-r }
893ef30413fSozaki-r 
89400a9cf74Sozaki-r /*
89500a9cf74Sozaki-r  * Purge in6_multi records associated to the interface.
89600a9cf74Sozaki-r  */
89700a9cf74Sozaki-r void
in6_purge_multi(struct ifnet * ifp)89800a9cf74Sozaki-r in6_purge_multi(struct ifnet *ifp)
89900a9cf74Sozaki-r {
90000a9cf74Sozaki-r 	struct in6_multi *in6m, *next;
90100a9cf74Sozaki-r 
90273c95a6aSozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
90300a9cf74Sozaki-r 	LIST_FOREACH_SAFE(in6m, &ifp->if_multiaddrs, in6m_entry, next) {
904d39540d6Sozaki-r 		LIST_REMOVE(in6m, in6m_entry);
90573c95a6aSozaki-r 		/*
90673c95a6aSozaki-r 		 * Normally multicast addresses are already purged at this
90773c95a6aSozaki-r 		 * point. Remaining references aren't accessible via ifp,
90873c95a6aSozaki-r 		 * so what we can do here is to prevent ifp from being
90973c95a6aSozaki-r 		 * accessed via in6m by removing it from the list of ifp.
91073c95a6aSozaki-r 		 */
91173c95a6aSozaki-r 		mld_stoptimer(in6m);
91200a9cf74Sozaki-r 	}
91373c95a6aSozaki-r 	rw_exit(&in6_multilock);
91400a9cf74Sozaki-r }
9158c2379fdSrpaulo 
916362a23cbSozaki-r void
in6_multi_lock(int op)917362a23cbSozaki-r in6_multi_lock(int op)
918362a23cbSozaki-r {
919362a23cbSozaki-r 
920362a23cbSozaki-r 	rw_enter(&in6_multilock, op);
921362a23cbSozaki-r }
922362a23cbSozaki-r 
923362a23cbSozaki-r void
in6_multi_unlock(void)924362a23cbSozaki-r in6_multi_unlock(void)
925362a23cbSozaki-r {
926362a23cbSozaki-r 
927362a23cbSozaki-r 	rw_exit(&in6_multilock);
928362a23cbSozaki-r }
929362a23cbSozaki-r 
930f27f4e28Sozaki-r bool
in6_multi_locked(int op)931f27f4e28Sozaki-r in6_multi_locked(int op)
932f27f4e28Sozaki-r {
933f27f4e28Sozaki-r 
934f27f4e28Sozaki-r 	switch (op) {
935f27f4e28Sozaki-r 	case RW_READER:
936f27f4e28Sozaki-r 		return rw_read_held(&in6_multilock);
937f27f4e28Sozaki-r 	case RW_WRITER:
938f27f4e28Sozaki-r 		return rw_write_held(&in6_multilock);
939f27f4e28Sozaki-r 	default:
940f27f4e28Sozaki-r 		return rw_lock_held(&in6_multilock);
941f27f4e28Sozaki-r 	}
942f27f4e28Sozaki-r }
943f27f4e28Sozaki-r 
9448c2379fdSrpaulo struct in6_multi_mship *
in6_joingroup(struct ifnet * ifp,struct in6_addr * addr,int * errorp,int timer)945a39eb722Smaxv in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, int *errorp, int timer)
9468c2379fdSrpaulo {
9478c2379fdSrpaulo 	struct in6_multi_mship *imm;
9488c2379fdSrpaulo 
949bb61b360Sdyoung 	imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT|M_ZERO);
950bb61b360Sdyoung 	if (imm == NULL) {
9518c2379fdSrpaulo 		*errorp = ENOBUFS;
9528c2379fdSrpaulo 		return NULL;
9538c2379fdSrpaulo 	}
9548c2379fdSrpaulo 
955941ce916Srpaulo 	imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, timer);
9568c2379fdSrpaulo 	if (!imm->i6mm_maddr) {
9572f9c32c8Sdyoung 		/* *errorp is already set */
9588c2379fdSrpaulo 		free(imm, M_IPMADDR);
9598c2379fdSrpaulo 		return NULL;
9608c2379fdSrpaulo 	}
9618c2379fdSrpaulo 	return imm;
9628c2379fdSrpaulo }
9638c2379fdSrpaulo 
9648c2379fdSrpaulo int
in6_leavegroup(struct in6_multi_mship * imm)96572cfe732Schristos in6_leavegroup(struct in6_multi_mship *imm)
9668c2379fdSrpaulo {
967f27f4e28Sozaki-r 	struct in6_multi *in6m;
9688c2379fdSrpaulo 
9690c65aee4Sozaki-r 	rw_enter(&in6_multilock, RW_WRITER);
970f27f4e28Sozaki-r 	in6m = imm->i6mm_maddr;
9710c65aee4Sozaki-r 	imm->i6mm_maddr = NULL;
972f27f4e28Sozaki-r 	if (in6m != NULL) {
9730c65aee4Sozaki-r 		in6_delmulti_locked(in6m);
9748c2379fdSrpaulo 	}
9750c65aee4Sozaki-r 	rw_exit(&in6_multilock);
9768c2379fdSrpaulo 	free(imm, M_IPMADDR);
9778c2379fdSrpaulo 	return 0;
9788c2379fdSrpaulo }
9798c2379fdSrpaulo 
9808c2379fdSrpaulo /*
98100a9cf74Sozaki-r  * DEPRECATED: keep it just to avoid breaking old sysctl users.
9828c2379fdSrpaulo  */
983539332ecSjoerg static int
in6_mkludge_sysctl(SYSCTLFN_ARGS)984539332ecSjoerg in6_mkludge_sysctl(SYSCTLFN_ARGS)
985539332ecSjoerg {
986539332ecSjoerg 
987539332ecSjoerg 	if (namelen != 1)
988539332ecSjoerg 		return EINVAL;
989539332ecSjoerg 	*oldlenp = 0;
990539332ecSjoerg 	return 0;
991539332ecSjoerg }
992539332ecSjoerg 
993539332ecSjoerg static int
in6_multicast_sysctl(SYSCTLFN_ARGS)994539332ecSjoerg in6_multicast_sysctl(SYSCTLFN_ARGS)
995539332ecSjoerg {
996539332ecSjoerg 	struct ifnet *ifp;
997539332ecSjoerg 	struct ifaddr *ifa;
99800a9cf74Sozaki-r 	struct in6_ifaddr *ia6;
999539332ecSjoerg 	struct in6_multi *in6m;
1000539332ecSjoerg 	uint32_t tmp;
1001539332ecSjoerg 	int error;
1002539332ecSjoerg 	size_t written;
1003a403cbd4Sozaki-r 	struct psref psref, psref_ia;
1004a403cbd4Sozaki-r 	int bound, s;
1005539332ecSjoerg 
1006539332ecSjoerg 	if (namelen != 1)
1007539332ecSjoerg 		return EINVAL;
1008539332ecSjoerg 
100973c95a6aSozaki-r 	rw_enter(&in6_multilock, RW_READER);
101073c95a6aSozaki-r 
1011f0423d34Sozaki-r 	bound = curlwp_bind();
1012f0423d34Sozaki-r 	ifp = if_get_byindex(name[0], &psref);
1013f0423d34Sozaki-r 	if (ifp == NULL) {
1014f0423d34Sozaki-r 		curlwp_bindx(bound);
101573c95a6aSozaki-r 		rw_exit(&in6_multilock);
1016539332ecSjoerg 		return ENODEV;
1017f0423d34Sozaki-r 	}
1018539332ecSjoerg 
1019539332ecSjoerg 	if (oldp == NULL) {
1020539332ecSjoerg 		*oldlenp = 0;
1021a403cbd4Sozaki-r 		s = pserialize_read_enter();
10229e4c2bdaSozaki-r 		IFADDR_READER_FOREACH(ifa, ifp) {
102300a9cf74Sozaki-r 			LIST_FOREACH(in6m, &ifp->if_multiaddrs, in6m_entry) {
1024539332ecSjoerg 				*oldlenp += 2 * sizeof(struct in6_addr) +
1025539332ecSjoerg 				    sizeof(uint32_t);
1026539332ecSjoerg 			}
1027539332ecSjoerg 		}
1028a403cbd4Sozaki-r 		pserialize_read_exit(s);
1029f0423d34Sozaki-r 		if_put(ifp, &psref);
1030f0423d34Sozaki-r 		curlwp_bindx(bound);
103173c95a6aSozaki-r 		rw_exit(&in6_multilock);
1032539332ecSjoerg 		return 0;
1033539332ecSjoerg 	}
1034539332ecSjoerg 
1035539332ecSjoerg 	error = 0;
1036539332ecSjoerg 	written = 0;
1037a403cbd4Sozaki-r 	s = pserialize_read_enter();
10389e4c2bdaSozaki-r 	IFADDR_READER_FOREACH(ifa, ifp) {
1039539332ecSjoerg 		if (ifa->ifa_addr->sa_family != AF_INET6)
1040539332ecSjoerg 			continue;
1041a403cbd4Sozaki-r 
1042a403cbd4Sozaki-r 		ifa_acquire(ifa, &psref_ia);
1043a403cbd4Sozaki-r 		pserialize_read_exit(s);
1044a403cbd4Sozaki-r 
104500a9cf74Sozaki-r 		ia6 = ifatoia6(ifa);
104600a9cf74Sozaki-r 		LIST_FOREACH(in6m, &ifp->if_multiaddrs, in6m_entry) {
1047539332ecSjoerg 			if (written + 2 * sizeof(struct in6_addr) +
1048539332ecSjoerg 			    sizeof(uint32_t) > *oldlenp)
1049539332ecSjoerg 				goto done;
105000a9cf74Sozaki-r 			/*
105100a9cf74Sozaki-r 			 * XXX return the first IPv6 address to keep backward
105200a9cf74Sozaki-r 			 * compatibility, however now multicast addresses
105300a9cf74Sozaki-r 			 * don't belong to any IPv6 addresses so it should be
105400a9cf74Sozaki-r 			 * unnecessary.
105500a9cf74Sozaki-r 			 */
105600a9cf74Sozaki-r 			error = sysctl_copyout(l, &ia6->ia_addr.sin6_addr,
1057539332ecSjoerg 			    oldp, sizeof(struct in6_addr));
1058539332ecSjoerg 			if (error)
1059539332ecSjoerg 				goto done;
1060539332ecSjoerg 			oldp = (char *)oldp + sizeof(struct in6_addr);
1061539332ecSjoerg 			written += sizeof(struct in6_addr);
1062539332ecSjoerg 			error = sysctl_copyout(l, &in6m->in6m_addr,
1063539332ecSjoerg 			    oldp, sizeof(struct in6_addr));
1064539332ecSjoerg 			if (error)
1065539332ecSjoerg 				goto done;
1066539332ecSjoerg 			oldp = (char *)oldp + sizeof(struct in6_addr);
1067539332ecSjoerg 			written += sizeof(struct in6_addr);
1068539332ecSjoerg 			tmp = in6m->in6m_refcount;
1069539332ecSjoerg 			error = sysctl_copyout(l, &tmp, oldp, sizeof(tmp));
1070539332ecSjoerg 			if (error)
1071539332ecSjoerg 				goto done;
1072539332ecSjoerg 			oldp = (char *)oldp + sizeof(tmp);
1073539332ecSjoerg 			written += sizeof(tmp);
1074539332ecSjoerg 		}
1075a403cbd4Sozaki-r 
1076a403cbd4Sozaki-r 		s = pserialize_read_enter();
107700a9cf74Sozaki-r 
107800a9cf74Sozaki-r 		break;
1079539332ecSjoerg 	}
1080a403cbd4Sozaki-r 	pserialize_read_exit(s);
1081539332ecSjoerg done:
1082a403cbd4Sozaki-r 	ifa_release(ifa, &psref_ia);
1083f0423d34Sozaki-r 	if_put(ifp, &psref);
1084f0423d34Sozaki-r 	curlwp_bindx(bound);
108573c95a6aSozaki-r 	rw_exit(&in6_multilock);
1086539332ecSjoerg 	*oldlenp = written;
1087539332ecSjoerg 	return error;
1088539332ecSjoerg }
1089539332ecSjoerg 
1090a94a2051Sozaki-r void
in6_sysctl_multicast_setup(struct sysctllog ** clog)1091a94a2051Sozaki-r in6_sysctl_multicast_setup(struct sysctllog **clog)
1092539332ecSjoerg {
1093539332ecSjoerg 
1094539332ecSjoerg 	sysctl_createv(clog, 0, NULL, NULL,
1095539332ecSjoerg 		       CTLFLAG_PERMANENT,
10960bb0ad59Sjoerg 		       CTLTYPE_NODE, "inet6", NULL,
10970bb0ad59Sjoerg 		       NULL, 0, NULL, 0,
10980bb0ad59Sjoerg 		       CTL_NET, PF_INET6, CTL_EOL);
10990bb0ad59Sjoerg 
11000bb0ad59Sjoerg 	sysctl_createv(clog, 0, NULL, NULL,
11010bb0ad59Sjoerg 		       CTLFLAG_PERMANENT,
1102539332ecSjoerg 		       CTLTYPE_NODE, "multicast",
1103539332ecSjoerg 		       SYSCTL_DESCR("Multicast information"),
1104539332ecSjoerg 		       in6_multicast_sysctl, 0, NULL, 0,
1105539332ecSjoerg 		       CTL_NET, PF_INET6, CTL_CREATE, CTL_EOL);
1106539332ecSjoerg 
1107539332ecSjoerg 	sysctl_createv(clog, 0, NULL, NULL,
1108539332ecSjoerg 		       CTLFLAG_PERMANENT,
1109539332ecSjoerg 		       CTLTYPE_NODE, "multicast_kludge",
1110539332ecSjoerg 		       SYSCTL_DESCR("multicast kludge information"),
1111539332ecSjoerg 		       in6_mkludge_sysctl, 0, NULL, 0,
1112539332ecSjoerg 		       CTL_NET, PF_INET6, CTL_CREATE, CTL_EOL);
1113539332ecSjoerg }
1114