xref: /illumos-gate/usr/src/uts/common/inet/ip/igmp.c (revision f5db8fb0)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
221eee170aSErik Nordmark  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Internet Group Management Protocol (IGMP) routines.
287c478bd9Sstevel@tonic-gate  * Multicast Listener Discovery Protocol (MLD) routines.
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * Written by Steve Deering, Stanford, May 1988.
317c478bd9Sstevel@tonic-gate  * Modified by Rosen Sharma, Stanford, Aug 1994.
327c478bd9Sstevel@tonic-gate  * Modified by Bill Fenner, Xerox PARC, Feb. 1995.
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * MULTICAST 3.5.1.1
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/types.h>
387c478bd9Sstevel@tonic-gate #include <sys/stream.h>
397c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
407c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
417c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
427c478bd9Sstevel@tonic-gate #include <sys/systm.h>
437c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
447c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
457c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
467c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
477c478bd9Sstevel@tonic-gate #include <sys/zone.h>
48e11c3f44Smeem #include <sys/callb.h>
497c478bd9Sstevel@tonic-gate #include <sys/param.h>
507c478bd9Sstevel@tonic-gate #include <sys/socket.h>
517c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
527c478bd9Sstevel@tonic-gate #include <net/if.h>
537c478bd9Sstevel@tonic-gate #include <net/route.h>
547c478bd9Sstevel@tonic-gate #include <netinet/in.h>
557c478bd9Sstevel@tonic-gate #include <netinet/igmp_var.h>
567c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
577c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
58bd670b35SErik Nordmark #include <inet/ipsec_impl.h>
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate #include <inet/common.h>
617c478bd9Sstevel@tonic-gate #include <inet/mi.h>
627c478bd9Sstevel@tonic-gate #include <inet/nd.h>
636e91bba0SGirish Moodalbail #include <inet/tunables.h>
647c478bd9Sstevel@tonic-gate #include <inet/ip.h>
657c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
667c478bd9Sstevel@tonic-gate #include <inet/ip_multi.h>
677c478bd9Sstevel@tonic-gate #include <inet/ip_listutils.h>
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #include <netinet/igmp.h>
70bd670b35SErik Nordmark #include <inet/ip_ndp.h>
717c478bd9Sstevel@tonic-gate #include <inet/ip_if.h>
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate static uint_t	igmp_query_in(ipha_t *ipha, igmpa_t *igmpa, ill_t *ill);
747c478bd9Sstevel@tonic-gate static uint_t	igmpv3_query_in(igmp3qa_t *igmp3qa, ill_t *ill, int igmplen);
757c478bd9Sstevel@tonic-gate static uint_t	mld_query_in(mld_hdr_t *mldh, ill_t *ill);
767c478bd9Sstevel@tonic-gate static uint_t	mldv2_query_in(mld2q_t *mld2q, ill_t *ill, int mldlen);
777c478bd9Sstevel@tonic-gate static void	igmp_sendpkt(ilm_t *ilm, uchar_t type, ipaddr_t addr);
787c478bd9Sstevel@tonic-gate static void	mld_sendpkt(ilm_t *ilm, uchar_t type, const in6_addr_t *v6addr);
79bd670b35SErik Nordmark static void	igmpv3_sendrpt(ill_t *ill, mrec_t *reclist);
807c478bd9Sstevel@tonic-gate static void	mldv2_sendrpt(ill_t *ill, mrec_t *reclist);
817c478bd9Sstevel@tonic-gate static mrec_t	*mcast_bldmrec(mcast_record_t type, in6_addr_t *grp,
827c478bd9Sstevel@tonic-gate 		    slist_t *srclist, mrec_t *next);
837c478bd9Sstevel@tonic-gate static void	mcast_init_rtx(ill_t *ill, rtx_state_t *rtxp,
847c478bd9Sstevel@tonic-gate 		    mcast_record_t rtype, slist_t *flist);
857c478bd9Sstevel@tonic-gate static mrec_t	*mcast_merge_rtx(ilm_t *ilm, mrec_t *rp, slist_t *flist);
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * Macros used to do timer len conversions.  Timer values are always
897c478bd9Sstevel@tonic-gate  * stored and passed to the timer functions as milliseconds; but the
907c478bd9Sstevel@tonic-gate  * default values and values from the wire may not be.
917c478bd9Sstevel@tonic-gate  *
927c478bd9Sstevel@tonic-gate  * And yes, it's obscure, but decisecond is easier to abbreviate than
937c478bd9Sstevel@tonic-gate  * "tenths of a second".
947c478bd9Sstevel@tonic-gate  */
957c478bd9Sstevel@tonic-gate #define	DSEC_TO_MSEC(dsec)	((dsec) * 100)
967c478bd9Sstevel@tonic-gate #define	SEC_TO_MSEC(sec)	((sec) * 1000)
977c478bd9Sstevel@tonic-gate 
988dc47d9fSudpa /*
998dc47d9fSudpa  * A running timer (scheduled thru timeout) can be cancelled if another
1008dc47d9fSudpa  * timer with a shorter timeout value is scheduled before it has timed
1018dc47d9fSudpa  * out.  When the shorter timer expires, the original timer is updated
1028dc47d9fSudpa  * to account for the time elapsed while the shorter timer ran; but this
1038dc47d9fSudpa  * does not take into account the amount of time already spent in timeout
1048dc47d9fSudpa  * state before being preempted by the shorter timer, that is the time
1058dc47d9fSudpa  * interval between time scheduled to time cancelled.  This can cause
1068dc47d9fSudpa  * delays in sending out multicast membership reports.  To resolve this
1078dc47d9fSudpa  * problem, wallclock time (absolute time) is used instead of deltas
1088dc47d9fSudpa  * (relative time) to track timers.
1098dc47d9fSudpa  *
1108dc47d9fSudpa  * The MACRO below gets the lbolt value, used for proper timer scheduling
1118dc47d9fSudpa  * and firing. Therefore multicast membership reports are sent on time.
1128dc47d9fSudpa  * The timer does not exactly fire at the time it was scehduled to fire,
1138dc47d9fSudpa  * there is a difference of a few milliseconds observed. An offset is used
1148dc47d9fSudpa  * to take care of the difference.
1158dc47d9fSudpa  */
1168dc47d9fSudpa 
1178dc47d9fSudpa #define	CURRENT_MSTIME	((uint_t)TICK_TO_MSEC(ddi_get_lbolt()))
1188dc47d9fSudpa #define	CURRENT_OFFSET	(999)
1198dc47d9fSudpa 
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate  * The first multicast join will trigger the igmp timers / mld timers
1227c478bd9Sstevel@tonic-gate  * The unit for next is milliseconds.
1237c478bd9Sstevel@tonic-gate  */
124bd670b35SErik Nordmark void
125f4b3ec61Sdh igmp_start_timers(unsigned next, ip_stack_t *ipst)
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	int	time_left;
1287c478bd9Sstevel@tonic-gate 	int	ret;
129bd670b35SErik Nordmark 	timeout_id_t id;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	ASSERT(next != 0 && next != INFINITY);
1327c478bd9Sstevel@tonic-gate 
133f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1347c478bd9Sstevel@tonic-gate 
135f4b3ec61Sdh 	if (ipst->ips_igmp_timer_setter_active) {
1367c478bd9Sstevel@tonic-gate 		/*
1377c478bd9Sstevel@tonic-gate 		 * Serialize timer setters, one at a time. If the
1387c478bd9Sstevel@tonic-gate 		 * timer is currently being set by someone,
1397c478bd9Sstevel@tonic-gate 		 * just record the next time when it has to be
1407c478bd9Sstevel@tonic-gate 		 * invoked and return. The current setter will
1417c478bd9Sstevel@tonic-gate 		 * take care.
1427c478bd9Sstevel@tonic-gate 		 */
143f4b3ec61Sdh 		ipst->ips_igmp_time_to_next =
144f4b3ec61Sdh 		    MIN(ipst->ips_igmp_time_to_next, next);
145f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1467c478bd9Sstevel@tonic-gate 		return;
1477c478bd9Sstevel@tonic-gate 	} else {
148f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_TRUE;
1497c478bd9Sstevel@tonic-gate 	}
150f4b3ec61Sdh 	if (ipst->ips_igmp_timeout_id == 0) {
1517c478bd9Sstevel@tonic-gate 		/*
152*f5db8fb0SRobert Mustacchi 		 * The timer is inactive. We need to start a timer if we haven't
153*f5db8fb0SRobert Mustacchi 		 * been asked to quiesce.
1547c478bd9Sstevel@tonic-gate 		 */
155f4b3ec61Sdh 		ipst->ips_igmp_time_to_next = next;
156*f5db8fb0SRobert Mustacchi 		if (ipst->ips_igmp_timer_quiesce != B_TRUE) {
157*f5db8fb0SRobert Mustacchi 			ipst->ips_igmp_timeout_id =
158*f5db8fb0SRobert Mustacchi 			    timeout(igmp_timeout_handler, (void *)ipst,
159*f5db8fb0SRobert Mustacchi 			    MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
160*f5db8fb0SRobert Mustacchi 			ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
161*f5db8fb0SRobert Mustacchi 		}
162f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_FALSE;
163f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1647c478bd9Sstevel@tonic-gate 		return;
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	/*
1687c478bd9Sstevel@tonic-gate 	 * The timer was scheduled sometime back for firing in
1697c478bd9Sstevel@tonic-gate 	 * 'igmp_time_to_next' ms and is active. We need to
1707c478bd9Sstevel@tonic-gate 	 * reschedule the timeout if the new 'next' will happen
1717c478bd9Sstevel@tonic-gate 	 * earlier than the currently scheduled timeout
1727c478bd9Sstevel@tonic-gate 	 */
1738dc47d9fSudpa 	time_left = ipst->ips_igmp_timer_scheduled_last +
174f4b3ec61Sdh 	    MSEC_TO_TICK(ipst->ips_igmp_time_to_next) - ddi_get_lbolt();
1757c478bd9Sstevel@tonic-gate 	if (time_left < MSEC_TO_TICK(next)) {
176f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_FALSE;
177f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1787c478bd9Sstevel@tonic-gate 		return;
1797c478bd9Sstevel@tonic-gate 	}
180bd670b35SErik Nordmark 	id = ipst->ips_igmp_timeout_id;
1817c478bd9Sstevel@tonic-gate 
182f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
183bd670b35SErik Nordmark 	ret = untimeout(id);
184f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1857c478bd9Sstevel@tonic-gate 	/*
1867c478bd9Sstevel@tonic-gate 	 * The timeout was cancelled, or the timeout handler
1877c478bd9Sstevel@tonic-gate 	 * completed, while we were blocked in the untimeout.
1887c478bd9Sstevel@tonic-gate 	 * No other thread could have set the timer meanwhile
1897c478bd9Sstevel@tonic-gate 	 * since we serialized all the timer setters. Thus
1907c478bd9Sstevel@tonic-gate 	 * no timer is currently active nor executing nor will
1917c478bd9Sstevel@tonic-gate 	 * any timer fire in the future. We start the timer now
1927c478bd9Sstevel@tonic-gate 	 * if needed.
1937c478bd9Sstevel@tonic-gate 	 */
1947c478bd9Sstevel@tonic-gate 	if (ret == -1) {
195f4b3ec61Sdh 		ASSERT(ipst->ips_igmp_timeout_id == 0);
1967c478bd9Sstevel@tonic-gate 	} else {
197f4b3ec61Sdh 		ASSERT(ipst->ips_igmp_timeout_id != 0);
198f4b3ec61Sdh 		ipst->ips_igmp_timeout_id = 0;
1997c478bd9Sstevel@tonic-gate 	}
200*f5db8fb0SRobert Mustacchi 	if (ipst->ips_igmp_time_to_next != 0 &&
201*f5db8fb0SRobert Mustacchi 	    ipst->ips_igmp_timer_quiesce != B_TRUE) {
202f4b3ec61Sdh 		ipst->ips_igmp_time_to_next =
203f4b3ec61Sdh 		    MIN(ipst->ips_igmp_time_to_next, next);
204f4b3ec61Sdh 		ipst->ips_igmp_timeout_id = timeout(igmp_timeout_handler,
205f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
2068dc47d9fSudpa 		ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
2077c478bd9Sstevel@tonic-gate 	}
208f4b3ec61Sdh 	ipst->ips_igmp_timer_setter_active = B_FALSE;
209f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
2107c478bd9Sstevel@tonic-gate }
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate /*
2137c478bd9Sstevel@tonic-gate  * mld_start_timers:
2147c478bd9Sstevel@tonic-gate  * The unit for next is milliseconds.
2157c478bd9Sstevel@tonic-gate  */
216bd670b35SErik Nordmark void
217f4b3ec61Sdh mld_start_timers(unsigned next, ip_stack_t *ipst)
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	int	time_left;
2207c478bd9Sstevel@tonic-gate 	int	ret;
221bd670b35SErik Nordmark 	timeout_id_t id;
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	ASSERT(next != 0 && next != INFINITY);
2247c478bd9Sstevel@tonic-gate 
225f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
226f4b3ec61Sdh 	if (ipst->ips_mld_timer_setter_active) {
2277c478bd9Sstevel@tonic-gate 		/*
2287c478bd9Sstevel@tonic-gate 		 * Serialize timer setters, one at a time. If the
2297c478bd9Sstevel@tonic-gate 		 * timer is currently being set by someone,
2307c478bd9Sstevel@tonic-gate 		 * just record the next time when it has to be
2317c478bd9Sstevel@tonic-gate 		 * invoked and return. The current setter will
2327c478bd9Sstevel@tonic-gate 		 * take care.
2337c478bd9Sstevel@tonic-gate 		 */
234f4b3ec61Sdh 		ipst->ips_mld_time_to_next =
235f4b3ec61Sdh 		    MIN(ipst->ips_mld_time_to_next, next);
236f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2377c478bd9Sstevel@tonic-gate 		return;
2387c478bd9Sstevel@tonic-gate 	} else {
239f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_TRUE;
2407c478bd9Sstevel@tonic-gate 	}
241f4b3ec61Sdh 	if (ipst->ips_mld_timeout_id == 0) {
2427c478bd9Sstevel@tonic-gate 		/*
243*f5db8fb0SRobert Mustacchi 		 * The timer is inactive. We need to start a timer, if we
244*f5db8fb0SRobert Mustacchi 		 * haven't been asked to quiesce.
2457c478bd9Sstevel@tonic-gate 		 */
246f4b3ec61Sdh 		ipst->ips_mld_time_to_next = next;
247*f5db8fb0SRobert Mustacchi 		if (ipst->ips_mld_timer_quiesce != B_TRUE) {
248*f5db8fb0SRobert Mustacchi 			ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
249*f5db8fb0SRobert Mustacchi 			    (void *)ipst,
250*f5db8fb0SRobert Mustacchi 			    MSEC_TO_TICK(ipst->ips_mld_time_to_next));
251*f5db8fb0SRobert Mustacchi 			ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
252*f5db8fb0SRobert Mustacchi 		}
253f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_FALSE;
254f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2557c478bd9Sstevel@tonic-gate 		return;
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/*
2597c478bd9Sstevel@tonic-gate 	 * The timer was scheduled sometime back for firing in
2607c478bd9Sstevel@tonic-gate 	 * 'igmp_time_to_next' ms and is active. We need to
2617c478bd9Sstevel@tonic-gate 	 * reschedule the timeout if the new 'next' will happen
2627c478bd9Sstevel@tonic-gate 	 * earlier than the currently scheduled timeout
2637c478bd9Sstevel@tonic-gate 	 */
2648dc47d9fSudpa 	time_left = ipst->ips_mld_timer_scheduled_last +
265f4b3ec61Sdh 	    MSEC_TO_TICK(ipst->ips_mld_time_to_next) - ddi_get_lbolt();
2667c478bd9Sstevel@tonic-gate 	if (time_left < MSEC_TO_TICK(next)) {
267f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_FALSE;
268f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2697c478bd9Sstevel@tonic-gate 		return;
2707c478bd9Sstevel@tonic-gate 	}
271bd670b35SErik Nordmark 	id = ipst->ips_mld_timeout_id;
2727c478bd9Sstevel@tonic-gate 
273f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
274bd670b35SErik Nordmark 	ret = untimeout(id);
275f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
2767c478bd9Sstevel@tonic-gate 	/*
2777c478bd9Sstevel@tonic-gate 	 * The timeout was cancelled, or the timeout handler
2787c478bd9Sstevel@tonic-gate 	 * completed, while we were blocked in the untimeout.
2797c478bd9Sstevel@tonic-gate 	 * No other thread could have set the timer meanwhile
2807c478bd9Sstevel@tonic-gate 	 * since we serialized all the timer setters. Thus
2817c478bd9Sstevel@tonic-gate 	 * no timer is currently active nor executing nor will
2827c478bd9Sstevel@tonic-gate 	 * any timer fire in the future. We start the timer now
2837c478bd9Sstevel@tonic-gate 	 * if needed.
2847c478bd9Sstevel@tonic-gate 	 */
2857c478bd9Sstevel@tonic-gate 	if (ret == -1) {
286f4b3ec61Sdh 		ASSERT(ipst->ips_mld_timeout_id == 0);
2877c478bd9Sstevel@tonic-gate 	} else {
288f4b3ec61Sdh 		ASSERT(ipst->ips_mld_timeout_id != 0);
289f4b3ec61Sdh 		ipst->ips_mld_timeout_id = 0;
2907c478bd9Sstevel@tonic-gate 	}
291*f5db8fb0SRobert Mustacchi 	if (ipst->ips_mld_time_to_next != 0 &&
292*f5db8fb0SRobert Mustacchi 	    ipst->ips_mld_timer_quiesce == B_FALSE) {
293f4b3ec61Sdh 		ipst->ips_mld_time_to_next =
294f4b3ec61Sdh 		    MIN(ipst->ips_mld_time_to_next, next);
295f4b3ec61Sdh 		ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
296f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_mld_time_to_next));
2978dc47d9fSudpa 		ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
2987c478bd9Sstevel@tonic-gate 	}
299f4b3ec61Sdh 	ipst->ips_mld_timer_setter_active = B_FALSE;
300f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
3017c478bd9Sstevel@tonic-gate }
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate /*
3047c478bd9Sstevel@tonic-gate  * igmp_input:
3056226e9d9Sethindra  * Return NULL for a bad packet that is discarded here.
3066226e9d9Sethindra  * Return mp if the message is OK and should be handed to "raw" receivers.
3077c478bd9Sstevel@tonic-gate  * Callers of igmp_input() may need to reinitialize variables that were copied
3087c478bd9Sstevel@tonic-gate  * from the mblk as this calls pullupmsg().
3097c478bd9Sstevel@tonic-gate  */
3106226e9d9Sethindra mblk_t *
311bd670b35SErik Nordmark igmp_input(mblk_t *mp, ip_recv_attr_t *ira)
3127c478bd9Sstevel@tonic-gate {
3137c478bd9Sstevel@tonic-gate 	igmpa_t 	*igmpa;
3147c478bd9Sstevel@tonic-gate 	ipha_t		*ipha = (ipha_t *)(mp->b_rptr);
3157c478bd9Sstevel@tonic-gate 	int		iphlen, igmplen, mblklen;
3167c478bd9Sstevel@tonic-gate 	ilm_t 		*ilm;
3177c478bd9Sstevel@tonic-gate 	uint32_t	src, dst;
3187c478bd9Sstevel@tonic-gate 	uint32_t 	group;
319bd670b35SErik Nordmark 	in6_addr_t	v6group;
3207c478bd9Sstevel@tonic-gate 	uint_t		next;
3217c478bd9Sstevel@tonic-gate 	ipif_t 		*ipif;
322bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
323bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	ASSERT(!ill->ill_isv6);
326f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_rcv_total;
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	mblklen = MBLKL(mp);
329bd670b35SErik Nordmark 	iphlen = ira->ira_ip_hdr_length;
330bd670b35SErik Nordmark 	if (mblklen < 1 || mblklen < iphlen) {
331f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
3326226e9d9Sethindra 		goto bad_pkt;
3337c478bd9Sstevel@tonic-gate 	}
334bd670b35SErik Nordmark 	igmplen = ira->ira_pktlen - iphlen;
3357c478bd9Sstevel@tonic-gate 	/*
3367c478bd9Sstevel@tonic-gate 	 * Since msg sizes are more variable with v3, just pullup the
3377c478bd9Sstevel@tonic-gate 	 * whole thing now.
3387c478bd9Sstevel@tonic-gate 	 */
3397c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < (igmplen + iphlen)) {
3407c478bd9Sstevel@tonic-gate 		mblk_t *mp1;
3417c478bd9Sstevel@tonic-gate 		if ((mp1 = msgpullup(mp, -1)) == NULL) {
342f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_tooshort;
3436226e9d9Sethindra 			goto bad_pkt;
3447c478bd9Sstevel@tonic-gate 		}
3457c478bd9Sstevel@tonic-gate 		freemsg(mp);
3467c478bd9Sstevel@tonic-gate 		mp = mp1;
3477c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)(mp->b_rptr);
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	/*
3517c478bd9Sstevel@tonic-gate 	 * Validate lengths
3527c478bd9Sstevel@tonic-gate 	 */
3537c478bd9Sstevel@tonic-gate 	if (igmplen < IGMP_MINLEN) {
354f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
3556226e9d9Sethindra 		goto bad_pkt;
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	igmpa = (igmpa_t *)(&mp->b_rptr[iphlen]);
3597c478bd9Sstevel@tonic-gate 	src = ipha->ipha_src;
3607c478bd9Sstevel@tonic-gate 	dst = ipha->ipha_dst;
3617c478bd9Sstevel@tonic-gate 	if (ip_debug > 1)
3627c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
3637c478bd9Sstevel@tonic-gate 		    "igmp_input: src 0x%x, dst 0x%x on %s\n",
3647c478bd9Sstevel@tonic-gate 		    (int)ntohl(src), (int)ntohl(dst),
3657c478bd9Sstevel@tonic-gate 		    ill->ill_name);
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	switch (igmpa->igmpa_type) {
3687c478bd9Sstevel@tonic-gate 	case IGMP_MEMBERSHIP_QUERY:
3697c478bd9Sstevel@tonic-gate 		/*
3707c478bd9Sstevel@tonic-gate 		 * packet length differentiates between v1/v2 and v3
3717c478bd9Sstevel@tonic-gate 		 * v1/v2 should be exactly 8 octets long; v3 is >= 12
3727c478bd9Sstevel@tonic-gate 		 */
3738dc47d9fSudpa 		if ((igmplen == IGMP_MINLEN) ||
3748dc47d9fSudpa 		    (ipst->ips_igmp_max_version <= IGMP_V2_ROUTER)) {
3757c478bd9Sstevel@tonic-gate 			next = igmp_query_in(ipha, igmpa, ill);
3767c478bd9Sstevel@tonic-gate 		} else if (igmplen >= IGMP_V3_QUERY_MINLEN) {
3777c478bd9Sstevel@tonic-gate 			next = igmpv3_query_in((igmp3qa_t *)igmpa, ill,
3787c478bd9Sstevel@tonic-gate 			    igmplen);
3797c478bd9Sstevel@tonic-gate 		} else {
380f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_tooshort;
3816226e9d9Sethindra 			goto bad_pkt;
3827c478bd9Sstevel@tonic-gate 		}
3836226e9d9Sethindra 		if (next == 0)
3846226e9d9Sethindra 			goto bad_pkt;
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		if (next != INFINITY)
387f4b3ec61Sdh 			igmp_start_timers(next, ipst);
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 		break;
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	case IGMP_V1_MEMBERSHIP_REPORT:
3927c478bd9Sstevel@tonic-gate 	case IGMP_V2_MEMBERSHIP_REPORT:
3937c478bd9Sstevel@tonic-gate 		/*
3947c478bd9Sstevel@tonic-gate 		 * For fast leave to work, we have to know that we are the
3957c478bd9Sstevel@tonic-gate 		 * last person to send a report for this group. Reports
3967c478bd9Sstevel@tonic-gate 		 * generated by us are looped back since we could potentially
3977c478bd9Sstevel@tonic-gate 		 * be a multicast router, so discard reports sourced by me.
3987c478bd9Sstevel@tonic-gate 		 */
3997c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
4007c478bd9Sstevel@tonic-gate 		for (ipif = ill->ill_ipif; ipif != NULL;
4017c478bd9Sstevel@tonic-gate 		    ipif = ipif->ipif_next) {
4027c478bd9Sstevel@tonic-gate 			if (ipif->ipif_lcl_addr == src) {
4037c478bd9Sstevel@tonic-gate 				if (ip_debug > 1) {
4047c478bd9Sstevel@tonic-gate 					(void) mi_strlog(ill->ill_rq,
4057c478bd9Sstevel@tonic-gate 					    1,
4067c478bd9Sstevel@tonic-gate 					    SL_TRACE,
4077c478bd9Sstevel@tonic-gate 					    "igmp_input: we are only "
408bd670b35SErik Nordmark 					    "member src 0x%x\n",
409bd670b35SErik Nordmark 					    (int)ntohl(src));
4107c478bd9Sstevel@tonic-gate 				}
4117c478bd9Sstevel@tonic-gate 				mutex_exit(&ill->ill_lock);
4126226e9d9Sethindra 				return (mp);
4137c478bd9Sstevel@tonic-gate 			}
4147c478bd9Sstevel@tonic-gate 		}
4157c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
4167c478bd9Sstevel@tonic-gate 
417f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_reports;
4187c478bd9Sstevel@tonic-gate 		group = igmpa->igmpa_group;
4197c478bd9Sstevel@tonic-gate 		if (!CLASSD(group)) {
420f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_badreports;
4216226e9d9Sethindra 			goto bad_pkt;
4227c478bd9Sstevel@tonic-gate 		}
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 		/*
4257c478bd9Sstevel@tonic-gate 		 * KLUDGE: if the IP source address of the report has an
4267c478bd9Sstevel@tonic-gate 		 * unspecified (i.e., zero) subnet number, as is allowed for
4277c478bd9Sstevel@tonic-gate 		 * a booting host, replace it with the correct subnet number
4287c478bd9Sstevel@tonic-gate 		 * so that a process-level multicast routing demon can
4297c478bd9Sstevel@tonic-gate 		 * determine which subnet it arrived from.  This is necessary
4307c478bd9Sstevel@tonic-gate 		 * to compensate for the lack of any way for a process to
4317c478bd9Sstevel@tonic-gate 		 * determine the arrival interface of an incoming packet.
4327c478bd9Sstevel@tonic-gate 		 *
4337c478bd9Sstevel@tonic-gate 		 * Requires that a copy of *this* message it passed up
4347c478bd9Sstevel@tonic-gate 		 * to the raw interface which is done by our caller.
4357c478bd9Sstevel@tonic-gate 		 */
4367c478bd9Sstevel@tonic-gate 		if ((src & htonl(0xFF000000U)) == 0) {	/* Minimum net mask */
4377c478bd9Sstevel@tonic-gate 			/* Pick the first ipif on this ill */
4387c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
4397c478bd9Sstevel@tonic-gate 			src = ill->ill_ipif->ipif_subnet;
4407c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
4417c478bd9Sstevel@tonic-gate 			ip1dbg(("igmp_input: changed src to 0x%x\n",
4427c478bd9Sstevel@tonic-gate 			    (int)ntohl(src)));
4437c478bd9Sstevel@tonic-gate 			ipha->ipha_src = src;
4447c478bd9Sstevel@tonic-gate 		}
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 		/*
447e11c3f44Smeem 		 * If our ill has ILMs that belong to the group being
448e11c3f44Smeem 		 * reported, and we are a 'Delaying Member' in the RFC
449e11c3f44Smeem 		 * terminology, stop our timer for that group and 'clear
450e11c3f44Smeem 		 * flag' i.e. mark as IGMP_OTHERMEMBER.
4517c478bd9Sstevel@tonic-gate 		 */
452bd670b35SErik Nordmark 		rw_enter(&ill->ill_mcast_lock, RW_WRITER);
453bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
454bd670b35SErik Nordmark 		for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
455bd670b35SErik Nordmark 			if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, &v6group))
456bd670b35SErik Nordmark 				continue;
457bd670b35SErik Nordmark 
458bd670b35SErik Nordmark 			++ipst->ips_igmpstat.igps_rcv_ourreports;
459bd670b35SErik Nordmark 			ilm->ilm_timer = INFINITY;
460bd670b35SErik Nordmark 			ilm->ilm_state = IGMP_OTHERMEMBER;
461bd670b35SErik Nordmark 		} /* for */
462bd670b35SErik Nordmark 		rw_exit(&ill->ill_mcast_lock);
463bd670b35SErik Nordmark 		ill_mcast_timer_start(ill->ill_ipst);
4647c478bd9Sstevel@tonic-gate 		break;
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	case IGMP_V3_MEMBERSHIP_REPORT:
4677c478bd9Sstevel@tonic-gate 		/*
4687c478bd9Sstevel@tonic-gate 		 * Currently nothing to do here; IGMP router is not
4697c478bd9Sstevel@tonic-gate 		 * implemented in ip, and v3 hosts don't pay attention
4707c478bd9Sstevel@tonic-gate 		 * to membership reports.
4717c478bd9Sstevel@tonic-gate 		 */
4727c478bd9Sstevel@tonic-gate 		break;
4737c478bd9Sstevel@tonic-gate 	}
4747c478bd9Sstevel@tonic-gate 	/*
4757c478bd9Sstevel@tonic-gate 	 * Pass all valid IGMP packets up to any process(es) listening
4767c478bd9Sstevel@tonic-gate 	 * on a raw IGMP socket. Do not free the packet.
4777c478bd9Sstevel@tonic-gate 	 */
4786226e9d9Sethindra 	return (mp);
4796226e9d9Sethindra 
4806226e9d9Sethindra bad_pkt:
4816226e9d9Sethindra 	freemsg(mp);
4826226e9d9Sethindra 	return (NULL);
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate static uint_t
4867c478bd9Sstevel@tonic-gate igmp_query_in(ipha_t *ipha, igmpa_t *igmpa, ill_t *ill)
4877c478bd9Sstevel@tonic-gate {
4887c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
4897c478bd9Sstevel@tonic-gate 	int	timer;
4908dc47d9fSudpa 	uint_t	next, current;
491f4b3ec61Sdh 	ip_stack_t	 *ipst;
4927c478bd9Sstevel@tonic-gate 
493f4b3ec61Sdh 	ipst = ill->ill_ipst;
494f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_rcv_queries;
4957c478bd9Sstevel@tonic-gate 
496bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
4977c478bd9Sstevel@tonic-gate 	/*
4987c478bd9Sstevel@tonic-gate 	 * In the IGMPv2 specification, there are 3 states and a flag.
4997c478bd9Sstevel@tonic-gate 	 *
5007c478bd9Sstevel@tonic-gate 	 * In Non-Member state, we simply don't have a membership record.
5017c478bd9Sstevel@tonic-gate 	 * In Delaying Member state, our timer is running (ilm->ilm_timer
5027c478bd9Sstevel@tonic-gate 	 * < INFINITY).  In Idle Member state, our timer is not running
5037c478bd9Sstevel@tonic-gate 	 * (ilm->ilm_timer == INFINITY).
5047c478bd9Sstevel@tonic-gate 	 *
5057c478bd9Sstevel@tonic-gate 	 * The flag is ilm->ilm_state, it is set to IGMP_OTHERMEMBER if
5067c478bd9Sstevel@tonic-gate 	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
5077c478bd9Sstevel@tonic-gate 	 * if I sent the last report.
5087c478bd9Sstevel@tonic-gate 	 */
5098dc47d9fSudpa 	if ((igmpa->igmpa_code == 0) ||
5108dc47d9fSudpa 	    (ipst->ips_igmp_max_version == IGMP_V1_ROUTER)) {
5117c478bd9Sstevel@tonic-gate 		/*
5127c478bd9Sstevel@tonic-gate 		 * Query from an old router.
5137c478bd9Sstevel@tonic-gate 		 * Remember that the querier on this interface is old,
5147c478bd9Sstevel@tonic-gate 		 * and set the timer to the value in RFC 1112.
5157c478bd9Sstevel@tonic-gate 		 */
5167c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v1_time = 0;
5177c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v1_tset = 1;
5187c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type != IGMP_V1_ROUTER) {
5197c478bd9Sstevel@tonic-gate 			ip1dbg(("Received IGMPv1 Query on %s, switching mode "
5207c478bd9Sstevel@tonic-gate 			    "to IGMP_V1_ROUTER\n", ill->ill_name));
5211a5e258fSJosef 'Jeff' Sipek 			atomic_inc_16(&ill->ill_ifptr->illif_mcast_v1);
5227c478bd9Sstevel@tonic-gate 			ill->ill_mcast_type = IGMP_V1_ROUTER;
5237c478bd9Sstevel@tonic-gate 		}
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 		timer = SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY);
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 		if (ipha->ipha_dst != htonl(INADDR_ALLHOSTS_GROUP) ||
5287c478bd9Sstevel@tonic-gate 		    igmpa->igmpa_group != 0) {
529f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_badqueries;
530bd670b35SErik Nordmark 			rw_exit(&ill->ill_mcast_lock);
531bd670b35SErik Nordmark 			ill_mcast_timer_start(ill->ill_ipst);
5327c478bd9Sstevel@tonic-gate 			return (0);
5337c478bd9Sstevel@tonic-gate 		}
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	} else {
5367c478bd9Sstevel@tonic-gate 		in_addr_t group;
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 		/*
5397c478bd9Sstevel@tonic-gate 		 * Query from a new router
5407c478bd9Sstevel@tonic-gate 		 * Simply do a validity check
5417c478bd9Sstevel@tonic-gate 		 */
5427c478bd9Sstevel@tonic-gate 		group = igmpa->igmpa_group;
5437c478bd9Sstevel@tonic-gate 		if (group != 0 && (!CLASSD(group))) {
544f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_badqueries;
545bd670b35SErik Nordmark 			rw_exit(&ill->ill_mcast_lock);
546bd670b35SErik Nordmark 			ill_mcast_timer_start(ill->ill_ipst);
5477c478bd9Sstevel@tonic-gate 			return (0);
5487c478bd9Sstevel@tonic-gate 		}
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 		/*
5517c478bd9Sstevel@tonic-gate 		 * Switch interface state to v2 on receipt of a v2 query
5527c478bd9Sstevel@tonic-gate 		 * ONLY IF current state is v3.  Let things be if current
5537c478bd9Sstevel@tonic-gate 		 * state if v1 but do reset the v2-querier-present timer.
5547c478bd9Sstevel@tonic-gate 		 */
5557c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V3_ROUTER) {
5567c478bd9Sstevel@tonic-gate 			ip1dbg(("Received IGMPv2 Query on %s, switching mode "
5577c478bd9Sstevel@tonic-gate 			    "to IGMP_V2_ROUTER", ill->ill_name));
5581a5e258fSJosef 'Jeff' Sipek 			atomic_inc_16(&ill->ill_ifptr->illif_mcast_v2);
5597c478bd9Sstevel@tonic-gate 			ill->ill_mcast_type = IGMP_V2_ROUTER;
5607c478bd9Sstevel@tonic-gate 		}
5617c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v2_time = 0;
5627c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v2_tset = 1;
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 		timer = DSEC_TO_MSEC((int)igmpa->igmpa_code);
5657c478bd9Sstevel@tonic-gate 	}
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
5687c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
5697c478bd9Sstevel@tonic-gate 		    "igmp_input: TIMER = igmp_code %d igmp_type 0x%x",
5707c478bd9Sstevel@tonic-gate 		    (int)ntohs(igmpa->igmpa_code),
5717c478bd9Sstevel@tonic-gate 		    (int)ntohs(igmpa->igmpa_type));
5727c478bd9Sstevel@tonic-gate 	}
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	/*
5757c478bd9Sstevel@tonic-gate 	 * -Start the timers in all of our membership records
5767c478bd9Sstevel@tonic-gate 	 *  for the physical interface on which the query
5777c478bd9Sstevel@tonic-gate 	 *  arrived, excluding those that belong to the "all
5787c478bd9Sstevel@tonic-gate 	 *  hosts" group (224.0.0.1).
5797c478bd9Sstevel@tonic-gate 	 *
5807c478bd9Sstevel@tonic-gate 	 * -Restart any timer that is already running but has
5817c478bd9Sstevel@tonic-gate 	 *  a value longer than the requested timeout.
5827c478bd9Sstevel@tonic-gate 	 *
5837c478bd9Sstevel@tonic-gate 	 * -Use the value specified in the query message as
5847c478bd9Sstevel@tonic-gate 	 *  the maximum timeout.
5857c478bd9Sstevel@tonic-gate 	 */
5867c478bd9Sstevel@tonic-gate 	next = (unsigned)INFINITY;
5878dc47d9fSudpa 
5888dc47d9fSudpa 	current = CURRENT_MSTIME;
589bd670b35SErik Nordmark 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		/*
5927c478bd9Sstevel@tonic-gate 		 * A multicast router joins INADDR_ANY address
5937c478bd9Sstevel@tonic-gate 		 * to enable promiscuous reception of all
5947c478bd9Sstevel@tonic-gate 		 * mcasts from the interface. This INADDR_ANY
5957c478bd9Sstevel@tonic-gate 		 * is stored in the ilm_v6addr as V6 unspec addr
5967c478bd9Sstevel@tonic-gate 		 */
5977c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr))
5987c478bd9Sstevel@tonic-gate 			continue;
5997c478bd9Sstevel@tonic-gate 		if (ilm->ilm_addr == htonl(INADDR_ANY))
6007c478bd9Sstevel@tonic-gate 			continue;
6017c478bd9Sstevel@tonic-gate 		if (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP) &&
6027c478bd9Sstevel@tonic-gate 		    (igmpa->igmpa_group == 0) ||
6037c478bd9Sstevel@tonic-gate 		    (igmpa->igmpa_group == ilm->ilm_addr)) {
6047c478bd9Sstevel@tonic-gate 			if (ilm->ilm_timer > timer) {
6057c478bd9Sstevel@tonic-gate 				MCAST_RANDOM_DELAY(ilm->ilm_timer, timer);
6067c478bd9Sstevel@tonic-gate 				if (ilm->ilm_timer < next)
6077c478bd9Sstevel@tonic-gate 					next = ilm->ilm_timer;
6088dc47d9fSudpa 				ilm->ilm_timer += current;
6097c478bd9Sstevel@tonic-gate 			}
6107c478bd9Sstevel@tonic-gate 		}
6117c478bd9Sstevel@tonic-gate 	}
612bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
613bd670b35SErik Nordmark 	/*
614bd670b35SErik Nordmark 	 * No packets have been sent above - no
615bd670b35SErik Nordmark 	 * ill_mcast_send_queued is needed.
616bd670b35SErik Nordmark 	 */
617bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	return (next);
6207c478bd9Sstevel@tonic-gate }
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate static uint_t
6237c478bd9Sstevel@tonic-gate igmpv3_query_in(igmp3qa_t *igmp3qa, ill_t *ill, int igmplen)
6247c478bd9Sstevel@tonic-gate {
6257c478bd9Sstevel@tonic-gate 	uint_t		i, next, mrd, qqi, timer, delay, numsrc;
6268dc47d9fSudpa 	uint_t		current;
6277c478bd9Sstevel@tonic-gate 	ilm_t		*ilm;
6287c478bd9Sstevel@tonic-gate 	ipaddr_t	*src_array;
6297c478bd9Sstevel@tonic-gate 	uint8_t		qrv;
630f4b3ec61Sdh 	ip_stack_t	 *ipst;
6317c478bd9Sstevel@tonic-gate 
632f4b3ec61Sdh 	ipst = ill->ill_ipst;
6337c478bd9Sstevel@tonic-gate 	/* make sure numsrc matches packet size */
6347c478bd9Sstevel@tonic-gate 	numsrc = ntohs(igmp3qa->igmp3qa_numsrc);
6357c478bd9Sstevel@tonic-gate 	if (igmplen < IGMP_V3_QUERY_MINLEN + (numsrc * sizeof (ipaddr_t))) {
636f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
6377c478bd9Sstevel@tonic-gate 		return (0);
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate 	src_array = (ipaddr_t *)&igmp3qa[1];
6407c478bd9Sstevel@tonic-gate 
641f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_rcv_queries;
6427c478bd9Sstevel@tonic-gate 
643bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
644bd670b35SErik Nordmark 
6457c478bd9Sstevel@tonic-gate 	if ((mrd = (uint_t)igmp3qa->igmp3qa_mxrc) >= IGMP_V3_MAXRT_FPMIN) {
6467c478bd9Sstevel@tonic-gate 		uint_t hdrval, mant, exp;
6477c478bd9Sstevel@tonic-gate 		hdrval = (uint_t)igmp3qa->igmp3qa_mxrc;
6487c478bd9Sstevel@tonic-gate 		mant = hdrval & IGMP_V3_MAXRT_MANT_MASK;
6497c478bd9Sstevel@tonic-gate 		exp = (hdrval & IGMP_V3_MAXRT_EXP_MASK) >> 4;
6507c478bd9Sstevel@tonic-gate 		mrd = (mant | 0x10) << (exp + 3);
6517c478bd9Sstevel@tonic-gate 	}
6527c478bd9Sstevel@tonic-gate 	if (mrd == 0)
6537c478bd9Sstevel@tonic-gate 		mrd = MCAST_DEF_QUERY_RESP_INTERVAL;
6547c478bd9Sstevel@tonic-gate 	timer = DSEC_TO_MSEC(mrd);
6557c478bd9Sstevel@tonic-gate 	MCAST_RANDOM_DELAY(delay, timer);
6567c478bd9Sstevel@tonic-gate 	next = (unsigned)INFINITY;
6578dc47d9fSudpa 	current = CURRENT_MSTIME;
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	if ((qrv = igmp3qa->igmp3qa_sqrv & IGMP_V3_RV_MASK) == 0)
6607c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = MCAST_DEF_ROBUSTNESS;
6617c478bd9Sstevel@tonic-gate 	else
6627c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = qrv;
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	if ((qqi = (uint_t)igmp3qa->igmp3qa_qqic) >= IGMP_V3_QQI_FPMIN) {
6657c478bd9Sstevel@tonic-gate 		uint_t hdrval, mant, exp;
6667c478bd9Sstevel@tonic-gate 		hdrval = (uint_t)igmp3qa->igmp3qa_qqic;
6677c478bd9Sstevel@tonic-gate 		mant = hdrval & IGMP_V3_QQI_MANT_MASK;
6687c478bd9Sstevel@tonic-gate 		exp = (hdrval & IGMP_V3_QQI_EXP_MASK) >> 4;
6697c478bd9Sstevel@tonic-gate 		qqi = (mant | 0x10) << (exp + 3);
6707c478bd9Sstevel@tonic-gate 	}
6717c478bd9Sstevel@tonic-gate 	ill->ill_mcast_qi = (qqi == 0) ? MCAST_DEF_QUERY_INTERVAL : qqi;
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	/*
6747c478bd9Sstevel@tonic-gate 	 * If we have a pending general query response that's scheduled
6757c478bd9Sstevel@tonic-gate 	 * sooner than the delay we calculated for this response, then
6767c478bd9Sstevel@tonic-gate 	 * no action is required (RFC3376 section 5.2 rule 1)
6777c478bd9Sstevel@tonic-gate 	 */
6788dc47d9fSudpa 	if (ill->ill_global_timer < (current + delay)) {
679bd670b35SErik Nordmark 		rw_exit(&ill->ill_mcast_lock);
680bd670b35SErik Nordmark 		ill_mcast_timer_start(ill->ill_ipst);
6817c478bd9Sstevel@tonic-gate 		return (next);
6827c478bd9Sstevel@tonic-gate 	}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	/*
6857c478bd9Sstevel@tonic-gate 	 * Now take action depending upon query type:
6867c478bd9Sstevel@tonic-gate 	 * general, group specific, or group/source specific.
6877c478bd9Sstevel@tonic-gate 	 */
6887c478bd9Sstevel@tonic-gate 	if ((numsrc == 0) && (igmp3qa->igmp3qa_group == INADDR_ANY)) {
6897c478bd9Sstevel@tonic-gate 		/*
6907c478bd9Sstevel@tonic-gate 		 * general query
6917c478bd9Sstevel@tonic-gate 		 * We know global timer is either not running or is
6927c478bd9Sstevel@tonic-gate 		 * greater than our calculated delay, so reset it to
6937c478bd9Sstevel@tonic-gate 		 * our delay (random value in range [0, response time]).
6947c478bd9Sstevel@tonic-gate 		 */
6958dc47d9fSudpa 		ill->ill_global_timer =  current + delay;
6968dc47d9fSudpa 		next = delay;
6977c478bd9Sstevel@tonic-gate 	} else {
6987c478bd9Sstevel@tonic-gate 		/* group or group/source specific query */
699bd670b35SErik Nordmark 		for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
7007c478bd9Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr) ||
7017c478bd9Sstevel@tonic-gate 			    (ilm->ilm_addr == htonl(INADDR_ANY)) ||
7027c478bd9Sstevel@tonic-gate 			    (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP)) ||
7037c478bd9Sstevel@tonic-gate 			    (igmp3qa->igmp3qa_group != ilm->ilm_addr))
7047c478bd9Sstevel@tonic-gate 				continue;
7057c478bd9Sstevel@tonic-gate 			/*
7067c478bd9Sstevel@tonic-gate 			 * If the query is group specific or we have a
7077c478bd9Sstevel@tonic-gate 			 * pending group specific query, the response is
7087c478bd9Sstevel@tonic-gate 			 * group specific (pending sources list should be
7097c478bd9Sstevel@tonic-gate 			 * empty).  Otherwise, need to update the pending
7107c478bd9Sstevel@tonic-gate 			 * sources list for the group and source specific
7117c478bd9Sstevel@tonic-gate 			 * response.
7127c478bd9Sstevel@tonic-gate 			 */
7137c478bd9Sstevel@tonic-gate 			if (numsrc == 0 || (ilm->ilm_timer < INFINITY &&
7147c478bd9Sstevel@tonic-gate 			    SLIST_IS_EMPTY(ilm->ilm_pendsrcs))) {
7157c478bd9Sstevel@tonic-gate group_query:
7167c478bd9Sstevel@tonic-gate 				FREE_SLIST(ilm->ilm_pendsrcs);
7177c478bd9Sstevel@tonic-gate 				ilm->ilm_pendsrcs = NULL;
7187c478bd9Sstevel@tonic-gate 			} else {
7197c478bd9Sstevel@tonic-gate 				boolean_t overflow;
7207c478bd9Sstevel@tonic-gate 				slist_t *pktl;
7217c478bd9Sstevel@tonic-gate 				if (numsrc > MAX_FILTER_SIZE ||
7227c478bd9Sstevel@tonic-gate 				    (ilm->ilm_pendsrcs == NULL &&
7237c478bd9Sstevel@tonic-gate 				    (ilm->ilm_pendsrcs = l_alloc()) == NULL)) {
7247c478bd9Sstevel@tonic-gate 					/*
7257c478bd9Sstevel@tonic-gate 					 * We've been sent more sources than
7267c478bd9Sstevel@tonic-gate 					 * we can deal with; or we can't deal
7277c478bd9Sstevel@tonic-gate 					 * with a source list at all.  Revert
7287c478bd9Sstevel@tonic-gate 					 * to a group specific query.
7297c478bd9Sstevel@tonic-gate 					 */
7307c478bd9Sstevel@tonic-gate 					goto group_query;
7317c478bd9Sstevel@tonic-gate 				}
7327c478bd9Sstevel@tonic-gate 				if ((pktl = l_alloc()) == NULL)
7337c478bd9Sstevel@tonic-gate 					goto group_query;
7347c478bd9Sstevel@tonic-gate 				pktl->sl_numsrc = numsrc;
7357c478bd9Sstevel@tonic-gate 				for (i = 0; i < numsrc; i++)
7367c478bd9Sstevel@tonic-gate 					IN6_IPADDR_TO_V4MAPPED(src_array[i],
7377c478bd9Sstevel@tonic-gate 					    &(pktl->sl_addr[i]));
7387c478bd9Sstevel@tonic-gate 				l_union_in_a(ilm->ilm_pendsrcs, pktl,
7397c478bd9Sstevel@tonic-gate 				    &overflow);
7407c478bd9Sstevel@tonic-gate 				l_free(pktl);
7417c478bd9Sstevel@tonic-gate 				if (overflow)
7427c478bd9Sstevel@tonic-gate 					goto group_query;
7437c478bd9Sstevel@tonic-gate 			}
7448dc47d9fSudpa 
7458dc47d9fSudpa 			ilm->ilm_timer = (ilm->ilm_timer == INFINITY) ?
7468dc47d9fSudpa 			    INFINITY : (ilm->ilm_timer - current);
7477c478bd9Sstevel@tonic-gate 			/* choose soonest timer */
7487c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = MIN(ilm->ilm_timer, delay);
7497c478bd9Sstevel@tonic-gate 			if (ilm->ilm_timer < next)
7507c478bd9Sstevel@tonic-gate 				next = ilm->ilm_timer;
7518dc47d9fSudpa 			ilm->ilm_timer += current;
7527c478bd9Sstevel@tonic-gate 		}
7537c478bd9Sstevel@tonic-gate 	}
754bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
755bd670b35SErik Nordmark 	/*
756bd670b35SErik Nordmark 	 * No packets have been sent above - no
757bd670b35SErik Nordmark 	 * ill_mcast_send_queued is needed.
758bd670b35SErik Nordmark 	 */
759bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	return (next);
7627c478bd9Sstevel@tonic-gate }
7637c478bd9Sstevel@tonic-gate 
764bd670b35SErik Nordmark /*
765bd670b35SErik Nordmark  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
766bd670b35SErik Nordmark  * and it gets sent after the lock is dropped.
767bd670b35SErik Nordmark  */
7687c478bd9Sstevel@tonic-gate void
7697c478bd9Sstevel@tonic-gate igmp_joingroup(ilm_t *ilm)
7707c478bd9Sstevel@tonic-gate {
7718dc47d9fSudpa 	uint_t	timer;
7727c478bd9Sstevel@tonic-gate 	ill_t	*ill;
773f4b3ec61Sdh 	ip_stack_t	*ipst = ilm->ilm_ipst;
7747c478bd9Sstevel@tonic-gate 
775bd670b35SErik Nordmark 	ill = ilm->ilm_ill;
7767c478bd9Sstevel@tonic-gate 
777bd670b35SErik Nordmark 	ASSERT(!ill->ill_isv6);
778bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 	if (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP)) {
7817c478bd9Sstevel@tonic-gate 		ilm->ilm_rtx.rtx_timer = INFINITY;
7827c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_OTHERMEMBER;
7837c478bd9Sstevel@tonic-gate 	} else {
7847c478bd9Sstevel@tonic-gate 		ip1dbg(("Querier mode %d, sending report, group %x\n",
7857c478bd9Sstevel@tonic-gate 		    ill->ill_mcast_type, htonl(ilm->ilm_addr)));
7867c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
7877c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
7887c478bd9Sstevel@tonic-gate 		} else if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
7897c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
7907c478bd9Sstevel@tonic-gate 		} else if (ill->ill_mcast_type == IGMP_V3_ROUTER) {
7917c478bd9Sstevel@tonic-gate 			mrec_t *rp;
7927c478bd9Sstevel@tonic-gate 			mcast_record_t rtype;
7937c478bd9Sstevel@tonic-gate 			/*
7947c478bd9Sstevel@tonic-gate 			 * The possible state changes we need to handle here:
7957c478bd9Sstevel@tonic-gate 			 *   Old State	New State	Report
7967c478bd9Sstevel@tonic-gate 			 *
7977c478bd9Sstevel@tonic-gate 			 *   INCLUDE(0)	INCLUDE(X)	ALLOW(X),BLOCK(0)
7987c478bd9Sstevel@tonic-gate 			 *   INCLUDE(0)	EXCLUDE(X)	TO_EX(X)
7997c478bd9Sstevel@tonic-gate 			 *
8007c478bd9Sstevel@tonic-gate 			 * No need to send the BLOCK(0) report; ALLOW(X)
8017c478bd9Sstevel@tonic-gate 			 * is enough.
8027c478bd9Sstevel@tonic-gate 			 */
8037c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
8047c478bd9Sstevel@tonic-gate 			    ALLOW_NEW_SOURCES : CHANGE_TO_EXCLUDE;
8057c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
8067c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
807bd670b35SErik Nordmark 			igmpv3_sendrpt(ill, rp);
8087c478bd9Sstevel@tonic-gate 			/*
8097c478bd9Sstevel@tonic-gate 			 * Set up retransmission state.  Timer is set below,
8107c478bd9Sstevel@tonic-gate 			 * for both v3 and older versions.
8117c478bd9Sstevel@tonic-gate 			 */
8127c478bd9Sstevel@tonic-gate 			mcast_init_rtx(ill, &ilm->ilm_rtx, rtype,
8137c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter);
8147c478bd9Sstevel@tonic-gate 		}
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 		/* Set the ilm timer value */
81770321377SErik Nordmark 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
8187c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
8197c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
8208dc47d9fSudpa 		timer = ilm->ilm_rtx.rtx_timer;
8218dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
8227c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 		/*
825bd670b35SErik Nordmark 		 * We are holding ill_mcast_lock here and the timeout
826bd670b35SErik Nordmark 		 * handler (igmp_timeout_handler_per_ill) acquires that
8270e0e37a8SErik Nordmark 		 * lock. Hence we can't call igmp_start_timers since it could
828bd670b35SErik Nordmark 		 * deadlock in untimeout().
829bd670b35SErik Nordmark 		 * Instead the thread which drops ill_mcast_lock will have
830bd670b35SErik Nordmark 		 * to call ill_mcast_timer_start().
8317c478bd9Sstevel@tonic-gate 		 */
832f4b3ec61Sdh 		mutex_enter(&ipst->ips_igmp_timer_lock);
8338dc47d9fSudpa 		ipst->ips_igmp_deferred_next = MIN(timer,
834f4b3ec61Sdh 		    ipst->ips_igmp_deferred_next);
835f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
8367c478bd9Sstevel@tonic-gate 	}
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
839bd670b35SErik Nordmark 		(void) mi_strlog(ilm->ilm_ill->ill_rq, 1, SL_TRACE,
8407c478bd9Sstevel@tonic-gate 		    "igmp_joingroup: multicast_type %d timer %d",
841bd670b35SErik Nordmark 		    (ilm->ilm_ill->ill_mcast_type),
8428dc47d9fSudpa 		    (int)ntohl(timer));
8437c478bd9Sstevel@tonic-gate 	}
8447c478bd9Sstevel@tonic-gate }
8457c478bd9Sstevel@tonic-gate 
846bd670b35SErik Nordmark /*
847bd670b35SErik Nordmark  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
848bd670b35SErik Nordmark  * and it gets sent after the lock is dropped.
849bd670b35SErik Nordmark  */
8507c478bd9Sstevel@tonic-gate void
8517c478bd9Sstevel@tonic-gate mld_joingroup(ilm_t *ilm)
8527c478bd9Sstevel@tonic-gate {
8538dc47d9fSudpa 	uint_t	timer;
8547c478bd9Sstevel@tonic-gate 	ill_t	*ill;
855f4b3ec61Sdh 	ip_stack_t	*ipst = ilm->ilm_ipst;
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	ill = ilm->ilm_ill;
8587c478bd9Sstevel@tonic-gate 
859bd670b35SErik Nordmark 	ASSERT(ill->ill_isv6);
860bd670b35SErik Nordmark 
861bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	if (IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr)) {
8647c478bd9Sstevel@tonic-gate 		ilm->ilm_rtx.rtx_timer = INFINITY;
8657c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_OTHERMEMBER;
8667c478bd9Sstevel@tonic-gate 	} else {
8677c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
8687c478bd9Sstevel@tonic-gate 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
8697c478bd9Sstevel@tonic-gate 		} else {
8707c478bd9Sstevel@tonic-gate 			mrec_t *rp;
8717c478bd9Sstevel@tonic-gate 			mcast_record_t rtype;
8727c478bd9Sstevel@tonic-gate 			/*
8737c478bd9Sstevel@tonic-gate 			 * The possible state changes we need to handle here:
8747c478bd9Sstevel@tonic-gate 			 *	Old State   New State	Report
8757c478bd9Sstevel@tonic-gate 			 *
8767c478bd9Sstevel@tonic-gate 			 *	INCLUDE(0)  INCLUDE(X)	ALLOW(X),BLOCK(0)
8777c478bd9Sstevel@tonic-gate 			 *	INCLUDE(0)  EXCLUDE(X)	TO_EX(X)
8787c478bd9Sstevel@tonic-gate 			 *
8797c478bd9Sstevel@tonic-gate 			 * No need to send the BLOCK(0) report; ALLOW(X)
8807c478bd9Sstevel@tonic-gate 			 * is enough
8817c478bd9Sstevel@tonic-gate 			 */
8827c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
8837c478bd9Sstevel@tonic-gate 			    ALLOW_NEW_SOURCES : CHANGE_TO_EXCLUDE;
8847c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
8857c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
8867c478bd9Sstevel@tonic-gate 			mldv2_sendrpt(ill, rp);
8877c478bd9Sstevel@tonic-gate 			/*
8887c478bd9Sstevel@tonic-gate 			 * Set up retransmission state.  Timer is set below,
8897c478bd9Sstevel@tonic-gate 			 * for both v2 and v1.
8907c478bd9Sstevel@tonic-gate 			 */
8917c478bd9Sstevel@tonic-gate 			mcast_init_rtx(ill, &ilm->ilm_rtx, rtype,
8927c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter);
8937c478bd9Sstevel@tonic-gate 		}
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 		/* Set the ilm timer value */
8967c478bd9Sstevel@tonic-gate 		ASSERT(ill->ill_mcast_type != MLD_V2_ROUTER ||
8977c478bd9Sstevel@tonic-gate 		    ilm->ilm_rtx.rtx_cnt > 0);
89870321377SErik Nordmark 
89970321377SErik Nordmark 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
9007c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
9017c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
9028dc47d9fSudpa 		timer = ilm->ilm_rtx.rtx_timer;
9038dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
9047c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		/*
907bd670b35SErik Nordmark 		 * We are holding ill_mcast_lock here and the timeout
908bd670b35SErik Nordmark 		 * handler (mld_timeout_handler_per_ill) acquires that
9090e0e37a8SErik Nordmark 		 * lock. Hence we can't call mld_start_timers since it could
910bd670b35SErik Nordmark 		 * deadlock in untimeout().
911bd670b35SErik Nordmark 		 * Instead the thread which drops ill_mcast_lock will have
912bd670b35SErik Nordmark 		 * to call ill_mcast_timer_start().
9137c478bd9Sstevel@tonic-gate 		 */
914f4b3ec61Sdh 		mutex_enter(&ipst->ips_mld_timer_lock);
9158dc47d9fSudpa 		ipst->ips_mld_deferred_next = MIN(timer,
916f4b3ec61Sdh 		    ipst->ips_mld_deferred_next);
917f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
9187c478bd9Sstevel@tonic-gate 	}
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
9217c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ilm->ilm_ill->ill_rq, 1, SL_TRACE,
9227c478bd9Sstevel@tonic-gate 		    "mld_joingroup: multicast_type %d timer %d",
9237c478bd9Sstevel@tonic-gate 		    (ilm->ilm_ill->ill_mcast_type),
9248dc47d9fSudpa 		    (int)ntohl(timer));
9257c478bd9Sstevel@tonic-gate 	}
9267c478bd9Sstevel@tonic-gate }
9277c478bd9Sstevel@tonic-gate 
928bd670b35SErik Nordmark /*
929bd670b35SErik Nordmark  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
930bd670b35SErik Nordmark  * and it gets sent after the lock is dropped.
931bd670b35SErik Nordmark  */
9327c478bd9Sstevel@tonic-gate void
9337c478bd9Sstevel@tonic-gate igmp_leavegroup(ilm_t *ilm)
9347c478bd9Sstevel@tonic-gate {
935bd670b35SErik Nordmark 	ill_t *ill = ilm->ilm_ill;
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	ASSERT(!ill->ill_isv6);
9387c478bd9Sstevel@tonic-gate 
939bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
9407c478bd9Sstevel@tonic-gate 	if (ilm->ilm_state == IGMP_IREPORTEDLAST &&
9417c478bd9Sstevel@tonic-gate 	    ill->ill_mcast_type == IGMP_V2_ROUTER &&
9427c478bd9Sstevel@tonic-gate 	    (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP))) {
9437c478bd9Sstevel@tonic-gate 		igmp_sendpkt(ilm, IGMP_V2_LEAVE_GROUP,
9447c478bd9Sstevel@tonic-gate 		    (htonl(INADDR_ALLRTRS_GROUP)));
9457c478bd9Sstevel@tonic-gate 		return;
946bd670b35SErik Nordmark 	}
947bd670b35SErik Nordmark 	if ((ill->ill_mcast_type == IGMP_V3_ROUTER) &&
9487c478bd9Sstevel@tonic-gate 	    (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP))) {
9497c478bd9Sstevel@tonic-gate 		mrec_t *rp;
9507c478bd9Sstevel@tonic-gate 		/*
9517c478bd9Sstevel@tonic-gate 		 * The possible state changes we need to handle here:
9527c478bd9Sstevel@tonic-gate 		 *	Old State	New State	Report
9537c478bd9Sstevel@tonic-gate 		 *
9547c478bd9Sstevel@tonic-gate 		 *	INCLUDE(X)	INCLUDE(0)	ALLOW(0),BLOCK(X)
9557c478bd9Sstevel@tonic-gate 		 *	EXCLUDE(X)	INCLUDE(0)	TO_IN(0)
9567c478bd9Sstevel@tonic-gate 		 *
9577c478bd9Sstevel@tonic-gate 		 * No need to send the ALLOW(0) report; BLOCK(X) is enough
9587c478bd9Sstevel@tonic-gate 		 */
9597c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
9607c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
9617c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
9627c478bd9Sstevel@tonic-gate 		} else {
9637c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr,
9647c478bd9Sstevel@tonic-gate 			    NULL, NULL);
9657c478bd9Sstevel@tonic-gate 		}
966bd670b35SErik Nordmark 		igmpv3_sendrpt(ill, rp);
9677c478bd9Sstevel@tonic-gate 		return;
9687c478bd9Sstevel@tonic-gate 	}
9697c478bd9Sstevel@tonic-gate }
9707c478bd9Sstevel@tonic-gate 
971bd670b35SErik Nordmark /*
972bd670b35SErik Nordmark  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
973bd670b35SErik Nordmark  * and it gets sent after the lock is dropped.
974bd670b35SErik Nordmark  */
9757c478bd9Sstevel@tonic-gate void
9767c478bd9Sstevel@tonic-gate mld_leavegroup(ilm_t *ilm)
9777c478bd9Sstevel@tonic-gate {
9787c478bd9Sstevel@tonic-gate 	ill_t *ill = ilm->ilm_ill;
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_isv6);
9817c478bd9Sstevel@tonic-gate 
982bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
9837c478bd9Sstevel@tonic-gate 	if (ilm->ilm_state == IGMP_IREPORTEDLAST &&
9847c478bd9Sstevel@tonic-gate 	    ill->ill_mcast_type == MLD_V1_ROUTER &&
9857c478bd9Sstevel@tonic-gate 	    (!IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr))) {
9867c478bd9Sstevel@tonic-gate 		mld_sendpkt(ilm, MLD_LISTENER_REDUCTION, &ipv6_all_rtrs_mcast);
9877c478bd9Sstevel@tonic-gate 		return;
988bd670b35SErik Nordmark 	}
989bd670b35SErik Nordmark 	if ((ill->ill_mcast_type == MLD_V2_ROUTER) &&
9907c478bd9Sstevel@tonic-gate 	    (!IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr))) {
9917c478bd9Sstevel@tonic-gate 		mrec_t *rp;
9927c478bd9Sstevel@tonic-gate 		/*
9937c478bd9Sstevel@tonic-gate 		 * The possible state changes we need to handle here:
9947c478bd9Sstevel@tonic-gate 		 *	Old State	New State	Report
9957c478bd9Sstevel@tonic-gate 		 *
9967c478bd9Sstevel@tonic-gate 		 *	INCLUDE(X)	INCLUDE(0)	ALLOW(0),BLOCK(X)
9977c478bd9Sstevel@tonic-gate 		 *	EXCLUDE(X)	INCLUDE(0)	TO_IN(0)
9987c478bd9Sstevel@tonic-gate 		 *
9997c478bd9Sstevel@tonic-gate 		 * No need to send the ALLOW(0) report; BLOCK(X) is enough
10007c478bd9Sstevel@tonic-gate 		 */
10017c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
10027c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
10037c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
10047c478bd9Sstevel@tonic-gate 		} else {
10057c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr,
10067c478bd9Sstevel@tonic-gate 			    NULL, NULL);
10077c478bd9Sstevel@tonic-gate 		}
10087c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rp);
10097c478bd9Sstevel@tonic-gate 		return;
10107c478bd9Sstevel@tonic-gate 	}
10117c478bd9Sstevel@tonic-gate }
10127c478bd9Sstevel@tonic-gate 
1013bd670b35SErik Nordmark /*
1014bd670b35SErik Nordmark  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
1015bd670b35SErik Nordmark  * and it gets sent after the lock is dropped.
1016bd670b35SErik Nordmark  */
10177c478bd9Sstevel@tonic-gate void
10187c478bd9Sstevel@tonic-gate igmp_statechange(ilm_t *ilm, mcast_record_t fmode, slist_t *flist)
10197c478bd9Sstevel@tonic-gate {
10207c478bd9Sstevel@tonic-gate 	ill_t *ill;
10217c478bd9Sstevel@tonic-gate 	mrec_t *rp;
1022f4b3ec61Sdh 	ip_stack_t	*ipst = ilm->ilm_ipst;
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	ASSERT(ilm != NULL);
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	/* state change reports should only be sent if the router is v3 */
1027bd670b35SErik Nordmark 	if (ilm->ilm_ill->ill_mcast_type != IGMP_V3_ROUTER)
10287c478bd9Sstevel@tonic-gate 		return;
10297c478bd9Sstevel@tonic-gate 
1030bd670b35SErik Nordmark 	ill = ilm->ilm_ill;
1031bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 	/*
10347c478bd9Sstevel@tonic-gate 	 * Compare existing(old) state with the new state and prepare
10357c478bd9Sstevel@tonic-gate 	 * State Change Report, according to the rules in RFC 3376:
10367c478bd9Sstevel@tonic-gate 	 *
10377c478bd9Sstevel@tonic-gate 	 *	Old State	New State	State Change Report
10387c478bd9Sstevel@tonic-gate 	 *
10397c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	INCLUDE(B)	ALLOW(B-A),BLOCK(A-B)
10407c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	EXCLUDE(B)	ALLOW(A-B),BLOCK(B-A)
10417c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	EXCLUDE(B)	TO_EX(B)
10427c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	INCLUDE(B)	TO_IN(B)
10437c478bd9Sstevel@tonic-gate 	 */
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate 	if (ilm->ilm_fmode == fmode) {
10467c478bd9Sstevel@tonic-gate 		slist_t	*a_minus_b = NULL, *b_minus_a = NULL;
10477c478bd9Sstevel@tonic-gate 		slist_t *allow, *block;
10487c478bd9Sstevel@tonic-gate 		if (((a_minus_b = l_alloc()) == NULL) ||
10497c478bd9Sstevel@tonic-gate 		    ((b_minus_a = l_alloc()) == NULL)) {
10507c478bd9Sstevel@tonic-gate 			l_free(a_minus_b);
10517c478bd9Sstevel@tonic-gate 			if (ilm->ilm_fmode == MODE_IS_INCLUDE)
10527c478bd9Sstevel@tonic-gate 				goto send_to_ex;
10537c478bd9Sstevel@tonic-gate 			else
10547c478bd9Sstevel@tonic-gate 				goto send_to_in;
10557c478bd9Sstevel@tonic-gate 		}
10567c478bd9Sstevel@tonic-gate 		l_difference(ilm->ilm_filter, flist, a_minus_b);
10577c478bd9Sstevel@tonic-gate 		l_difference(flist, ilm->ilm_filter, b_minus_a);
10587c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
10597c478bd9Sstevel@tonic-gate 			allow = b_minus_a;
10607c478bd9Sstevel@tonic-gate 			block = a_minus_b;
10617c478bd9Sstevel@tonic-gate 		} else {
10627c478bd9Sstevel@tonic-gate 			allow = a_minus_b;
10637c478bd9Sstevel@tonic-gate 			block = b_minus_a;
10647c478bd9Sstevel@tonic-gate 		}
10657c478bd9Sstevel@tonic-gate 		rp = NULL;
10667c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(allow))
10677c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(ALLOW_NEW_SOURCES, &ilm->ilm_v6addr,
10687c478bd9Sstevel@tonic-gate 			    allow, rp);
10697c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(block))
10707c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
10717c478bd9Sstevel@tonic-gate 			    block, rp);
10727c478bd9Sstevel@tonic-gate 		l_free(a_minus_b);
10737c478bd9Sstevel@tonic-gate 		l_free(b_minus_a);
10747c478bd9Sstevel@tonic-gate 	} else if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
10757c478bd9Sstevel@tonic-gate send_to_ex:
10767c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_EXCLUDE, &ilm->ilm_v6addr, flist,
10777c478bd9Sstevel@tonic-gate 		    NULL);
10787c478bd9Sstevel@tonic-gate 	} else {
10797c478bd9Sstevel@tonic-gate send_to_in:
10807c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr, flist,
10817c478bd9Sstevel@tonic-gate 		    NULL);
10827c478bd9Sstevel@tonic-gate 	}
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 	/*
10857c478bd9Sstevel@tonic-gate 	 * Need to set up retransmission state; merge the new info with the
10867c478bd9Sstevel@tonic-gate 	 * current state (which may be null).  If the timer is not currently
1087bd670b35SErik Nordmark 	 * running, the caller will start it when dropping ill_mcast_lock.
10887c478bd9Sstevel@tonic-gate 	 */
10897c478bd9Sstevel@tonic-gate 	rp = mcast_merge_rtx(ilm, rp, flist);
10907c478bd9Sstevel@tonic-gate 	if (ilm->ilm_rtx.rtx_timer == INFINITY) {
109170321377SErik Nordmark 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
10927c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
10937c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
1094f4b3ec61Sdh 		mutex_enter(&ipst->ips_igmp_timer_lock);
1095f4b3ec61Sdh 		ipst->ips_igmp_deferred_next = MIN(ipst->ips_igmp_deferred_next,
10967c478bd9Sstevel@tonic-gate 		    ilm->ilm_rtx.rtx_timer);
10978dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
1098f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
10997c478bd9Sstevel@tonic-gate 	}
11007c478bd9Sstevel@tonic-gate 
1101bd670b35SErik Nordmark 	igmpv3_sendrpt(ill, rp);
11027c478bd9Sstevel@tonic-gate }
11037c478bd9Sstevel@tonic-gate 
1104bd670b35SErik Nordmark /*
1105bd670b35SErik Nordmark  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
1106bd670b35SErik Nordmark  * and it gets sent after the lock is dropped.
1107bd670b35SErik Nordmark  */
11087c478bd9Sstevel@tonic-gate void
11097c478bd9Sstevel@tonic-gate mld_statechange(ilm_t *ilm, mcast_record_t fmode, slist_t *flist)
11107c478bd9Sstevel@tonic-gate {
11117c478bd9Sstevel@tonic-gate 	ill_t *ill;
11127c478bd9Sstevel@tonic-gate 	mrec_t *rp = NULL;
1113f4b3ec61Sdh 	ip_stack_t	*ipst = ilm->ilm_ipst;
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 	ASSERT(ilm != NULL);
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	ill = ilm->ilm_ill;
1118bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate 	/* only need to send if we have an mldv2-capable router */
11217c478bd9Sstevel@tonic-gate 	if (ill->ill_mcast_type != MLD_V2_ROUTER) {
11227c478bd9Sstevel@tonic-gate 		return;
11237c478bd9Sstevel@tonic-gate 	}
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	/*
11267c478bd9Sstevel@tonic-gate 	 * Compare existing (old) state with the new state passed in
11277c478bd9Sstevel@tonic-gate 	 * and send appropriate MLDv2 State Change Report.
11287c478bd9Sstevel@tonic-gate 	 *
11297c478bd9Sstevel@tonic-gate 	 *	Old State	New State	State Change Report
11307c478bd9Sstevel@tonic-gate 	 *
11317c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	INCLUDE(B)	ALLOW(B-A),BLOCK(A-B)
11327c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	EXCLUDE(B)	ALLOW(A-B),BLOCK(B-A)
11337c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	EXCLUDE(B)	TO_EX(B)
11347c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	INCLUDE(B)	TO_IN(B)
11357c478bd9Sstevel@tonic-gate 	 */
11367c478bd9Sstevel@tonic-gate 	if (ilm->ilm_fmode == fmode) {
11377c478bd9Sstevel@tonic-gate 		slist_t	*a_minus_b = NULL, *b_minus_a = NULL;
11387c478bd9Sstevel@tonic-gate 		slist_t *allow, *block;
11397c478bd9Sstevel@tonic-gate 		if (((a_minus_b = l_alloc()) == NULL) ||
11407c478bd9Sstevel@tonic-gate 		    ((b_minus_a = l_alloc()) == NULL)) {
11417c478bd9Sstevel@tonic-gate 			l_free(a_minus_b);
11427c478bd9Sstevel@tonic-gate 			if (ilm->ilm_fmode == MODE_IS_INCLUDE)
11437c478bd9Sstevel@tonic-gate 				goto send_to_ex;
11447c478bd9Sstevel@tonic-gate 			else
11457c478bd9Sstevel@tonic-gate 				goto send_to_in;
11467c478bd9Sstevel@tonic-gate 		}
11477c478bd9Sstevel@tonic-gate 		l_difference(ilm->ilm_filter, flist, a_minus_b);
11487c478bd9Sstevel@tonic-gate 		l_difference(flist, ilm->ilm_filter, b_minus_a);
11497c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
11507c478bd9Sstevel@tonic-gate 			allow = b_minus_a;
11517c478bd9Sstevel@tonic-gate 			block = a_minus_b;
11527c478bd9Sstevel@tonic-gate 		} else {
11537c478bd9Sstevel@tonic-gate 			allow = a_minus_b;
11547c478bd9Sstevel@tonic-gate 			block = b_minus_a;
11557c478bd9Sstevel@tonic-gate 		}
11567c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(allow))
11577c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(ALLOW_NEW_SOURCES, &ilm->ilm_v6addr,
11587c478bd9Sstevel@tonic-gate 			    allow, rp);
11597c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(block))
11607c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
11617c478bd9Sstevel@tonic-gate 			    block, rp);
11627c478bd9Sstevel@tonic-gate 		l_free(a_minus_b);
11637c478bd9Sstevel@tonic-gate 		l_free(b_minus_a);
11647c478bd9Sstevel@tonic-gate 	} else if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
11657c478bd9Sstevel@tonic-gate send_to_ex:
11667c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_EXCLUDE, &ilm->ilm_v6addr, flist,
11677c478bd9Sstevel@tonic-gate 		    NULL);
11687c478bd9Sstevel@tonic-gate 	} else {
11697c478bd9Sstevel@tonic-gate send_to_in:
11707c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr, flist,
11717c478bd9Sstevel@tonic-gate 		    NULL);
11727c478bd9Sstevel@tonic-gate 	}
11737c478bd9Sstevel@tonic-gate 
11747c478bd9Sstevel@tonic-gate 	/*
11757c478bd9Sstevel@tonic-gate 	 * Need to set up retransmission state; merge the new info with the
11767c478bd9Sstevel@tonic-gate 	 * current state (which may be null).  If the timer is not currently
1177bd670b35SErik Nordmark 	 * running, the caller will start it when dropping ill_mcast_lock.
11787c478bd9Sstevel@tonic-gate 	 */
11797c478bd9Sstevel@tonic-gate 	rp = mcast_merge_rtx(ilm, rp, flist);
11807c478bd9Sstevel@tonic-gate 	ASSERT(ilm->ilm_rtx.rtx_cnt > 0);
11817c478bd9Sstevel@tonic-gate 	if (ilm->ilm_rtx.rtx_timer == INFINITY) {
118270321377SErik Nordmark 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
11837c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
11847c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
1185f4b3ec61Sdh 		mutex_enter(&ipst->ips_mld_timer_lock);
1186f4b3ec61Sdh 		ipst->ips_mld_deferred_next =
1187f4b3ec61Sdh 		    MIN(ipst->ips_mld_deferred_next, ilm->ilm_rtx.rtx_timer);
11888dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
1189f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
11907c478bd9Sstevel@tonic-gate 	}
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate 	mldv2_sendrpt(ill, rp);
11937c478bd9Sstevel@tonic-gate }
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate uint_t
11968dc47d9fSudpa igmp_timeout_handler_per_ill(ill_t *ill)
11977c478bd9Sstevel@tonic-gate {
11988dc47d9fSudpa 	uint_t	next = INFINITY, current;
11997c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
12007c478bd9Sstevel@tonic-gate 	mrec_t	*rp = NULL;
12017c478bd9Sstevel@tonic-gate 	mrec_t	*rtxrp = NULL;
12027c478bd9Sstevel@tonic-gate 	rtx_state_t *rtxp;
12037c478bd9Sstevel@tonic-gate 	mcast_record_t	rtype;
12047c478bd9Sstevel@tonic-gate 
1205bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
12067c478bd9Sstevel@tonic-gate 
12078dc47d9fSudpa 	current = CURRENT_MSTIME;
12087c478bd9Sstevel@tonic-gate 	/* First check the global timer on this interface */
12097c478bd9Sstevel@tonic-gate 	if (ill->ill_global_timer == INFINITY)
12107c478bd9Sstevel@tonic-gate 		goto per_ilm_timer;
12118dc47d9fSudpa 	if (ill->ill_global_timer <= (current + CURRENT_OFFSET)) {
12127c478bd9Sstevel@tonic-gate 		ill->ill_global_timer = INFINITY;
12137c478bd9Sstevel@tonic-gate 		/*
12147c478bd9Sstevel@tonic-gate 		 * Send report for each group on this interface.
12157c478bd9Sstevel@tonic-gate 		 * Since we just set the global timer (received a v3 general
12167c478bd9Sstevel@tonic-gate 		 * query), need to skip the all hosts addr (224.0.0.1), per
12177c478bd9Sstevel@tonic-gate 		 * RFC 3376 section 5.
12187c478bd9Sstevel@tonic-gate 		 */
12197c478bd9Sstevel@tonic-gate 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
12207c478bd9Sstevel@tonic-gate 			if (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP))
12217c478bd9Sstevel@tonic-gate 				continue;
1222bd670b35SErik Nordmark 			rp = mcast_bldmrec(ilm->ilm_fmode, &ilm->ilm_v6addr,
1223bd670b35SErik Nordmark 			    ilm->ilm_filter, rp);
12247c478bd9Sstevel@tonic-gate 			/*
12257c478bd9Sstevel@tonic-gate 			 * Since we're sending a report on this group, okay
12267c478bd9Sstevel@tonic-gate 			 * to delete pending group-specific timers.  Note
12277c478bd9Sstevel@tonic-gate 			 * that group-specific retransmit timers still need
12287c478bd9Sstevel@tonic-gate 			 * to be checked in the per_ilm_timer for-loop.
12297c478bd9Sstevel@tonic-gate 			 */
12307c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = INFINITY;
12317c478bd9Sstevel@tonic-gate 			ilm->ilm_state = IGMP_IREPORTEDLAST;
12327c478bd9Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_pendsrcs);
12337c478bd9Sstevel@tonic-gate 			ilm->ilm_pendsrcs = NULL;
12347c478bd9Sstevel@tonic-gate 		}
1235bd670b35SErik Nordmark 		igmpv3_sendrpt(ill, rp);
1236bd670b35SErik Nordmark 		rp = NULL;
12377c478bd9Sstevel@tonic-gate 	} else {
12388dc47d9fSudpa 		if ((ill->ill_global_timer - current) < next)
12398dc47d9fSudpa 			next = ill->ill_global_timer - current;
12407c478bd9Sstevel@tonic-gate 	}
12417c478bd9Sstevel@tonic-gate 
12427c478bd9Sstevel@tonic-gate per_ilm_timer:
12437c478bd9Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
12447c478bd9Sstevel@tonic-gate 		if (ilm->ilm_timer == INFINITY)
12457c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
12467c478bd9Sstevel@tonic-gate 
12478dc47d9fSudpa 		if (ilm->ilm_timer > (current + CURRENT_OFFSET)) {
12488dc47d9fSudpa 			if ((ilm->ilm_timer - current) < next)
12498dc47d9fSudpa 				next = ilm->ilm_timer - current;
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 			if (ip_debug > 1) {
12527c478bd9Sstevel@tonic-gate 				(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
12538dc47d9fSudpa 				    "igmp_timo_hlr 2: ilm_timr %d "
12547c478bd9Sstevel@tonic-gate 				    "typ %d nxt %d",
12558dc47d9fSudpa 				    (int)ntohl(ilm->ilm_timer - current),
12567c478bd9Sstevel@tonic-gate 				    (ill->ill_mcast_type), next);
12577c478bd9Sstevel@tonic-gate 			}
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
12607c478bd9Sstevel@tonic-gate 		}
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 		/* the timer has expired, need to take action */
12637c478bd9Sstevel@tonic-gate 		ilm->ilm_timer = INFINITY;
12647c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
12657c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
12667c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
12677c478bd9Sstevel@tonic-gate 		} else if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
12687c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
12697c478bd9Sstevel@tonic-gate 		} else {
12707c478bd9Sstevel@tonic-gate 			slist_t *rsp;
12717c478bd9Sstevel@tonic-gate 			if (!SLIST_IS_EMPTY(ilm->ilm_pendsrcs) &&
12727c478bd9Sstevel@tonic-gate 			    (rsp = l_alloc()) != NULL) {
12737c478bd9Sstevel@tonic-gate 				/*
12747c478bd9Sstevel@tonic-gate 				 * Contents of reply depend on pending
12757c478bd9Sstevel@tonic-gate 				 * requested source list.
12767c478bd9Sstevel@tonic-gate 				 */
12777c478bd9Sstevel@tonic-gate 				if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
12787c478bd9Sstevel@tonic-gate 					l_intersection(ilm->ilm_filter,
12797c478bd9Sstevel@tonic-gate 					    ilm->ilm_pendsrcs, rsp);
12807c478bd9Sstevel@tonic-gate 				} else {
12817c478bd9Sstevel@tonic-gate 					l_difference(ilm->ilm_pendsrcs,
12827c478bd9Sstevel@tonic-gate 					    ilm->ilm_filter, rsp);
12837c478bd9Sstevel@tonic-gate 				}
12847c478bd9Sstevel@tonic-gate 				FREE_SLIST(ilm->ilm_pendsrcs);
12857c478bd9Sstevel@tonic-gate 				ilm->ilm_pendsrcs = NULL;
12867c478bd9Sstevel@tonic-gate 				if (!SLIST_IS_EMPTY(rsp))
12877c478bd9Sstevel@tonic-gate 					rp = mcast_bldmrec(MODE_IS_INCLUDE,
12887c478bd9Sstevel@tonic-gate 					    &ilm->ilm_v6addr, rsp, rp);
12897c478bd9Sstevel@tonic-gate 				FREE_SLIST(rsp);
12907c478bd9Sstevel@tonic-gate 			} else {
12917c478bd9Sstevel@tonic-gate 				/*
12927c478bd9Sstevel@tonic-gate 				 * Either the pending request is just group-
12937c478bd9Sstevel@tonic-gate 				 * specific, or we couldn't get the resources
12947c478bd9Sstevel@tonic-gate 				 * (rsp) to build a source-specific reply.
12957c478bd9Sstevel@tonic-gate 				 */
12967c478bd9Sstevel@tonic-gate 				rp = mcast_bldmrec(ilm->ilm_fmode,
12977c478bd9Sstevel@tonic-gate 				    &ilm->ilm_v6addr, ilm->ilm_filter, rp);
12987c478bd9Sstevel@tonic-gate 			}
1299bd670b35SErik Nordmark 			igmpv3_sendrpt(ill, rp);
13007c478bd9Sstevel@tonic-gate 			rp = NULL;
13017c478bd9Sstevel@tonic-gate 		}
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate per_ilm_rtxtimer:
13047c478bd9Sstevel@tonic-gate 		rtxp = &ilm->ilm_rtx;
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_timer == INFINITY)
13077c478bd9Sstevel@tonic-gate 			continue;
13088dc47d9fSudpa 		if (rtxp->rtx_timer > (current + CURRENT_OFFSET)) {
13098dc47d9fSudpa 			if ((rtxp->rtx_timer - current) < next)
13108dc47d9fSudpa 				next = rtxp->rtx_timer - current;
13117c478bd9Sstevel@tonic-gate 			continue;
13127c478bd9Sstevel@tonic-gate 		}
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate 		rtxp->rtx_timer = INFINITY;
13157c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
13167c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
13177c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
13187c478bd9Sstevel@tonic-gate 			continue;
1319bd670b35SErik Nordmark 		}
1320bd670b35SErik Nordmark 		if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
13217c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
13227c478bd9Sstevel@tonic-gate 			continue;
13237c478bd9Sstevel@tonic-gate 		}
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 		/*
13267c478bd9Sstevel@tonic-gate 		 * The retransmit timer has popped, and our router is
13277c478bd9Sstevel@tonic-gate 		 * IGMPv3.  We have to delve into the retransmit state
13287c478bd9Sstevel@tonic-gate 		 * stored in the ilm.
13297c478bd9Sstevel@tonic-gate 		 *
13307c478bd9Sstevel@tonic-gate 		 * Decrement the retransmit count.  If the fmode rtx
13317c478bd9Sstevel@tonic-gate 		 * count is active, decrement it, and send a filter
13327c478bd9Sstevel@tonic-gate 		 * mode change report with the ilm's source list.
13337c478bd9Sstevel@tonic-gate 		 * Otherwise, send a source list change report with
13347c478bd9Sstevel@tonic-gate 		 * the current retransmit lists.
13357c478bd9Sstevel@tonic-gate 		 */
13367c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt > 0);
13377c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt >= rtxp->rtx_fmode_cnt);
13387c478bd9Sstevel@tonic-gate 		rtxp->rtx_cnt--;
13397c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_fmode_cnt > 0) {
13407c478bd9Sstevel@tonic-gate 			rtxp->rtx_fmode_cnt--;
13417c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
13427c478bd9Sstevel@tonic-gate 			    CHANGE_TO_INCLUDE : CHANGE_TO_EXCLUDE;
13437c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
13447c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, rtxrp);
13457c478bd9Sstevel@tonic-gate 		} else {
13467c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(ALLOW_NEW_SOURCES,
13477c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_allow, rtxrp);
13487c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(BLOCK_OLD_SOURCES,
13497c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_block, rtxrp);
13507c478bd9Sstevel@tonic-gate 		}
13517c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_cnt > 0) {
13527c478bd9Sstevel@tonic-gate 			MCAST_RANDOM_DELAY(rtxp->rtx_timer,
13537c478bd9Sstevel@tonic-gate 			    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
13547c478bd9Sstevel@tonic-gate 			if (rtxp->rtx_timer < next)
13557c478bd9Sstevel@tonic-gate 				next = rtxp->rtx_timer;
13568dc47d9fSudpa 			rtxp->rtx_timer += current;
13577c478bd9Sstevel@tonic-gate 		} else {
135870321377SErik Nordmark 			ASSERT(rtxp->rtx_timer == INFINITY);
13597c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_allow);
13607c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_block);
13617c478bd9Sstevel@tonic-gate 		}
1362bd670b35SErik Nordmark 		igmpv3_sendrpt(ill, rtxrp);
13637c478bd9Sstevel@tonic-gate 		rtxrp = NULL;
13647c478bd9Sstevel@tonic-gate 	}
13657c478bd9Sstevel@tonic-gate 
1366bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
1367bd670b35SErik Nordmark 	/* Send any deferred/queued IP packets */
1368bd670b35SErik Nordmark 	ill_mcast_send_queued(ill);
1369bd670b35SErik Nordmark 	/* Defer ill_mcast_timer_start() until the caller is done */
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate 	return (next);
13727c478bd9Sstevel@tonic-gate }
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate /*
13757c478bd9Sstevel@tonic-gate  * igmp_timeout_handler:
13767c478bd9Sstevel@tonic-gate  * Called when there are timeout events, every next * TMEOUT_INTERVAL (tick).
13777c478bd9Sstevel@tonic-gate  * Returns number of ticks to next event (or 0 if none).
13787c478bd9Sstevel@tonic-gate  *
13797c478bd9Sstevel@tonic-gate  * As part of multicast join and leave igmp we may need to send out an
13807c478bd9Sstevel@tonic-gate  * igmp request. The igmp related state variables in the ilm are protected
1381bd670b35SErik Nordmark  * by ill_mcast_lock. A single global igmp timer is used to track igmp timeouts.
13827c478bd9Sstevel@tonic-gate  * igmp_timer_lock protects the global igmp_timeout_id. igmp_start_timers
13837c478bd9Sstevel@tonic-gate  * starts the igmp timer if needed. It serializes multiple threads trying to
13847c478bd9Sstevel@tonic-gate  * simultaneously start the timer using the igmp_timer_setter_active flag.
13857c478bd9Sstevel@tonic-gate  *
13867c478bd9Sstevel@tonic-gate  * igmp_input() receives igmp queries and responds to the queries
13877c478bd9Sstevel@tonic-gate  * in a delayed fashion by posting a timer i.e. it calls igmp_start_timers().
1388bd670b35SErik Nordmark  * Later the igmp_timer fires, the timeout handler igmp_timerout_handler()
1389bd670b35SErik Nordmark  * performs the action exclusively after acquiring ill_mcast_lock.
13907c478bd9Sstevel@tonic-gate  *
13917c478bd9Sstevel@tonic-gate  * The igmp_slowtimeo() function is called thru another timer.
13927c478bd9Sstevel@tonic-gate  * igmp_slowtimeout_lock protects the igmp_slowtimeout_id
13937c478bd9Sstevel@tonic-gate  */
13947c478bd9Sstevel@tonic-gate void
13957c478bd9Sstevel@tonic-gate igmp_timeout_handler(void *arg)
13967c478bd9Sstevel@tonic-gate {
13977c478bd9Sstevel@tonic-gate 	ill_t	*ill;
13987c478bd9Sstevel@tonic-gate 	uint_t  global_next = INFINITY;
13997c478bd9Sstevel@tonic-gate 	uint_t  next;
14007c478bd9Sstevel@tonic-gate 	ill_walk_context_t ctx;
1401328c7d1fSmeem 	ip_stack_t *ipst = arg;
1402f4b3ec61Sdh 
1403f4b3ec61Sdh 	ASSERT(arg != NULL);
1404f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1405f4b3ec61Sdh 	ASSERT(ipst->ips_igmp_timeout_id != 0);
1406bd670b35SErik Nordmark 	ipst->ips_igmp_timeout_id = 0;
14078dc47d9fSudpa 	ipst->ips_igmp_timer_scheduled_last = 0;
1408f4b3ec61Sdh 	ipst->ips_igmp_time_to_next = 0;
1409f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
1410f4b3ec61Sdh 
1411f4b3ec61Sdh 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
1412f4b3ec61Sdh 	ill = ILL_START_WALK_V4(&ctx, ipst);
14137c478bd9Sstevel@tonic-gate 	for (; ill != NULL; ill = ill_next(&ctx, ill)) {
14147c478bd9Sstevel@tonic-gate 		ASSERT(!ill->ill_isv6);
1415bd670b35SErik Nordmark 		/* Make sure the ill isn't going away. */
1416bd670b35SErik Nordmark 		if (!ill_check_and_refhold(ill))
14177c478bd9Sstevel@tonic-gate 			continue;
1418f4b3ec61Sdh 		rw_exit(&ipst->ips_ill_g_lock);
1419bd670b35SErik Nordmark 		next = igmp_timeout_handler_per_ill(ill);
1420bd670b35SErik Nordmark 		if (next < global_next)
1421bd670b35SErik Nordmark 			global_next = next;
1422bd670b35SErik Nordmark 		ill_refrele(ill);
1423f4b3ec61Sdh 		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
14247c478bd9Sstevel@tonic-gate 	}
1425f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
14267c478bd9Sstevel@tonic-gate 	if (global_next != INFINITY)
1427f4b3ec61Sdh 		igmp_start_timers(global_next, ipst);
14287c478bd9Sstevel@tonic-gate }
14297c478bd9Sstevel@tonic-gate 
14307c478bd9Sstevel@tonic-gate /*
14317c478bd9Sstevel@tonic-gate  * mld_timeout_handler:
14327c478bd9Sstevel@tonic-gate  * Called when there are timeout events, every next (tick).
14337c478bd9Sstevel@tonic-gate  * Returns number of ticks to next event (or 0 if none).
14347c478bd9Sstevel@tonic-gate  */
14357c478bd9Sstevel@tonic-gate uint_t
14368dc47d9fSudpa mld_timeout_handler_per_ill(ill_t *ill)
14377c478bd9Sstevel@tonic-gate {
14387c478bd9Sstevel@tonic-gate 	ilm_t 	*ilm;
14398dc47d9fSudpa 	uint_t	next = INFINITY, current;
14407c478bd9Sstevel@tonic-gate 	mrec_t	*rp, *rtxrp;
14417c478bd9Sstevel@tonic-gate 	rtx_state_t *rtxp;
14427c478bd9Sstevel@tonic-gate 	mcast_record_t	rtype;
14437c478bd9Sstevel@tonic-gate 
1444bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
14457c478bd9Sstevel@tonic-gate 
14468dc47d9fSudpa 	current = CURRENT_MSTIME;
14477c478bd9Sstevel@tonic-gate 	/*
14487c478bd9Sstevel@tonic-gate 	 * First check the global timer on this interface; the global timer
14497c478bd9Sstevel@tonic-gate 	 * is not used for MLDv1, so if it's set we can assume we're v2.
14507c478bd9Sstevel@tonic-gate 	 */
14517c478bd9Sstevel@tonic-gate 	if (ill->ill_global_timer == INFINITY)
14527c478bd9Sstevel@tonic-gate 		goto per_ilm_timer;
14538dc47d9fSudpa 	if (ill->ill_global_timer <= (current + CURRENT_OFFSET)) {
14547c478bd9Sstevel@tonic-gate 		ill->ill_global_timer = INFINITY;
14557c478bd9Sstevel@tonic-gate 		/*
14567c478bd9Sstevel@tonic-gate 		 * Send report for each group on this interface.
14577c478bd9Sstevel@tonic-gate 		 * Since we just set the global timer (received a v2 general
14587c478bd9Sstevel@tonic-gate 		 * query), need to skip the all hosts addr (ff02::1), per
14597c478bd9Sstevel@tonic-gate 		 * RFC 3810 section 6.
14607c478bd9Sstevel@tonic-gate 		 */
14617c478bd9Sstevel@tonic-gate 		rp = NULL;
14627c478bd9Sstevel@tonic-gate 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
14637c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr,
14647c478bd9Sstevel@tonic-gate 			    &ipv6_all_hosts_mcast))
14657c478bd9Sstevel@tonic-gate 				continue;
14667c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(ilm->ilm_fmode, &ilm->ilm_v6addr,
14677c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, rp);
14687c478bd9Sstevel@tonic-gate 			/*
14697c478bd9Sstevel@tonic-gate 			 * Since we're sending a report on this group, okay
14707c478bd9Sstevel@tonic-gate 			 * to delete pending group-specific timers.  Note
14717c478bd9Sstevel@tonic-gate 			 * that group-specific retransmit timers still need
14727c478bd9Sstevel@tonic-gate 			 * to be checked in the per_ilm_timer for-loop.
14737c478bd9Sstevel@tonic-gate 			 */
14747c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = INFINITY;
14757c478bd9Sstevel@tonic-gate 			ilm->ilm_state = IGMP_IREPORTEDLAST;
14767c478bd9Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_pendsrcs);
14777c478bd9Sstevel@tonic-gate 			ilm->ilm_pendsrcs = NULL;
14787c478bd9Sstevel@tonic-gate 		}
14797c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rp);
14807c478bd9Sstevel@tonic-gate 	} else {
14818dc47d9fSudpa 		if ((ill->ill_global_timer - current) < next)
14828dc47d9fSudpa 			next = ill->ill_global_timer - current;
14837c478bd9Sstevel@tonic-gate 	}
14847c478bd9Sstevel@tonic-gate 
14857c478bd9Sstevel@tonic-gate per_ilm_timer:
14867c478bd9Sstevel@tonic-gate 	rp = rtxrp = NULL;
14877c478bd9Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
14887c478bd9Sstevel@tonic-gate 		if (ilm->ilm_timer == INFINITY)
14897c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
14907c478bd9Sstevel@tonic-gate 
14918dc47d9fSudpa 		if (ilm->ilm_timer > (current + CURRENT_OFFSET)) {
14928dc47d9fSudpa 			if ((ilm->ilm_timer - current) < next)
14938dc47d9fSudpa 				next = ilm->ilm_timer - current;
14947c478bd9Sstevel@tonic-gate 
14957c478bd9Sstevel@tonic-gate 			if (ip_debug > 1) {
14967c478bd9Sstevel@tonic-gate 				(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
14977c478bd9Sstevel@tonic-gate 				    "igmp_timo_hlr 2: ilm_timr"
14988dc47d9fSudpa 				    " %d typ %d nxt %d",
14998dc47d9fSudpa 				    (int)ntohl(ilm->ilm_timer - current),
15007c478bd9Sstevel@tonic-gate 				    (ill->ill_mcast_type), next);
15017c478bd9Sstevel@tonic-gate 			}
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
15047c478bd9Sstevel@tonic-gate 		}
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate 		/* the timer has expired, need to take action */
15077c478bd9Sstevel@tonic-gate 		ilm->ilm_timer = INFINITY;
15087c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
15097c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
15107c478bd9Sstevel@tonic-gate 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
15117c478bd9Sstevel@tonic-gate 		} else {
15127c478bd9Sstevel@tonic-gate 			slist_t *rsp;
15137c478bd9Sstevel@tonic-gate 			if (!SLIST_IS_EMPTY(ilm->ilm_pendsrcs) &&
15147c478bd9Sstevel@tonic-gate 			    (rsp = l_alloc()) != NULL) {
15157c478bd9Sstevel@tonic-gate 				/*
15167c478bd9Sstevel@tonic-gate 				 * Contents of reply depend on pending
15177c478bd9Sstevel@tonic-gate 				 * requested source list.
15187c478bd9Sstevel@tonic-gate 				 */
15197c478bd9Sstevel@tonic-gate 				if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
15207c478bd9Sstevel@tonic-gate 					l_intersection(ilm->ilm_filter,
15217c478bd9Sstevel@tonic-gate 					    ilm->ilm_pendsrcs, rsp);
15227c478bd9Sstevel@tonic-gate 				} else {
15237c478bd9Sstevel@tonic-gate 					l_difference(ilm->ilm_pendsrcs,
15247c478bd9Sstevel@tonic-gate 					    ilm->ilm_filter, rsp);
15257c478bd9Sstevel@tonic-gate 				}
15267c478bd9Sstevel@tonic-gate 				FREE_SLIST(ilm->ilm_pendsrcs);
15277c478bd9Sstevel@tonic-gate 				ilm->ilm_pendsrcs = NULL;
15287c478bd9Sstevel@tonic-gate 				if (!SLIST_IS_EMPTY(rsp))
15297c478bd9Sstevel@tonic-gate 					rp = mcast_bldmrec(MODE_IS_INCLUDE,
15307c478bd9Sstevel@tonic-gate 					    &ilm->ilm_v6addr, rsp, rp);
15317c478bd9Sstevel@tonic-gate 				FREE_SLIST(rsp);
15327c478bd9Sstevel@tonic-gate 			} else {
15337c478bd9Sstevel@tonic-gate 				rp = mcast_bldmrec(ilm->ilm_fmode,
15347c478bd9Sstevel@tonic-gate 				    &ilm->ilm_v6addr, ilm->ilm_filter, rp);
15357c478bd9Sstevel@tonic-gate 			}
15367c478bd9Sstevel@tonic-gate 		}
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate per_ilm_rtxtimer:
15397c478bd9Sstevel@tonic-gate 		rtxp = &ilm->ilm_rtx;
15407c478bd9Sstevel@tonic-gate 
15417c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_timer == INFINITY)
15427c478bd9Sstevel@tonic-gate 			continue;
15438dc47d9fSudpa 		if (rtxp->rtx_timer > (current + CURRENT_OFFSET)) {
15448dc47d9fSudpa 			if ((rtxp->rtx_timer - current) < next)
15458dc47d9fSudpa 				next = rtxp->rtx_timer - current;
15467c478bd9Sstevel@tonic-gate 			continue;
15477c478bd9Sstevel@tonic-gate 		}
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 		rtxp->rtx_timer = INFINITY;
15507c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
15517c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
15527c478bd9Sstevel@tonic-gate 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
15537c478bd9Sstevel@tonic-gate 			continue;
15547c478bd9Sstevel@tonic-gate 		}
15557c478bd9Sstevel@tonic-gate 
15567c478bd9Sstevel@tonic-gate 		/*
15577c478bd9Sstevel@tonic-gate 		 * The retransmit timer has popped, and our router is
15587c478bd9Sstevel@tonic-gate 		 * MLDv2.  We have to delve into the retransmit state
15597c478bd9Sstevel@tonic-gate 		 * stored in the ilm.
15607c478bd9Sstevel@tonic-gate 		 *
15617c478bd9Sstevel@tonic-gate 		 * Decrement the retransmit count.  If the fmode rtx
15627c478bd9Sstevel@tonic-gate 		 * count is active, decrement it, and send a filter
15637c478bd9Sstevel@tonic-gate 		 * mode change report with the ilm's source list.
15647c478bd9Sstevel@tonic-gate 		 * Otherwise, send a source list change report with
15657c478bd9Sstevel@tonic-gate 		 * the current retransmit lists.
15667c478bd9Sstevel@tonic-gate 		 */
15677c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt > 0);
15687c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt >= rtxp->rtx_fmode_cnt);
15697c478bd9Sstevel@tonic-gate 		rtxp->rtx_cnt--;
15707c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_fmode_cnt > 0) {
15717c478bd9Sstevel@tonic-gate 			rtxp->rtx_fmode_cnt--;
15727c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
15737c478bd9Sstevel@tonic-gate 			    CHANGE_TO_INCLUDE : CHANGE_TO_EXCLUDE;
15747c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
15757c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, rtxrp);
15767c478bd9Sstevel@tonic-gate 		} else {
15777c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(ALLOW_NEW_SOURCES,
15787c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_allow, rtxrp);
15797c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(BLOCK_OLD_SOURCES,
15807c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_block, rtxrp);
15817c478bd9Sstevel@tonic-gate 		}
15827c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_cnt > 0) {
15837c478bd9Sstevel@tonic-gate 			MCAST_RANDOM_DELAY(rtxp->rtx_timer,
15847c478bd9Sstevel@tonic-gate 			    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
15857c478bd9Sstevel@tonic-gate 			if (rtxp->rtx_timer < next)
15867c478bd9Sstevel@tonic-gate 				next = rtxp->rtx_timer;
15878dc47d9fSudpa 			rtxp->rtx_timer += current;
15887c478bd9Sstevel@tonic-gate 		} else {
158970321377SErik Nordmark 			ASSERT(rtxp->rtx_timer == INFINITY);
15907c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_allow);
15917c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_block);
15927c478bd9Sstevel@tonic-gate 		}
15937c478bd9Sstevel@tonic-gate 	}
15947c478bd9Sstevel@tonic-gate 
15957c478bd9Sstevel@tonic-gate 	if (ill->ill_mcast_type == MLD_V2_ROUTER) {
15967c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rp);
15977c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rtxrp);
15987c478bd9Sstevel@tonic-gate 	}
1599bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
1600bd670b35SErik Nordmark 	/* Send any deferred/queued IP packets */
1601bd670b35SErik Nordmark 	ill_mcast_send_queued(ill);
1602bd670b35SErik Nordmark 	/* Defer ill_mcast_timer_start() until the caller is done */
16037c478bd9Sstevel@tonic-gate 
16047c478bd9Sstevel@tonic-gate 	return (next);
16057c478bd9Sstevel@tonic-gate }
16067c478bd9Sstevel@tonic-gate 
16077c478bd9Sstevel@tonic-gate /*
16087c478bd9Sstevel@tonic-gate  * mld_timeout_handler:
16097c478bd9Sstevel@tonic-gate  * Called when there are timeout events, every next * TMEOUT_INTERVAL (tick).
16107c478bd9Sstevel@tonic-gate  * Returns number of ticks to next event (or 0 if none).
16117c478bd9Sstevel@tonic-gate  * MT issues are same as igmp_timeout_handler
16127c478bd9Sstevel@tonic-gate  */
16137c478bd9Sstevel@tonic-gate void
16147c478bd9Sstevel@tonic-gate mld_timeout_handler(void *arg)
16157c478bd9Sstevel@tonic-gate {
16167c478bd9Sstevel@tonic-gate 	ill_t	*ill;
16177c478bd9Sstevel@tonic-gate 	uint_t  global_next = INFINITY;
16187c478bd9Sstevel@tonic-gate 	uint_t  next;
16197c478bd9Sstevel@tonic-gate 	ill_walk_context_t ctx;
1620328c7d1fSmeem 	ip_stack_t *ipst = arg;
1621f4b3ec61Sdh 
1622f4b3ec61Sdh 	ASSERT(arg != NULL);
1623f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
1624f4b3ec61Sdh 	ASSERT(ipst->ips_mld_timeout_id != 0);
1625bd670b35SErik Nordmark 	ipst->ips_mld_timeout_id = 0;
16268dc47d9fSudpa 	ipst->ips_mld_timer_scheduled_last = 0;
1627f4b3ec61Sdh 	ipst->ips_mld_time_to_next = 0;
1628f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
1629f4b3ec61Sdh 
1630f4b3ec61Sdh 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
1631f4b3ec61Sdh 	ill = ILL_START_WALK_V6(&ctx, ipst);
16327c478bd9Sstevel@tonic-gate 	for (; ill != NULL; ill = ill_next(&ctx, ill)) {
16337c478bd9Sstevel@tonic-gate 		ASSERT(ill->ill_isv6);
1634bd670b35SErik Nordmark 		/* Make sure the ill isn't going away. */
1635bd670b35SErik Nordmark 		if (!ill_check_and_refhold(ill))
16367c478bd9Sstevel@tonic-gate 			continue;
1637f4b3ec61Sdh 		rw_exit(&ipst->ips_ill_g_lock);
1638bd670b35SErik Nordmark 		next = mld_timeout_handler_per_ill(ill);
1639bd670b35SErik Nordmark 		if (next < global_next)
1640bd670b35SErik Nordmark 			global_next = next;
1641bd670b35SErik Nordmark 		ill_refrele(ill);
1642f4b3ec61Sdh 		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
16437c478bd9Sstevel@tonic-gate 	}
1644f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
16457c478bd9Sstevel@tonic-gate 	if (global_next != INFINITY)
1646f4b3ec61Sdh 		mld_start_timers(global_next, ipst);
16477c478bd9Sstevel@tonic-gate }
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate /*
16507c478bd9Sstevel@tonic-gate  * Calculate the Older Version Querier Present timeout value, in number
16517c478bd9Sstevel@tonic-gate  * of slowtimo intervals, for the given ill.
16527c478bd9Sstevel@tonic-gate  */
16537c478bd9Sstevel@tonic-gate #define	OVQP(ill) \
16547c478bd9Sstevel@tonic-gate 	((1000 * (((ill)->ill_mcast_rv * (ill)->ill_mcast_qi) \
16557c478bd9Sstevel@tonic-gate 	+ MCAST_QUERY_RESP_INTERVAL)) / MCAST_SLOWTIMO_INTERVAL)
16567c478bd9Sstevel@tonic-gate 
16577c478bd9Sstevel@tonic-gate /*
16587c478bd9Sstevel@tonic-gate  * igmp_slowtimo:
16597c478bd9Sstevel@tonic-gate  * - Resets to new router if we didnt we hear from the router
16607c478bd9Sstevel@tonic-gate  *   in IGMP_AGE_THRESHOLD seconds.
16617c478bd9Sstevel@tonic-gate  * - Resets slowtimeout.
16628dc47d9fSudpa  * Check for ips_igmp_max_version ensures that we don't revert to a higher
16638dc47d9fSudpa  * IGMP version than configured.
16647c478bd9Sstevel@tonic-gate  */
16657c478bd9Sstevel@tonic-gate void
16667c478bd9Sstevel@tonic-gate igmp_slowtimo(void *arg)
16677c478bd9Sstevel@tonic-gate {
16687c478bd9Sstevel@tonic-gate 	ill_t	*ill;
16697c478bd9Sstevel@tonic-gate 	ill_if_t *ifp;
16707c478bd9Sstevel@tonic-gate 	avl_tree_t *avl_tree;
1671f4b3ec61Sdh 	ip_stack_t *ipst = (ip_stack_t *)arg;
16727c478bd9Sstevel@tonic-gate 
1673f4b3ec61Sdh 	ASSERT(arg != NULL);
16747c478bd9Sstevel@tonic-gate 
16757c478bd9Sstevel@tonic-gate 	/*
16767c478bd9Sstevel@tonic-gate 	 * The ill_if_t list is circular, hence the odd loop parameters.
16777c478bd9Sstevel@tonic-gate 	 *
16787c478bd9Sstevel@tonic-gate 	 * We can't use the ILL_START_WALK and ill_next() wrappers for this
16797c478bd9Sstevel@tonic-gate 	 * walk, as we need to check the illif_mcast_* fields in the ill_if_t
16807c478bd9Sstevel@tonic-gate 	 * structure (allowing us to skip if none of the instances have timers
16817c478bd9Sstevel@tonic-gate 	 * running).
16827c478bd9Sstevel@tonic-gate 	 */
1683bd670b35SErik Nordmark 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
1684f4b3ec61Sdh 	for (ifp = IP_V4_ILL_G_LIST(ipst);
1685f4b3ec61Sdh 	    ifp != (ill_if_t *)&IP_V4_ILL_G_LIST(ipst);
16867c478bd9Sstevel@tonic-gate 	    ifp = ifp->illif_next) {
16877c478bd9Sstevel@tonic-gate 		/*
16887c478bd9Sstevel@tonic-gate 		 * illif_mcast_v[12] are set using atomics. If an ill hears
16897c478bd9Sstevel@tonic-gate 		 * a V1 or V2 query now and we miss seeing the count now,
16907c478bd9Sstevel@tonic-gate 		 * we will see it the next time igmp_slowtimo is called.
16917c478bd9Sstevel@tonic-gate 		 */
16927c478bd9Sstevel@tonic-gate 		if (ifp->illif_mcast_v1 == 0 && ifp->illif_mcast_v2 == 0)
16937c478bd9Sstevel@tonic-gate 			continue;
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 		avl_tree = &ifp->illif_avl_by_ppa;
16967c478bd9Sstevel@tonic-gate 		for (ill = avl_first(avl_tree); ill != NULL;
16977c478bd9Sstevel@tonic-gate 		    ill = avl_walk(avl_tree, ill, AVL_AFTER)) {
1698bd670b35SErik Nordmark 			/* Make sure the ill isn't going away. */
1699bd670b35SErik Nordmark 			if (!ill_check_and_refhold(ill))
1700bd670b35SErik Nordmark 				continue;
1701bd670b35SErik Nordmark 			rw_exit(&ipst->ips_ill_g_lock);
1702bd670b35SErik Nordmark 			rw_enter(&ill->ill_mcast_lock, RW_WRITER);
17037c478bd9Sstevel@tonic-gate 			if (ill->ill_mcast_v1_tset == 1)
17047c478bd9Sstevel@tonic-gate 				ill->ill_mcast_v1_time++;
17057c478bd9Sstevel@tonic-gate 			if (ill->ill_mcast_v2_tset == 1)
17067c478bd9Sstevel@tonic-gate 				ill->ill_mcast_v2_time++;
17078dc47d9fSudpa 			if ((ill->ill_mcast_type == IGMP_V1_ROUTER) &&
17088dc47d9fSudpa 			    (ipst->ips_igmp_max_version >= IGMP_V2_ROUTER) &&
17098dc47d9fSudpa 			    (ill->ill_mcast_v1_time >= OVQP(ill))) {
17108dc47d9fSudpa 				if ((ill->ill_mcast_v2_tset > 0) ||
17118dc47d9fSudpa 				    (ipst->ips_igmp_max_version ==
17128dc47d9fSudpa 				    IGMP_V2_ROUTER)) {
17138dc47d9fSudpa 					ip1dbg(("V1 query timer "
17148dc47d9fSudpa 					    "expired on %s; switching "
17158dc47d9fSudpa 					    "mode to IGMP_V2\n",
17168dc47d9fSudpa 					    ill->ill_name));
17178dc47d9fSudpa 					ill->ill_mcast_type =
17188dc47d9fSudpa 					    IGMP_V2_ROUTER;
17198dc47d9fSudpa 				} else {
17208dc47d9fSudpa 					ip1dbg(("V1 query timer "
17218dc47d9fSudpa 					    "expired on %s; switching "
17228dc47d9fSudpa 					    "mode to IGMP_V3\n",
17237c478bd9Sstevel@tonic-gate 					    ill->ill_name));
17248dc47d9fSudpa 					ill->ill_mcast_type =
17258dc47d9fSudpa 					    IGMP_V3_ROUTER;
17267c478bd9Sstevel@tonic-gate 				}
17278dc47d9fSudpa 				ill->ill_mcast_v1_time = 0;
17288dc47d9fSudpa 				ill->ill_mcast_v1_tset = 0;
17291a5e258fSJosef 'Jeff' Sipek 				atomic_dec_16(&ifp->illif_mcast_v1);
17308dc47d9fSudpa 			}
17318dc47d9fSudpa 			if ((ill->ill_mcast_type == IGMP_V2_ROUTER) &&
17328dc47d9fSudpa 			    (ipst->ips_igmp_max_version >= IGMP_V3_ROUTER) &&
17338dc47d9fSudpa 			    (ill->ill_mcast_v2_time >= OVQP(ill))) {
17348dc47d9fSudpa 				ip1dbg(("V2 query timer expired on "
17358dc47d9fSudpa 				    "%s; switching mode to IGMP_V3\n",
17368dc47d9fSudpa 				    ill->ill_name));
17378dc47d9fSudpa 				ill->ill_mcast_type = IGMP_V3_ROUTER;
17388dc47d9fSudpa 				ill->ill_mcast_v2_time = 0;
17398dc47d9fSudpa 				ill->ill_mcast_v2_tset = 0;
17401a5e258fSJosef 'Jeff' Sipek 				atomic_dec_16(&ifp->illif_mcast_v2);
17417c478bd9Sstevel@tonic-gate 			}
1742bd670b35SErik Nordmark 			rw_exit(&ill->ill_mcast_lock);
1743bd670b35SErik Nordmark 			ill_refrele(ill);
1744bd670b35SErik Nordmark 			rw_enter(&ipst->ips_ill_g_lock, RW_READER);
17457c478bd9Sstevel@tonic-gate 		}
17467c478bd9Sstevel@tonic-gate 	}
1747f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
1748bd670b35SErik Nordmark 	ill_mcast_timer_start(ipst);
1749f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_slowtimeout_lock);
1750*f5db8fb0SRobert Mustacchi 	if (ipst->ips_igmp_slowtimeout_quiesce != B_TRUE) {
1751*f5db8fb0SRobert Mustacchi 		ipst->ips_igmp_slowtimeout_id = timeout(igmp_slowtimo,
1752*f5db8fb0SRobert Mustacchi 		    (void *)ipst, MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
1753*f5db8fb0SRobert Mustacchi 	} else {
1754*f5db8fb0SRobert Mustacchi 		ipst->ips_igmp_slowtimeout_id = 0;
1755*f5db8fb0SRobert Mustacchi 	}
1756f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_slowtimeout_lock);
17577c478bd9Sstevel@tonic-gate }
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate /*
17607c478bd9Sstevel@tonic-gate  * mld_slowtimo:
17617c478bd9Sstevel@tonic-gate  * - Resets to newer version if we didn't hear from the older version router
17627c478bd9Sstevel@tonic-gate  *   in MLD_AGE_THRESHOLD seconds.
17637c478bd9Sstevel@tonic-gate  * - Restarts slowtimeout.
17648dc47d9fSudpa  * Check for ips_mld_max_version ensures that we don't revert to a higher
17658dc47d9fSudpa  * IGMP version than configured.
17667c478bd9Sstevel@tonic-gate  */
17677c478bd9Sstevel@tonic-gate void
17687c478bd9Sstevel@tonic-gate mld_slowtimo(void *arg)
17697c478bd9Sstevel@tonic-gate {
17707c478bd9Sstevel@tonic-gate 	ill_t *ill;
17717c478bd9Sstevel@tonic-gate 	ill_if_t *ifp;
17727c478bd9Sstevel@tonic-gate 	avl_tree_t *avl_tree;
1773f4b3ec61Sdh 	ip_stack_t *ipst = (ip_stack_t *)arg;
17747c478bd9Sstevel@tonic-gate 
1775f4b3ec61Sdh 	ASSERT(arg != NULL);
17767c478bd9Sstevel@tonic-gate 	/* See comments in igmp_slowtimo() above... */
1777f4b3ec61Sdh 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
1778f4b3ec61Sdh 	for (ifp = IP_V6_ILL_G_LIST(ipst);
1779f4b3ec61Sdh 	    ifp != (ill_if_t *)&IP_V6_ILL_G_LIST(ipst);
17807c478bd9Sstevel@tonic-gate 	    ifp = ifp->illif_next) {
17817c478bd9Sstevel@tonic-gate 		if (ifp->illif_mcast_v1 == 0)
17827c478bd9Sstevel@tonic-gate 			continue;
17837c478bd9Sstevel@tonic-gate 
17847c478bd9Sstevel@tonic-gate 		avl_tree = &ifp->illif_avl_by_ppa;
17857c478bd9Sstevel@tonic-gate 		for (ill = avl_first(avl_tree); ill != NULL;
17867c478bd9Sstevel@tonic-gate 		    ill = avl_walk(avl_tree, ill, AVL_AFTER)) {
1787bd670b35SErik Nordmark 			/* Make sure the ill isn't going away. */
1788bd670b35SErik Nordmark 			if (!ill_check_and_refhold(ill))
1789bd670b35SErik Nordmark 				continue;
1790bd670b35SErik Nordmark 			rw_exit(&ipst->ips_ill_g_lock);
1791bd670b35SErik Nordmark 			rw_enter(&ill->ill_mcast_lock, RW_WRITER);
17927c478bd9Sstevel@tonic-gate 			if (ill->ill_mcast_v1_tset == 1)
17937c478bd9Sstevel@tonic-gate 				ill->ill_mcast_v1_time++;
17948dc47d9fSudpa 			if ((ill->ill_mcast_type == MLD_V1_ROUTER) &&
17958dc47d9fSudpa 			    (ipst->ips_mld_max_version >= MLD_V2_ROUTER) &&
17968dc47d9fSudpa 			    (ill->ill_mcast_v1_time >= OVQP(ill))) {
17978dc47d9fSudpa 				ip1dbg(("MLD query timer expired on"
17988dc47d9fSudpa 				    " %s; switching mode to MLD_V2\n",
17998dc47d9fSudpa 				    ill->ill_name));
18008dc47d9fSudpa 				ill->ill_mcast_type = MLD_V2_ROUTER;
18018dc47d9fSudpa 				ill->ill_mcast_v1_time = 0;
18028dc47d9fSudpa 				ill->ill_mcast_v1_tset = 0;
18031a5e258fSJosef 'Jeff' Sipek 				atomic_dec_16(&ifp->illif_mcast_v1);
18047c478bd9Sstevel@tonic-gate 			}
1805bd670b35SErik Nordmark 			rw_exit(&ill->ill_mcast_lock);
1806bd670b35SErik Nordmark 			ill_refrele(ill);
1807bd670b35SErik Nordmark 			rw_enter(&ipst->ips_ill_g_lock, RW_READER);
18087c478bd9Sstevel@tonic-gate 		}
18097c478bd9Sstevel@tonic-gate 	}
1810f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
1811bd670b35SErik Nordmark 	ill_mcast_timer_start(ipst);
1812f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_slowtimeout_lock);
1813*f5db8fb0SRobert Mustacchi 	if (ipst->ips_mld_slowtimeout_quiesce != B_TRUE) {
1814*f5db8fb0SRobert Mustacchi 		ipst->ips_mld_slowtimeout_id = timeout(mld_slowtimo,
1815*f5db8fb0SRobert Mustacchi 		    (void *)ipst, MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
1816*f5db8fb0SRobert Mustacchi 	} else {
1817*f5db8fb0SRobert Mustacchi 		ipst->ips_mld_slowtimeout_id = 0;
1818*f5db8fb0SRobert Mustacchi 	}
1819f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_slowtimeout_lock);
18207c478bd9Sstevel@tonic-gate }
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate /*
18237c478bd9Sstevel@tonic-gate  * igmp_sendpkt:
1824bd670b35SErik Nordmark  * This will send to ip_output_simple just like icmp_inbound.
18257c478bd9Sstevel@tonic-gate  */
18267c478bd9Sstevel@tonic-gate static void
18277c478bd9Sstevel@tonic-gate igmp_sendpkt(ilm_t *ilm, uchar_t type, ipaddr_t addr)
18287c478bd9Sstevel@tonic-gate {
18297c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
18307c478bd9Sstevel@tonic-gate 	igmpa_t	*igmpa;
18317c478bd9Sstevel@tonic-gate 	uint8_t *rtralert;
18327c478bd9Sstevel@tonic-gate 	ipha_t	*ipha;
18337c478bd9Sstevel@tonic-gate 	int	hdrlen = sizeof (ipha_t) + RTRALERT_LEN;
18347c478bd9Sstevel@tonic-gate 	size_t	size  = hdrlen + sizeof (igmpa_t);
1835bd670b35SErik Nordmark 	ill_t 	*ill  = ilm->ilm_ill;
1836f4b3ec61Sdh 	ip_stack_t *ipst = ill->ill_ipst;
18377c478bd9Sstevel@tonic-gate 
1838bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
18417c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
18427c478bd9Sstevel@tonic-gate 		return;
18437c478bd9Sstevel@tonic-gate 	}
18447c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + size;
18457c478bd9Sstevel@tonic-gate 
18467c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
18477c478bd9Sstevel@tonic-gate 	rtralert = (uint8_t *)&(ipha[1]);
18487c478bd9Sstevel@tonic-gate 	igmpa = (igmpa_t *)&(rtralert[RTRALERT_LEN]);
18497c478bd9Sstevel@tonic-gate 	igmpa->igmpa_type   = type;
18507c478bd9Sstevel@tonic-gate 	igmpa->igmpa_code   = 0;
18517c478bd9Sstevel@tonic-gate 	igmpa->igmpa_group  = ilm->ilm_addr;
18527c478bd9Sstevel@tonic-gate 	igmpa->igmpa_cksum  = 0;
18537c478bd9Sstevel@tonic-gate 	igmpa->igmpa_cksum  = IP_CSUM(mp, hdrlen, 0);
18547c478bd9Sstevel@tonic-gate 
1855c0ad9723Srk 	rtralert[0] = IPOPT_COPY | IPOPT_RTRALERT;
18567c478bd9Sstevel@tonic-gate 	rtralert[1] = RTRALERT_LEN;
18577c478bd9Sstevel@tonic-gate 	rtralert[2] = 0;
18587c478bd9Sstevel@tonic-gate 	rtralert[3] = 0;
18597c478bd9Sstevel@tonic-gate 
18607c478bd9Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length = (IP_VERSION << 4)
18617c478bd9Sstevel@tonic-gate 	    | (IP_SIMPLE_HDR_LENGTH_IN_WORDS + RTRALERT_LEN_IN_WORDS);
18627c478bd9Sstevel@tonic-gate 	ipha->ipha_type_of_service 	= 0;
18637c478bd9Sstevel@tonic-gate 	ipha->ipha_length = htons(size);
18647c478bd9Sstevel@tonic-gate 	ipha->ipha_ident = 0;
18657c478bd9Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags = 0;
18667c478bd9Sstevel@tonic-gate 	ipha->ipha_ttl 		= IGMP_TTL;
18677c478bd9Sstevel@tonic-gate 	ipha->ipha_protocol 	= IPPROTO_IGMP;
18687c478bd9Sstevel@tonic-gate 	ipha->ipha_hdr_checksum 	= 0;
18697c478bd9Sstevel@tonic-gate 	ipha->ipha_dst 		= addr ? addr : igmpa->igmpa_group;
1870bd670b35SErik Nordmark 	ipha->ipha_src 		= INADDR_ANY;
18717c478bd9Sstevel@tonic-gate 
1872bd670b35SErik Nordmark 	ill_mcast_queue(ill, mp);
18737c478bd9Sstevel@tonic-gate 
1874f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_snd_reports;
18757c478bd9Sstevel@tonic-gate }
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate /*
1878bd670b35SErik Nordmark  * Sends an IGMP_V3_MEMBERSHIP_REPORT message out the ill.
1879bd670b35SErik Nordmark  * The report will contain one group record
18807c478bd9Sstevel@tonic-gate  * for each element of reclist.  If this causes packet length to
18811eee170aSErik Nordmark  * exceed ill->ill_mc_mtu, multiple reports are sent.
18827c478bd9Sstevel@tonic-gate  * reclist is assumed to be made up of buffers allocated by mcast_bldmrec(),
18837c478bd9Sstevel@tonic-gate  * and those buffers are freed here.
18847c478bd9Sstevel@tonic-gate  */
18857c478bd9Sstevel@tonic-gate static void
1886bd670b35SErik Nordmark igmpv3_sendrpt(ill_t *ill, mrec_t *reclist)
18877c478bd9Sstevel@tonic-gate {
18887c478bd9Sstevel@tonic-gate 	igmp3ra_t *igmp3ra;
18897c478bd9Sstevel@tonic-gate 	grphdra_t *grphdr;
1890bd670b35SErik Nordmark 	mblk_t *mp;
18917c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
18927c478bd9Sstevel@tonic-gate 	uint8_t *rtralert;
18937c478bd9Sstevel@tonic-gate 	ipaddr_t *src_array;
18947c478bd9Sstevel@tonic-gate 	int i, j, numrec, more_src_cnt;
18957c478bd9Sstevel@tonic-gate 	size_t hdrsize, size, rsize;
18967c478bd9Sstevel@tonic-gate 	mrec_t *rp, *cur_reclist;
18977c478bd9Sstevel@tonic-gate 	mrec_t *next_reclist = reclist;
18987c478bd9Sstevel@tonic-gate 	boolean_t morepkts;
1899f4b3ec61Sdh 	ip_stack_t	 *ipst = ill->ill_ipst;
19007c478bd9Sstevel@tonic-gate 
1901bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
1902e11c3f44Smeem 
19037c478bd9Sstevel@tonic-gate 	/* if there aren't any records, there's nothing to send */
19047c478bd9Sstevel@tonic-gate 	if (reclist == NULL)
19057c478bd9Sstevel@tonic-gate 		return;
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	hdrsize = sizeof (ipha_t) + RTRALERT_LEN;
19087c478bd9Sstevel@tonic-gate nextpkt:
19097c478bd9Sstevel@tonic-gate 	size = hdrsize + sizeof (igmp3ra_t);
19107c478bd9Sstevel@tonic-gate 	morepkts = B_FALSE;
19117c478bd9Sstevel@tonic-gate 	more_src_cnt = 0;
19127c478bd9Sstevel@tonic-gate 	cur_reclist = next_reclist;
19137c478bd9Sstevel@tonic-gate 	numrec = 0;
19147c478bd9Sstevel@tonic-gate 	for (rp = cur_reclist; rp != NULL; rp = rp->mrec_next) {
19157c478bd9Sstevel@tonic-gate 		rsize = sizeof (grphdra_t) +
19167c478bd9Sstevel@tonic-gate 		    (rp->mrec_srcs.sl_numsrc * sizeof (ipaddr_t));
19171eee170aSErik Nordmark 		if (size + rsize > ill->ill_mc_mtu) {
19187c478bd9Sstevel@tonic-gate 			if (rp == cur_reclist) {
19197c478bd9Sstevel@tonic-gate 				/*
19207c478bd9Sstevel@tonic-gate 				 * If the first mrec we looked at is too big
19217c478bd9Sstevel@tonic-gate 				 * to fit in a single packet (i.e the source
19227c478bd9Sstevel@tonic-gate 				 * list is too big), we must either truncate
19237c478bd9Sstevel@tonic-gate 				 * the list (if TO_EX or IS_EX), or send
19247c478bd9Sstevel@tonic-gate 				 * multiple reports for the same group (all
19257c478bd9Sstevel@tonic-gate 				 * other types).
19267c478bd9Sstevel@tonic-gate 				 */
19277c478bd9Sstevel@tonic-gate 				int srcspace, srcsperpkt;
19281eee170aSErik Nordmark 				srcspace = ill->ill_mc_mtu - (size +
19297c478bd9Sstevel@tonic-gate 				    sizeof (grphdra_t));
1930e11c3f44Smeem 
1931e11c3f44Smeem 				/*
1932e11c3f44Smeem 				 * Skip if there's not even enough room in
1933e11c3f44Smeem 				 * a single packet to send something useful.
1934e11c3f44Smeem 				 */
1935e11c3f44Smeem 				if (srcspace <= sizeof (ipaddr_t))
1936e11c3f44Smeem 					continue;
1937e11c3f44Smeem 
19387c478bd9Sstevel@tonic-gate 				srcsperpkt = srcspace / sizeof (ipaddr_t);
19397c478bd9Sstevel@tonic-gate 				/*
19407c478bd9Sstevel@tonic-gate 				 * Increment size and numrec, because we will
19417c478bd9Sstevel@tonic-gate 				 * be sending a record for the mrec we're
19427c478bd9Sstevel@tonic-gate 				 * looking at now.
19437c478bd9Sstevel@tonic-gate 				 */
19447c478bd9Sstevel@tonic-gate 				size += sizeof (grphdra_t) +
19457c478bd9Sstevel@tonic-gate 				    (srcsperpkt * sizeof (ipaddr_t));
19467c478bd9Sstevel@tonic-gate 				numrec++;
19477c478bd9Sstevel@tonic-gate 				if (rp->mrec_type == MODE_IS_EXCLUDE ||
19487c478bd9Sstevel@tonic-gate 				    rp->mrec_type == CHANGE_TO_EXCLUDE) {
19497c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
19507c478bd9Sstevel@tonic-gate 					if (rp->mrec_next == NULL) {
19517c478bd9Sstevel@tonic-gate 						/* no more packets to send */
19527c478bd9Sstevel@tonic-gate 						break;
19537c478bd9Sstevel@tonic-gate 					} else {
19547c478bd9Sstevel@tonic-gate 						/*
19557c478bd9Sstevel@tonic-gate 						 * more packets, but we're
19567c478bd9Sstevel@tonic-gate 						 * done with this mrec.
19577c478bd9Sstevel@tonic-gate 						 */
19587c478bd9Sstevel@tonic-gate 						next_reclist = rp->mrec_next;
19597c478bd9Sstevel@tonic-gate 					}
19607c478bd9Sstevel@tonic-gate 				} else {
19617c478bd9Sstevel@tonic-gate 					more_src_cnt = rp->mrec_srcs.sl_numsrc
19627c478bd9Sstevel@tonic-gate 					    - srcsperpkt;
19637c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
19647c478bd9Sstevel@tonic-gate 					/*
19657c478bd9Sstevel@tonic-gate 					 * We'll fix up this mrec (remove the
19667c478bd9Sstevel@tonic-gate 					 * srcs we've already sent) before
19677c478bd9Sstevel@tonic-gate 					 * returning to nextpkt above.
19687c478bd9Sstevel@tonic-gate 					 */
19697c478bd9Sstevel@tonic-gate 					next_reclist = rp;
19707c478bd9Sstevel@tonic-gate 				}
19717c478bd9Sstevel@tonic-gate 			} else {
19727c478bd9Sstevel@tonic-gate 				next_reclist = rp;
19737c478bd9Sstevel@tonic-gate 			}
19747c478bd9Sstevel@tonic-gate 			morepkts = B_TRUE;
19757c478bd9Sstevel@tonic-gate 			break;
19767c478bd9Sstevel@tonic-gate 		}
19777c478bd9Sstevel@tonic-gate 		size += rsize;
19787c478bd9Sstevel@tonic-gate 		numrec++;
19797c478bd9Sstevel@tonic-gate 	}
19807c478bd9Sstevel@tonic-gate 
19817c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
19827c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
19837c478bd9Sstevel@tonic-gate 		goto free_reclist;
19847c478bd9Sstevel@tonic-gate 	}
19857c478bd9Sstevel@tonic-gate 	bzero((char *)mp->b_rptr, size);
19867c478bd9Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)(mp->b_rptr + size);
19877c478bd9Sstevel@tonic-gate 
19887c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
19897c478bd9Sstevel@tonic-gate 	rtralert = (uint8_t *)&(ipha[1]);
19907c478bd9Sstevel@tonic-gate 	igmp3ra = (igmp3ra_t *)&(rtralert[RTRALERT_LEN]);
19917c478bd9Sstevel@tonic-gate 	grphdr = (grphdra_t *)&(igmp3ra[1]);
19927c478bd9Sstevel@tonic-gate 
19937c478bd9Sstevel@tonic-gate 	rp = cur_reclist;
19947c478bd9Sstevel@tonic-gate 	for (i = 0; i < numrec; i++) {
19957c478bd9Sstevel@tonic-gate 		grphdr->grphdra_type = rp->mrec_type;
19967c478bd9Sstevel@tonic-gate 		grphdr->grphdra_numsrc = htons(rp->mrec_srcs.sl_numsrc);
19977c478bd9Sstevel@tonic-gate 		grphdr->grphdra_group = V4_PART_OF_V6(rp->mrec_group);
19987c478bd9Sstevel@tonic-gate 		src_array = (ipaddr_t *)&(grphdr[1]);
19997c478bd9Sstevel@tonic-gate 
20007c478bd9Sstevel@tonic-gate 		for (j = 0; j < rp->mrec_srcs.sl_numsrc; j++)
20017c478bd9Sstevel@tonic-gate 			src_array[j] = V4_PART_OF_V6(rp->mrec_srcs.sl_addr[j]);
20027c478bd9Sstevel@tonic-gate 
20037c478bd9Sstevel@tonic-gate 		grphdr = (grphdra_t *)&(src_array[j]);
20047c478bd9Sstevel@tonic-gate 		rp = rp->mrec_next;
20057c478bd9Sstevel@tonic-gate 	}
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 	igmp3ra->igmp3ra_type = IGMP_V3_MEMBERSHIP_REPORT;
20087c478bd9Sstevel@tonic-gate 	igmp3ra->igmp3ra_numrec = htons(numrec);
20097c478bd9Sstevel@tonic-gate 	igmp3ra->igmp3ra_cksum = IP_CSUM(mp, hdrsize, 0);
20107c478bd9Sstevel@tonic-gate 
2011c0ad9723Srk 	rtralert[0] = IPOPT_COPY | IPOPT_RTRALERT;
20127c478bd9Sstevel@tonic-gate 	rtralert[1] = RTRALERT_LEN;
20137c478bd9Sstevel@tonic-gate 	rtralert[2] = 0;
20147c478bd9Sstevel@tonic-gate 	rtralert[3] = 0;
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length = IP_VERSION << 4
20177c478bd9Sstevel@tonic-gate 	    | (IP_SIMPLE_HDR_LENGTH_IN_WORDS + RTRALERT_LEN_IN_WORDS);
20187c478bd9Sstevel@tonic-gate 	ipha->ipha_type_of_service = IPTOS_PREC_INTERNETCONTROL;
20197c478bd9Sstevel@tonic-gate 	ipha->ipha_length = htons(size);
20207c478bd9Sstevel@tonic-gate 	ipha->ipha_ttl = IGMP_TTL;
20217c478bd9Sstevel@tonic-gate 	ipha->ipha_protocol = IPPROTO_IGMP;
20227c478bd9Sstevel@tonic-gate 	ipha->ipha_dst = htonl(INADDR_ALLRPTS_GROUP);
2023bd670b35SErik Nordmark 	ipha->ipha_src = INADDR_ANY;
20247c478bd9Sstevel@tonic-gate 
2025bd670b35SErik Nordmark 	ill_mcast_queue(ill, mp);
20267c478bd9Sstevel@tonic-gate 
2027f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_snd_reports;
20287c478bd9Sstevel@tonic-gate 
20297c478bd9Sstevel@tonic-gate 	if (morepkts) {
20307c478bd9Sstevel@tonic-gate 		if (more_src_cnt > 0) {
20317c478bd9Sstevel@tonic-gate 			int index, mvsize;
20327c478bd9Sstevel@tonic-gate 			slist_t *sl = &next_reclist->mrec_srcs;
20337c478bd9Sstevel@tonic-gate 			index = sl->sl_numsrc;
20347c478bd9Sstevel@tonic-gate 			mvsize = more_src_cnt * sizeof (in6_addr_t);
20357c478bd9Sstevel@tonic-gate 			(void) memmove(&sl->sl_addr[0], &sl->sl_addr[index],
20367c478bd9Sstevel@tonic-gate 			    mvsize);
20377c478bd9Sstevel@tonic-gate 			sl->sl_numsrc = more_src_cnt;
20387c478bd9Sstevel@tonic-gate 		}
20397c478bd9Sstevel@tonic-gate 		goto nextpkt;
20407c478bd9Sstevel@tonic-gate 	}
20417c478bd9Sstevel@tonic-gate 
20427c478bd9Sstevel@tonic-gate free_reclist:
20437c478bd9Sstevel@tonic-gate 	while (reclist != NULL) {
20447c478bd9Sstevel@tonic-gate 		rp = reclist->mrec_next;
20457c478bd9Sstevel@tonic-gate 		mi_free(reclist);
20467c478bd9Sstevel@tonic-gate 		reclist = rp;
20477c478bd9Sstevel@tonic-gate 	}
20487c478bd9Sstevel@tonic-gate }
20497c478bd9Sstevel@tonic-gate 
20507c478bd9Sstevel@tonic-gate /*
20517c478bd9Sstevel@tonic-gate  * mld_input:
2052bd670b35SErik Nordmark  * Return NULL for a bad packet that is discarded here.
2053bd670b35SErik Nordmark  * Return mp if the message is OK and should be handed to "raw" receivers.
2054bd670b35SErik Nordmark  * Callers of mld_input() may need to reinitialize variables that were copied
2055bd670b35SErik Nordmark  * from the mblk as this calls pullupmsg().
20567c478bd9Sstevel@tonic-gate  */
2057bd670b35SErik Nordmark mblk_t *
2058bd670b35SErik Nordmark mld_input(mblk_t *mp, ip_recv_attr_t *ira)
20597c478bd9Sstevel@tonic-gate {
20607c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h = (ip6_t *)(mp->b_rptr);
20617c478bd9Sstevel@tonic-gate 	mld_hdr_t	*mldh;
20627c478bd9Sstevel@tonic-gate 	ilm_t		*ilm;
20637c478bd9Sstevel@tonic-gate 	ipif_t		*ipif;
20647c478bd9Sstevel@tonic-gate 	uint16_t	hdr_length, exthdr_length;
2065bd670b35SErik Nordmark 	in6_addr_t	*v6group_ptr;
20667c478bd9Sstevel@tonic-gate 	uint_t		next;
20677c478bd9Sstevel@tonic-gate 	int		mldlen;
2068bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
2069f4b3ec61Sdh 	ip_stack_t	*ipst = ill->ill_ipst;
20707c478bd9Sstevel@tonic-gate 
20717c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembTotal);
20727c478bd9Sstevel@tonic-gate 
20737c478bd9Sstevel@tonic-gate 	/* Make sure the src address of the packet is link-local */
20747c478bd9Sstevel@tonic-gate 	if (!(IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src))) {
20757c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
20767c478bd9Sstevel@tonic-gate 		freemsg(mp);
2077bd670b35SErik Nordmark 		return (NULL);
20787c478bd9Sstevel@tonic-gate 	}
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 	if (ip6h->ip6_hlim != 1) {
20817c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpBadHoplimit);
20827c478bd9Sstevel@tonic-gate 		freemsg(mp);
2083bd670b35SErik Nordmark 		return (NULL);
20847c478bd9Sstevel@tonic-gate 	}
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 	/* Get to the icmp header part */
2087bd670b35SErik Nordmark 	hdr_length = ira->ira_ip_hdr_length;
2088bd670b35SErik Nordmark 	exthdr_length = hdr_length - IPV6_HDR_LEN;
2089bd670b35SErik Nordmark 
20907c478bd9Sstevel@tonic-gate 	mldlen = ntohs(ip6h->ip6_plen) - exthdr_length;
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 	/* An MLD packet must at least be 24 octets to be valid */
20937c478bd9Sstevel@tonic-gate 	if (mldlen < MLD_MINLEN) {
20947c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
20957c478bd9Sstevel@tonic-gate 		freemsg(mp);
2096bd670b35SErik Nordmark 		return (NULL);
20977c478bd9Sstevel@tonic-gate 	}
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	mldh = (mld_hdr_t *)(&mp->b_rptr[hdr_length]);
21007c478bd9Sstevel@tonic-gate 
21017c478bd9Sstevel@tonic-gate 	switch (mldh->mld_type) {
21027c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_QUERY:
21037c478bd9Sstevel@tonic-gate 		/*
21047c478bd9Sstevel@tonic-gate 		 * packet length differentiates between v1 and v2.  v1
21057c478bd9Sstevel@tonic-gate 		 * query should be exactly 24 octets long; v2 is >= 28.
21067c478bd9Sstevel@tonic-gate 		 */
21078dc47d9fSudpa 		if ((mldlen == MLD_MINLEN) ||
21088dc47d9fSudpa 		    (ipst->ips_mld_max_version < MLD_V2_ROUTER)) {
21097c478bd9Sstevel@tonic-gate 			next = mld_query_in(mldh, ill);
21107c478bd9Sstevel@tonic-gate 		} else if (mldlen >= MLD_V2_QUERY_MINLEN) {
21117c478bd9Sstevel@tonic-gate 			next = mldv2_query_in((mld2q_t *)mldh, ill, mldlen);
21127c478bd9Sstevel@tonic-gate 		} else {
21137c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
21147c478bd9Sstevel@tonic-gate 			freemsg(mp);
2115bd670b35SErik Nordmark 			return (NULL);
21167c478bd9Sstevel@tonic-gate 		}
21177c478bd9Sstevel@tonic-gate 		if (next == 0) {
2118bd670b35SErik Nordmark 			return (mp);
21197c478bd9Sstevel@tonic-gate 		}
21207c478bd9Sstevel@tonic-gate 
21217c478bd9Sstevel@tonic-gate 		if (next != INFINITY)
2122f4b3ec61Sdh 			mld_start_timers(next, ipst);
21237c478bd9Sstevel@tonic-gate 		break;
21247c478bd9Sstevel@tonic-gate 
2125bd670b35SErik Nordmark 	case MLD_LISTENER_REPORT:
21267c478bd9Sstevel@tonic-gate 		/*
21277c478bd9Sstevel@tonic-gate 		 * For fast leave to work, we have to know that we are the
21287c478bd9Sstevel@tonic-gate 		 * last person to send a report for this group.  Reports
21297c478bd9Sstevel@tonic-gate 		 * generated by us are looped back since we could potentially
21307c478bd9Sstevel@tonic-gate 		 * be a multicast router, so discard reports sourced by me.
21317c478bd9Sstevel@tonic-gate 		 */
21327c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
21337c478bd9Sstevel@tonic-gate 		for (ipif = ill->ill_ipif; ipif != NULL;
21347c478bd9Sstevel@tonic-gate 		    ipif = ipif->ipif_next) {
21357c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr,
2136bd670b35SErik Nordmark 			    &ip6h->ip6_src)) {
21377c478bd9Sstevel@tonic-gate 				if (ip_debug > 1) {
21387c478bd9Sstevel@tonic-gate 					char    buf1[INET6_ADDRSTRLEN];
21397c478bd9Sstevel@tonic-gate 
21407c478bd9Sstevel@tonic-gate 					(void) mi_strlog(ill->ill_rq,
21417c478bd9Sstevel@tonic-gate 					    1,
21427c478bd9Sstevel@tonic-gate 					    SL_TRACE,
21437c478bd9Sstevel@tonic-gate 					    "mld_input: we are only "
2144bd670b35SErik Nordmark 					    "member src %s\n",
2145bd670b35SErik Nordmark 					    inet_ntop(AF_INET6, &ip6h->ip6_src,
2146bd670b35SErik Nordmark 					    buf1, sizeof (buf1)));
21477c478bd9Sstevel@tonic-gate 				}
21487c478bd9Sstevel@tonic-gate 				mutex_exit(&ill->ill_lock);
2149bd670b35SErik Nordmark 				return (mp);
21507c478bd9Sstevel@tonic-gate 			}
21517c478bd9Sstevel@tonic-gate 		}
21527c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
21537c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembResponses);
21547c478bd9Sstevel@tonic-gate 
21557c478bd9Sstevel@tonic-gate 		v6group_ptr = &mldh->mld_addr;
21567c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_MULTICAST(v6group_ptr)) {
21577c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib,
21587c478bd9Sstevel@tonic-gate 			    ipv6IfIcmpInGroupMembBadReports);
21597c478bd9Sstevel@tonic-gate 			freemsg(mp);
2160bd670b35SErik Nordmark 			return (NULL);
21617c478bd9Sstevel@tonic-gate 		}
21627c478bd9Sstevel@tonic-gate 
2163bd670b35SErik Nordmark 
21647c478bd9Sstevel@tonic-gate 		/*
21657c478bd9Sstevel@tonic-gate 		 * If we belong to the group being reported, and we are a
21667c478bd9Sstevel@tonic-gate 		 * 'Delaying member' per the RFC terminology, stop our timer
21677c478bd9Sstevel@tonic-gate 		 * for that group and 'clear flag' i.e. mark ilm_state as
21687c478bd9Sstevel@tonic-gate 		 * IGMP_OTHERMEMBER. With zones, there can be multiple group
21697c478bd9Sstevel@tonic-gate 		 * membership entries for the same group address (one per zone)
21707c478bd9Sstevel@tonic-gate 		 * so we need to walk the ill_ilm list.
21717c478bd9Sstevel@tonic-gate 		 */
2172bd670b35SErik Nordmark 		rw_enter(&ill->ill_mcast_lock, RW_WRITER);
2173bd670b35SErik Nordmark 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
21747c478bd9Sstevel@tonic-gate 			if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group_ptr))
2175328c7d1fSmeem 				continue;
21767c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib,
21777c478bd9Sstevel@tonic-gate 			    ipv6IfIcmpInGroupMembOurReports);
21787c478bd9Sstevel@tonic-gate 
21797c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = INFINITY;
21807c478bd9Sstevel@tonic-gate 			ilm->ilm_state = IGMP_OTHERMEMBER;
21817c478bd9Sstevel@tonic-gate 		}
2182bd670b35SErik Nordmark 		rw_exit(&ill->ill_mcast_lock);
2183bd670b35SErik Nordmark 		/*
2184bd670b35SErik Nordmark 		 * No packets have been sent above - no
2185bd670b35SErik Nordmark 		 * ill_mcast_send_queued is needed.
2186bd670b35SErik Nordmark 		 */
2187bd670b35SErik Nordmark 		ill_mcast_timer_start(ill->ill_ipst);
21887c478bd9Sstevel@tonic-gate 		break;
2189bd670b35SErik Nordmark 
21907c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_REDUCTION:
21917c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembReductions);
21927c478bd9Sstevel@tonic-gate 		break;
21937c478bd9Sstevel@tonic-gate 	}
2194bd670b35SErik Nordmark 	return (mp);
21957c478bd9Sstevel@tonic-gate }
21967c478bd9Sstevel@tonic-gate 
21977c478bd9Sstevel@tonic-gate /*
21987c478bd9Sstevel@tonic-gate  * Handles an MLDv1 Listener Query.  Returns 0 on error, or the appropriate
21997c478bd9Sstevel@tonic-gate  * (non-zero, unsigned) timer value to be set on success.
22007c478bd9Sstevel@tonic-gate  */
22017c478bd9Sstevel@tonic-gate static uint_t
22027c478bd9Sstevel@tonic-gate mld_query_in(mld_hdr_t *mldh, ill_t *ill)
22037c478bd9Sstevel@tonic-gate {
22047c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
22057c478bd9Sstevel@tonic-gate 	int	timer;
22068dc47d9fSudpa 	uint_t	next, current;
22077c478bd9Sstevel@tonic-gate 	in6_addr_t *v6group;
22087c478bd9Sstevel@tonic-gate 
22097c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembQueries);
22107c478bd9Sstevel@tonic-gate 
22117c478bd9Sstevel@tonic-gate 	/*
22127c478bd9Sstevel@tonic-gate 	 * In the MLD specification, there are 3 states and a flag.
22137c478bd9Sstevel@tonic-gate 	 *
22147c478bd9Sstevel@tonic-gate 	 * In Non-Listener state, we simply don't have a membership record.
22157c478bd9Sstevel@tonic-gate 	 * In Delaying state, our timer is running (ilm->ilm_timer < INFINITY)
22167c478bd9Sstevel@tonic-gate 	 * In Idle Member state, our timer is not running (ilm->ilm_timer ==
22177c478bd9Sstevel@tonic-gate 	 * INFINITY)
22187c478bd9Sstevel@tonic-gate 	 *
22197c478bd9Sstevel@tonic-gate 	 * The flag is ilm->ilm_state, it is set to IGMP_OTHERMEMBER if
22207c478bd9Sstevel@tonic-gate 	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
22217c478bd9Sstevel@tonic-gate 	 * if I sent the last report.
22227c478bd9Sstevel@tonic-gate 	 */
22237c478bd9Sstevel@tonic-gate 	v6group = &mldh->mld_addr;
22247c478bd9Sstevel@tonic-gate 	if (!(IN6_IS_ADDR_UNSPECIFIED(v6group)) &&
22257c478bd9Sstevel@tonic-gate 	    ((!IN6_IS_ADDR_MULTICAST(v6group)))) {
22267c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembBadQueries);
22277c478bd9Sstevel@tonic-gate 		return (0);
22287c478bd9Sstevel@tonic-gate 	}
22297c478bd9Sstevel@tonic-gate 
22307c478bd9Sstevel@tonic-gate 	/* Need to do compatibility mode checking */
2231bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
22327c478bd9Sstevel@tonic-gate 	ill->ill_mcast_v1_time = 0;
22337c478bd9Sstevel@tonic-gate 	ill->ill_mcast_v1_tset = 1;
22347c478bd9Sstevel@tonic-gate 	if (ill->ill_mcast_type == MLD_V2_ROUTER) {
22357c478bd9Sstevel@tonic-gate 		ip1dbg(("Received MLDv1 Query on %s, switching mode to "
22367c478bd9Sstevel@tonic-gate 		    "MLD_V1_ROUTER\n", ill->ill_name));
22371a5e258fSJosef 'Jeff' Sipek 		atomic_inc_16(&ill->ill_ifptr->illif_mcast_v1);
22387c478bd9Sstevel@tonic-gate 		ill->ill_mcast_type = MLD_V1_ROUTER;
22397c478bd9Sstevel@tonic-gate 	}
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 	timer = (int)ntohs(mldh->mld_maxdelay);
22427c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
22437c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
22447c478bd9Sstevel@tonic-gate 		    "mld_input: TIMER = mld_maxdelay %d mld_type 0x%x",
22457c478bd9Sstevel@tonic-gate 		    timer, (int)mldh->mld_type);
22467c478bd9Sstevel@tonic-gate 	}
22477c478bd9Sstevel@tonic-gate 
22487c478bd9Sstevel@tonic-gate 	/*
22497c478bd9Sstevel@tonic-gate 	 * -Start the timers in all of our membership records for
22507c478bd9Sstevel@tonic-gate 	 * the physical interface on which the query arrived,
22517c478bd9Sstevel@tonic-gate 	 * excl:
22527c478bd9Sstevel@tonic-gate 	 *	1.  those that belong to the "all hosts" group,
22537c478bd9Sstevel@tonic-gate 	 *	2.  those with 0 scope, or 1 node-local scope.
22547c478bd9Sstevel@tonic-gate 	 *
22557c478bd9Sstevel@tonic-gate 	 * -Restart any timer that is already running but has a value
22567c478bd9Sstevel@tonic-gate 	 * longer that the requested timeout.
22577c478bd9Sstevel@tonic-gate 	 * -Use the value specified in the query message as the
22587c478bd9Sstevel@tonic-gate 	 * maximum timeout.
22597c478bd9Sstevel@tonic-gate 	 */
22607c478bd9Sstevel@tonic-gate 	next = INFINITY;
22618dc47d9fSudpa 
22628dc47d9fSudpa 	current = CURRENT_MSTIME;
2263bd670b35SErik Nordmark 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
22647c478bd9Sstevel@tonic-gate 		ASSERT(!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr));
22657c478bd9Sstevel@tonic-gate 
22667c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr) ||
22677c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_MC_NODELOCAL(&ilm->ilm_v6addr) ||
22687c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_MC_RESERVED(&ilm->ilm_v6addr))
22697c478bd9Sstevel@tonic-gate 			continue;
22707c478bd9Sstevel@tonic-gate 		if ((!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr,
22717c478bd9Sstevel@tonic-gate 		    &ipv6_all_hosts_mcast)) &&
22727c478bd9Sstevel@tonic-gate 		    (IN6_IS_ADDR_UNSPECIFIED(v6group)) ||
22737c478bd9Sstevel@tonic-gate 		    (IN6_ARE_ADDR_EQUAL(v6group, &ilm->ilm_v6addr))) {
22747c478bd9Sstevel@tonic-gate 			if (timer == 0) {
22757c478bd9Sstevel@tonic-gate 				/* Respond immediately */
22767c478bd9Sstevel@tonic-gate 				ilm->ilm_timer = INFINITY;
22777c478bd9Sstevel@tonic-gate 				ilm->ilm_state = IGMP_IREPORTEDLAST;
22787c478bd9Sstevel@tonic-gate 				mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
22797c478bd9Sstevel@tonic-gate 				break;
22807c478bd9Sstevel@tonic-gate 			}
22817c478bd9Sstevel@tonic-gate 			if (ilm->ilm_timer > timer) {
22827c478bd9Sstevel@tonic-gate 				MCAST_RANDOM_DELAY(ilm->ilm_timer, timer);
22837c478bd9Sstevel@tonic-gate 				if (ilm->ilm_timer < next)
22847c478bd9Sstevel@tonic-gate 					next = ilm->ilm_timer;
22858dc47d9fSudpa 				ilm->ilm_timer += current;
22867c478bd9Sstevel@tonic-gate 			}
22877c478bd9Sstevel@tonic-gate 			break;
22887c478bd9Sstevel@tonic-gate 		}
22897c478bd9Sstevel@tonic-gate 	}
2290bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
2291bd670b35SErik Nordmark 	/* Send any deferred/queued IP packets */
2292bd670b35SErik Nordmark 	ill_mcast_send_queued(ill);
2293bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
22947c478bd9Sstevel@tonic-gate 
22957c478bd9Sstevel@tonic-gate 	return (next);
22967c478bd9Sstevel@tonic-gate }
22977c478bd9Sstevel@tonic-gate 
22987c478bd9Sstevel@tonic-gate /*
22997c478bd9Sstevel@tonic-gate  * Handles an MLDv2 Listener Query.  On error, returns 0; on success,
23007c478bd9Sstevel@tonic-gate  * returns the appropriate (non-zero, unsigned) timer value (which may
23017c478bd9Sstevel@tonic-gate  * be INFINITY) to be set.
23027c478bd9Sstevel@tonic-gate  */
23037c478bd9Sstevel@tonic-gate static uint_t
23047c478bd9Sstevel@tonic-gate mldv2_query_in(mld2q_t *mld2q, ill_t *ill, int mldlen)
23057c478bd9Sstevel@tonic-gate {
23067c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
23077c478bd9Sstevel@tonic-gate 	in6_addr_t *v6group, *src_array;
23088dc47d9fSudpa 	uint_t	next, numsrc, i, mrd, delay, qqi, current;
23097c478bd9Sstevel@tonic-gate 	uint8_t	qrv;
23107c478bd9Sstevel@tonic-gate 
23117c478bd9Sstevel@tonic-gate 	v6group = &mld2q->mld2q_addr;
23127c478bd9Sstevel@tonic-gate 	numsrc = ntohs(mld2q->mld2q_numsrc);
23137c478bd9Sstevel@tonic-gate 
23147c478bd9Sstevel@tonic-gate 	/* make sure numsrc matches packet size */
23157c478bd9Sstevel@tonic-gate 	if (mldlen < MLD_V2_QUERY_MINLEN + (numsrc * sizeof (in6_addr_t))) {
23167c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
23177c478bd9Sstevel@tonic-gate 		return (0);
23187c478bd9Sstevel@tonic-gate 	}
23197c478bd9Sstevel@tonic-gate 	src_array = (in6_addr_t *)&mld2q[1];
23207c478bd9Sstevel@tonic-gate 
23217c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembQueries);
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 	/* extract Maximum Response Delay from code in header */
23247c478bd9Sstevel@tonic-gate 	mrd = ntohs(mld2q->mld2q_mxrc);
23257c478bd9Sstevel@tonic-gate 	if (mrd >= MLD_V2_MAXRT_FPMIN) {
23267c478bd9Sstevel@tonic-gate 		uint_t hdrval, mant, exp;
23277c478bd9Sstevel@tonic-gate 		hdrval = mrd;
23287c478bd9Sstevel@tonic-gate 		mant = hdrval & MLD_V2_MAXRT_MANT_MASK;
23297c478bd9Sstevel@tonic-gate 		exp = (hdrval & MLD_V2_MAXRT_EXP_MASK) >> 12;
23307c478bd9Sstevel@tonic-gate 		mrd = (mant | 0x1000) << (exp + 3);
23317c478bd9Sstevel@tonic-gate 	}
23328dc47d9fSudpa 	if (mrd == 0)
23338dc47d9fSudpa 		mrd = DSEC_TO_MSEC(MCAST_DEF_QUERY_RESP_INTERVAL);
23348dc47d9fSudpa 
23357c478bd9Sstevel@tonic-gate 	MCAST_RANDOM_DELAY(delay, mrd);
23367c478bd9Sstevel@tonic-gate 	next = (unsigned)INFINITY;
23378dc47d9fSudpa 	current = CURRENT_MSTIME;
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 	if ((qrv = mld2q->mld2q_sqrv & MLD_V2_RV_MASK) == 0)
23407c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = MCAST_DEF_ROBUSTNESS;
23417c478bd9Sstevel@tonic-gate 	else
23427c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = qrv;
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate 	if ((qqi = (uint_t)mld2q->mld2q_qqic) >= MLD_V2_QQI_FPMIN) {
23457c478bd9Sstevel@tonic-gate 		uint_t mant, exp;
23467c478bd9Sstevel@tonic-gate 		mant = qqi & MLD_V2_QQI_MANT_MASK;
23477c478bd9Sstevel@tonic-gate 		exp = (qqi & MLD_V2_QQI_EXP_MASK) >> 12;
23487c478bd9Sstevel@tonic-gate 		qqi = (mant | 0x10) << (exp + 3);
23497c478bd9Sstevel@tonic-gate 	}
23507c478bd9Sstevel@tonic-gate 	ill->ill_mcast_qi = (qqi == 0) ? MCAST_DEF_QUERY_INTERVAL : qqi;
23517c478bd9Sstevel@tonic-gate 
23527c478bd9Sstevel@tonic-gate 	/*
23537c478bd9Sstevel@tonic-gate 	 * If we have a pending general query response that's scheduled
23547c478bd9Sstevel@tonic-gate 	 * sooner than the delay we calculated for this response, then
23557c478bd9Sstevel@tonic-gate 	 * no action is required (MLDv2 draft section 6.2 rule 1)
23567c478bd9Sstevel@tonic-gate 	 */
2357bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
23588dc47d9fSudpa 	if (ill->ill_global_timer < (current + delay)) {
2359bd670b35SErik Nordmark 		rw_exit(&ill->ill_mcast_lock);
23607c478bd9Sstevel@tonic-gate 		return (next);
23617c478bd9Sstevel@tonic-gate 	}
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 	/*
23647c478bd9Sstevel@tonic-gate 	 * Now take action depending on query type: general,
23657c478bd9Sstevel@tonic-gate 	 * group specific, or group/source specific.
23667c478bd9Sstevel@tonic-gate 	 */
23677c478bd9Sstevel@tonic-gate 	if ((numsrc == 0) && IN6_IS_ADDR_UNSPECIFIED(v6group)) {
23687c478bd9Sstevel@tonic-gate 		/*
23697c478bd9Sstevel@tonic-gate 		 * general query
23707c478bd9Sstevel@tonic-gate 		 * We know global timer is either not running or is
23717c478bd9Sstevel@tonic-gate 		 * greater than our calculated delay, so reset it to
23727c478bd9Sstevel@tonic-gate 		 * our delay (random value in range [0, response time])
23737c478bd9Sstevel@tonic-gate 		 */
23748dc47d9fSudpa 		ill->ill_global_timer = current + delay;
23758dc47d9fSudpa 		next = delay;
23767c478bd9Sstevel@tonic-gate 	} else {
23777c478bd9Sstevel@tonic-gate 		/* group or group/source specific query */
2378bd670b35SErik Nordmark 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
23797c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr) ||
23807c478bd9Sstevel@tonic-gate 			    IN6_IS_ADDR_MC_NODELOCAL(&ilm->ilm_v6addr) ||
23817c478bd9Sstevel@tonic-gate 			    IN6_IS_ADDR_MC_RESERVED(&ilm->ilm_v6addr) ||
23827c478bd9Sstevel@tonic-gate 			    !IN6_ARE_ADDR_EQUAL(v6group, &ilm->ilm_v6addr))
23837c478bd9Sstevel@tonic-gate 				continue;
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate 			/*
23867c478bd9Sstevel@tonic-gate 			 * If the query is group specific or we have a
23877c478bd9Sstevel@tonic-gate 			 * pending group specific query, the response is
23887c478bd9Sstevel@tonic-gate 			 * group specific (pending sources list should be
23897c478bd9Sstevel@tonic-gate 			 * empty).  Otherwise, need to update the pending
23907c478bd9Sstevel@tonic-gate 			 * sources list for the group and source specific
23917c478bd9Sstevel@tonic-gate 			 * response.
23927c478bd9Sstevel@tonic-gate 			 */
23937c478bd9Sstevel@tonic-gate 			if (numsrc == 0 || (ilm->ilm_timer < INFINITY &&
23947c478bd9Sstevel@tonic-gate 			    SLIST_IS_EMPTY(ilm->ilm_pendsrcs))) {
23957c478bd9Sstevel@tonic-gate group_query:
23967c478bd9Sstevel@tonic-gate 				FREE_SLIST(ilm->ilm_pendsrcs);
23977c478bd9Sstevel@tonic-gate 				ilm->ilm_pendsrcs = NULL;
23987c478bd9Sstevel@tonic-gate 			} else {
23997c478bd9Sstevel@tonic-gate 				boolean_t overflow;
24007c478bd9Sstevel@tonic-gate 				slist_t *pktl;
24017c478bd9Sstevel@tonic-gate 				if (numsrc > MAX_FILTER_SIZE ||
24027c478bd9Sstevel@tonic-gate 				    (ilm->ilm_pendsrcs == NULL &&
24037c478bd9Sstevel@tonic-gate 				    (ilm->ilm_pendsrcs = l_alloc()) == NULL)) {
24047c478bd9Sstevel@tonic-gate 					/*
24057c478bd9Sstevel@tonic-gate 					 * We've been sent more sources than
24067c478bd9Sstevel@tonic-gate 					 * we can deal with; or we can't deal
24077c478bd9Sstevel@tonic-gate 					 * with a source list at all. Revert
24087c478bd9Sstevel@tonic-gate 					 * to a group specific query.
24097c478bd9Sstevel@tonic-gate 					 */
24107c478bd9Sstevel@tonic-gate 					goto group_query;
24117c478bd9Sstevel@tonic-gate 				}
24127c478bd9Sstevel@tonic-gate 				if ((pktl = l_alloc()) == NULL)
24137c478bd9Sstevel@tonic-gate 					goto group_query;
24147c478bd9Sstevel@tonic-gate 				pktl->sl_numsrc = numsrc;
24157c478bd9Sstevel@tonic-gate 				for (i = 0; i < numsrc; i++)
24167c478bd9Sstevel@tonic-gate 					pktl->sl_addr[i] = src_array[i];
24177c478bd9Sstevel@tonic-gate 				l_union_in_a(ilm->ilm_pendsrcs, pktl,
24187c478bd9Sstevel@tonic-gate 				    &overflow);
24197c478bd9Sstevel@tonic-gate 				l_free(pktl);
24207c478bd9Sstevel@tonic-gate 				if (overflow)
24217c478bd9Sstevel@tonic-gate 					goto group_query;
24227c478bd9Sstevel@tonic-gate 			}
24238dc47d9fSudpa 			ilm->ilm_timer = (ilm->ilm_timer == INFINITY) ?
24248dc47d9fSudpa 			    INFINITY : (ilm->ilm_timer - current);
24257c478bd9Sstevel@tonic-gate 			/* set timer to soonest value */
24267c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = MIN(ilm->ilm_timer, delay);
24277c478bd9Sstevel@tonic-gate 			if (ilm->ilm_timer < next)
24287c478bd9Sstevel@tonic-gate 				next = ilm->ilm_timer;
24298dc47d9fSudpa 			ilm->ilm_timer += current;
24307c478bd9Sstevel@tonic-gate 			break;
24317c478bd9Sstevel@tonic-gate 		}
24327c478bd9Sstevel@tonic-gate 	}
2433bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
2434bd670b35SErik Nordmark 	/*
2435bd670b35SErik Nordmark 	 * No packets have been sent above - no
2436bd670b35SErik Nordmark 	 * ill_mcast_send_queued is needed.
2437bd670b35SErik Nordmark 	 */
2438bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
24397c478bd9Sstevel@tonic-gate 
24407c478bd9Sstevel@tonic-gate 	return (next);
24417c478bd9Sstevel@tonic-gate }
24427c478bd9Sstevel@tonic-gate 
24437c478bd9Sstevel@tonic-gate /*
24447c478bd9Sstevel@tonic-gate  * Send MLDv1 response packet with hoplimit 1
24457c478bd9Sstevel@tonic-gate  */
24467c478bd9Sstevel@tonic-gate static void
24477c478bd9Sstevel@tonic-gate mld_sendpkt(ilm_t *ilm, uchar_t type, const in6_addr_t *v6addr)
24487c478bd9Sstevel@tonic-gate {
24497c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
24507c478bd9Sstevel@tonic-gate 	mld_hdr_t	*mldh;
24517c478bd9Sstevel@tonic-gate 	ip6_t 		*ip6h;
24527c478bd9Sstevel@tonic-gate 	ip6_hbh_t	*ip6hbh;
24537c478bd9Sstevel@tonic-gate 	struct ip6_opt_router	*ip6router;
24547c478bd9Sstevel@tonic-gate 	size_t		size = IPV6_HDR_LEN + sizeof (mld_hdr_t);
2455e11c3f44Smeem 	ill_t		*ill = ilm->ilm_ill;
2456bd670b35SErik Nordmark 
2457bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
24587c478bd9Sstevel@tonic-gate 
24597c478bd9Sstevel@tonic-gate 	/*
24607c478bd9Sstevel@tonic-gate 	 * We need to place a router alert option in this packet.  The length
24617c478bd9Sstevel@tonic-gate 	 * of the options must be a multiple of 8.  The hbh option header is 2
24627c478bd9Sstevel@tonic-gate 	 * bytes followed by the 4 byte router alert option.  That leaves
24637c478bd9Sstevel@tonic-gate 	 * 2 bytes of pad for a total of 8 bytes.
24647c478bd9Sstevel@tonic-gate 	 */
24657c478bd9Sstevel@tonic-gate 	const int	router_alert_length = 8;
24667c478bd9Sstevel@tonic-gate 
24677c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_isv6);
24687c478bd9Sstevel@tonic-gate 
2469e11c3f44Smeem 	size += router_alert_length;
24707c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
24717c478bd9Sstevel@tonic-gate 	if (mp == NULL)
24727c478bd9Sstevel@tonic-gate 		return;
24737c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, size);
24747c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + size;
24757c478bd9Sstevel@tonic-gate 
2476e11c3f44Smeem 	ip6h = (ip6_t *)mp->b_rptr;
24777c478bd9Sstevel@tonic-gate 	ip6hbh = (struct ip6_hbh *)&ip6h[1];
24787c478bd9Sstevel@tonic-gate 	ip6router = (struct ip6_opt_router *)&ip6hbh[1];
24797c478bd9Sstevel@tonic-gate 	/*
24807c478bd9Sstevel@tonic-gate 	 * A zero is a pad option of length 1.  The bzero of the whole packet
24817c478bd9Sstevel@tonic-gate 	 * above will pad between ip6router and mld.
24827c478bd9Sstevel@tonic-gate 	 */
24837c478bd9Sstevel@tonic-gate 	mldh = (mld_hdr_t *)((uint8_t *)ip6hbh + router_alert_length);
24847c478bd9Sstevel@tonic-gate 
24857c478bd9Sstevel@tonic-gate 	mldh->mld_type = type;
24867c478bd9Sstevel@tonic-gate 	mldh->mld_addr = ilm->ilm_v6addr;
24877c478bd9Sstevel@tonic-gate 
24887c478bd9Sstevel@tonic-gate 	ip6router->ip6or_type = IP6OPT_ROUTER_ALERT;
24897c478bd9Sstevel@tonic-gate 	ip6router->ip6or_len = 2;
24907c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[0] = 0;
24917c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[1] = IP6_ALERT_MLD;
24927c478bd9Sstevel@tonic-gate 
24937c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_nxt = IPPROTO_ICMPV6;
24947c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_len = 0;
24957c478bd9Sstevel@tonic-gate 
24967c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
24977c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = htons(sizeof (*mldh) + router_alert_length);
24987c478bd9Sstevel@tonic-gate 	ip6h->ip6_nxt = IPPROTO_HOPOPTS;
24997c478bd9Sstevel@tonic-gate 	ip6h->ip6_hops = MLD_HOP_LIMIT;
25007c478bd9Sstevel@tonic-gate 	if (v6addr == NULL)
25017c478bd9Sstevel@tonic-gate 		ip6h->ip6_dst =  ilm->ilm_v6addr;
25027c478bd9Sstevel@tonic-gate 	else
25037c478bd9Sstevel@tonic-gate 		ip6h->ip6_dst = *v6addr;
25047c478bd9Sstevel@tonic-gate 
2505bd670b35SErik Nordmark 	ip6h->ip6_src = ipv6_all_zeros;
25067c478bd9Sstevel@tonic-gate 	/*
25077c478bd9Sstevel@tonic-gate 	 * Prepare for checksum by putting icmp length in the icmp
2508bd670b35SErik Nordmark 	 * checksum field. The checksum is calculated in ip_output.
25097c478bd9Sstevel@tonic-gate 	 */
25107c478bd9Sstevel@tonic-gate 	mldh->mld_cksum = htons(sizeof (*mldh));
25117c478bd9Sstevel@tonic-gate 
2512bd670b35SErik Nordmark 	ill_mcast_queue(ill, mp);
25137c478bd9Sstevel@tonic-gate }
25147c478bd9Sstevel@tonic-gate 
25157c478bd9Sstevel@tonic-gate /*
25167c478bd9Sstevel@tonic-gate  * Sends an MLD_V2_LISTENER_REPORT message out the passed-in ill.  The
25177c478bd9Sstevel@tonic-gate  * report will contain one multicast address record for each element of
25181eee170aSErik Nordmark  * reclist.  If this causes packet length to exceed ill->ill_mc_mtu,
25197c478bd9Sstevel@tonic-gate  * multiple reports are sent.  reclist is assumed to be made up of
25207c478bd9Sstevel@tonic-gate  * buffers allocated by mcast_bldmrec(), and those buffers are freed here.
25217c478bd9Sstevel@tonic-gate  */
25227c478bd9Sstevel@tonic-gate static void
25237c478bd9Sstevel@tonic-gate mldv2_sendrpt(ill_t *ill, mrec_t *reclist)
25247c478bd9Sstevel@tonic-gate {
25257c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
25267c478bd9Sstevel@tonic-gate 	mld2r_t		*mld2r;
25277c478bd9Sstevel@tonic-gate 	mld2mar_t	*mld2mar;
25287c478bd9Sstevel@tonic-gate 	in6_addr_t	*srcarray;
25297c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h;
25307c478bd9Sstevel@tonic-gate 	ip6_hbh_t	*ip6hbh;
25317c478bd9Sstevel@tonic-gate 	struct ip6_opt_router	*ip6router;
25327c478bd9Sstevel@tonic-gate 	size_t		size, optlen, padlen, icmpsize, rsize;
25337c478bd9Sstevel@tonic-gate 	int		i, numrec, more_src_cnt;
25347c478bd9Sstevel@tonic-gate 	mrec_t		*rp, *cur_reclist;
25357c478bd9Sstevel@tonic-gate 	mrec_t		*next_reclist = reclist;
25367c478bd9Sstevel@tonic-gate 	boolean_t	morepkts;
25377c478bd9Sstevel@tonic-gate 
25387c478bd9Sstevel@tonic-gate 	/* If there aren't any records, there's nothing to send */
25397c478bd9Sstevel@tonic-gate 	if (reclist == NULL)
25407c478bd9Sstevel@tonic-gate 		return;
25417c478bd9Sstevel@tonic-gate 
25427c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_isv6);
2543bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
25447c478bd9Sstevel@tonic-gate 
25457c478bd9Sstevel@tonic-gate 	/*
25467c478bd9Sstevel@tonic-gate 	 * Total option length (optlen + padlen) must be a multiple of
25477c478bd9Sstevel@tonic-gate 	 * 8 bytes.  We assume here that optlen <= 8, so the total option
25487c478bd9Sstevel@tonic-gate 	 * length will be 8.  Assert this in case anything ever changes.
25497c478bd9Sstevel@tonic-gate 	 */
25507c478bd9Sstevel@tonic-gate 	optlen = sizeof (ip6_hbh_t) + sizeof (struct ip6_opt_router);
25517c478bd9Sstevel@tonic-gate 	ASSERT(optlen <= 8);
25527c478bd9Sstevel@tonic-gate 	padlen = 8 - optlen;
25537c478bd9Sstevel@tonic-gate nextpkt:
25547c478bd9Sstevel@tonic-gate 	icmpsize = sizeof (mld2r_t);
25557c478bd9Sstevel@tonic-gate 	size = IPV6_HDR_LEN + optlen + padlen + icmpsize;
25567c478bd9Sstevel@tonic-gate 	morepkts = B_FALSE;
25577c478bd9Sstevel@tonic-gate 	more_src_cnt = 0;
25587c478bd9Sstevel@tonic-gate 	for (rp = cur_reclist = next_reclist, numrec = 0; rp != NULL;
25597c478bd9Sstevel@tonic-gate 	    rp = rp->mrec_next, numrec++) {
25607c478bd9Sstevel@tonic-gate 		rsize = sizeof (mld2mar_t) +
25617c478bd9Sstevel@tonic-gate 		    (rp->mrec_srcs.sl_numsrc * sizeof (in6_addr_t));
25621eee170aSErik Nordmark 		if (size + rsize > ill->ill_mc_mtu) {
25637c478bd9Sstevel@tonic-gate 			if (rp == cur_reclist) {
25647c478bd9Sstevel@tonic-gate 				/*
25657c478bd9Sstevel@tonic-gate 				 * If the first mrec we looked at is too big
25667c478bd9Sstevel@tonic-gate 				 * to fit in a single packet (i.e the source
25677c478bd9Sstevel@tonic-gate 				 * list is too big), we must either truncate
25687c478bd9Sstevel@tonic-gate 				 * the list (if TO_EX or IS_EX), or send
25697c478bd9Sstevel@tonic-gate 				 * multiple reports for the same group (all
25707c478bd9Sstevel@tonic-gate 				 * other types).
25717c478bd9Sstevel@tonic-gate 				 */
25727c478bd9Sstevel@tonic-gate 				int srcspace, srcsperpkt;
25731eee170aSErik Nordmark 				srcspace = ill->ill_mc_mtu -
25747c478bd9Sstevel@tonic-gate 				    (size + sizeof (mld2mar_t));
2575e11c3f44Smeem 
2576e11c3f44Smeem 				/*
2577e11c3f44Smeem 				 * Skip if there's not even enough room in
2578e11c3f44Smeem 				 * a single packet to send something useful.
2579e11c3f44Smeem 				 */
2580e11c3f44Smeem 				if (srcspace <= sizeof (in6_addr_t))
2581e11c3f44Smeem 					continue;
2582e11c3f44Smeem 
25837c478bd9Sstevel@tonic-gate 				srcsperpkt = srcspace / sizeof (in6_addr_t);
25847c478bd9Sstevel@tonic-gate 				/*
25857c478bd9Sstevel@tonic-gate 				 * Increment icmpsize and size, because we will
25867c478bd9Sstevel@tonic-gate 				 * be sending a record for the mrec we're
25877c478bd9Sstevel@tonic-gate 				 * looking at now.
25887c478bd9Sstevel@tonic-gate 				 */
25897c478bd9Sstevel@tonic-gate 				rsize = sizeof (mld2mar_t) +
25907c478bd9Sstevel@tonic-gate 				    (srcsperpkt * sizeof (in6_addr_t));
25917c478bd9Sstevel@tonic-gate 				icmpsize += rsize;
25927c478bd9Sstevel@tonic-gate 				size += rsize;
25937c478bd9Sstevel@tonic-gate 				if (rp->mrec_type == MODE_IS_EXCLUDE ||
25947c478bd9Sstevel@tonic-gate 				    rp->mrec_type == CHANGE_TO_EXCLUDE) {
25957c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
25967c478bd9Sstevel@tonic-gate 					if (rp->mrec_next == NULL) {
25977c478bd9Sstevel@tonic-gate 						/* no more packets to send */
25987c478bd9Sstevel@tonic-gate 						break;
25997c478bd9Sstevel@tonic-gate 					} else {
26007c478bd9Sstevel@tonic-gate 						/*
26017c478bd9Sstevel@tonic-gate 						 * more packets, but we're
26027c478bd9Sstevel@tonic-gate 						 * done with this mrec.
26037c478bd9Sstevel@tonic-gate 						 */
26047c478bd9Sstevel@tonic-gate 						next_reclist = rp->mrec_next;
26057c478bd9Sstevel@tonic-gate 					}
26067c478bd9Sstevel@tonic-gate 				} else {
26077c478bd9Sstevel@tonic-gate 					more_src_cnt = rp->mrec_srcs.sl_numsrc
26087c478bd9Sstevel@tonic-gate 					    - srcsperpkt;
26097c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
26107c478bd9Sstevel@tonic-gate 					/*
26117c478bd9Sstevel@tonic-gate 					 * We'll fix up this mrec (remove the
26127c478bd9Sstevel@tonic-gate 					 * srcs we've already sent) before
26137c478bd9Sstevel@tonic-gate 					 * returning to nextpkt above.
26147c478bd9Sstevel@tonic-gate 					 */
26157c478bd9Sstevel@tonic-gate 					next_reclist = rp;
26167c478bd9Sstevel@tonic-gate 				}
26177c478bd9Sstevel@tonic-gate 			} else {
26187c478bd9Sstevel@tonic-gate 				next_reclist = rp;
26197c478bd9Sstevel@tonic-gate 			}
26207c478bd9Sstevel@tonic-gate 			morepkts = B_TRUE;
26217c478bd9Sstevel@tonic-gate 			break;
26227c478bd9Sstevel@tonic-gate 		}
26237c478bd9Sstevel@tonic-gate 		icmpsize += rsize;
26247c478bd9Sstevel@tonic-gate 		size += rsize;
26257c478bd9Sstevel@tonic-gate 	}
26267c478bd9Sstevel@tonic-gate 
26277c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
26287c478bd9Sstevel@tonic-gate 	if (mp == NULL)
26297c478bd9Sstevel@tonic-gate 		goto free_reclist;
26307c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, size);
26317c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + size;
26327c478bd9Sstevel@tonic-gate 
2633e11c3f44Smeem 	ip6h = (ip6_t *)mp->b_rptr;
26347c478bd9Sstevel@tonic-gate 	ip6hbh = (ip6_hbh_t *)&(ip6h[1]);
26357c478bd9Sstevel@tonic-gate 	ip6router = (struct ip6_opt_router *)&(ip6hbh[1]);
26367c478bd9Sstevel@tonic-gate 	mld2r = (mld2r_t *)((uint8_t *)ip6hbh + optlen + padlen);
26377c478bd9Sstevel@tonic-gate 	mld2mar = (mld2mar_t *)&(mld2r[1]);
26387c478bd9Sstevel@tonic-gate 
26397c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
26407c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = htons(optlen + padlen + icmpsize);
26417c478bd9Sstevel@tonic-gate 	ip6h->ip6_nxt = IPPROTO_HOPOPTS;
26427c478bd9Sstevel@tonic-gate 	ip6h->ip6_hops = MLD_HOP_LIMIT;
26437c478bd9Sstevel@tonic-gate 	ip6h->ip6_dst = ipv6_all_v2rtrs_mcast;
2644bd670b35SErik Nordmark 	ip6h->ip6_src = ipv6_all_zeros;
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_nxt = IPPROTO_ICMPV6;
26477c478bd9Sstevel@tonic-gate 	/*
26487c478bd9Sstevel@tonic-gate 	 * ip6h_len is the number of 8-byte words, not including the first
26497c478bd9Sstevel@tonic-gate 	 * 8 bytes; we've assumed optlen + padlen == 8 bytes; hence len = 0.
26507c478bd9Sstevel@tonic-gate 	 */
26517c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_len = 0;
26527c478bd9Sstevel@tonic-gate 
26537c478bd9Sstevel@tonic-gate 	ip6router->ip6or_type = IP6OPT_ROUTER_ALERT;
26547c478bd9Sstevel@tonic-gate 	ip6router->ip6or_len = 2;
26557c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[0] = 0;
26567c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[1] = IP6_ALERT_MLD;
26577c478bd9Sstevel@tonic-gate 
26587c478bd9Sstevel@tonic-gate 	mld2r->mld2r_type = MLD_V2_LISTENER_REPORT;
26597c478bd9Sstevel@tonic-gate 	mld2r->mld2r_nummar = htons(numrec);
26607c478bd9Sstevel@tonic-gate 	/*
26617c478bd9Sstevel@tonic-gate 	 * Prepare for the checksum by putting icmp length in the icmp
2662bd670b35SErik Nordmark 	 * checksum field. The checksum is calculated in ip_output_simple.
26637c478bd9Sstevel@tonic-gate 	 */
26647c478bd9Sstevel@tonic-gate 	mld2r->mld2r_cksum = htons(icmpsize);
26657c478bd9Sstevel@tonic-gate 
26667c478bd9Sstevel@tonic-gate 	for (rp = cur_reclist; rp != NULL; rp = rp->mrec_next) {
26677c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_type = rp->mrec_type;
26687c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_auxlen = 0;
26697c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_numsrc = htons(rp->mrec_srcs.sl_numsrc);
26707c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_group = rp->mrec_group;
26717c478bd9Sstevel@tonic-gate 		srcarray = (in6_addr_t *)&(mld2mar[1]);
26727c478bd9Sstevel@tonic-gate 
26737c478bd9Sstevel@tonic-gate 		for (i = 0; i < rp->mrec_srcs.sl_numsrc; i++)
26747c478bd9Sstevel@tonic-gate 			srcarray[i] = rp->mrec_srcs.sl_addr[i];
26757c478bd9Sstevel@tonic-gate 
26767c478bd9Sstevel@tonic-gate 		mld2mar = (mld2mar_t *)&(srcarray[i]);
26777c478bd9Sstevel@tonic-gate 	}
26787c478bd9Sstevel@tonic-gate 
2679bd670b35SErik Nordmark 	ill_mcast_queue(ill, mp);
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 	if (morepkts) {
26827c478bd9Sstevel@tonic-gate 		if (more_src_cnt > 0) {
26837c478bd9Sstevel@tonic-gate 			int index, mvsize;
26847c478bd9Sstevel@tonic-gate 			slist_t *sl = &next_reclist->mrec_srcs;
26857c478bd9Sstevel@tonic-gate 			index = sl->sl_numsrc;
26867c478bd9Sstevel@tonic-gate 			mvsize = more_src_cnt * sizeof (in6_addr_t);
26877c478bd9Sstevel@tonic-gate 			(void) memmove(&sl->sl_addr[0], &sl->sl_addr[index],
26887c478bd9Sstevel@tonic-gate 			    mvsize);
26897c478bd9Sstevel@tonic-gate 			sl->sl_numsrc = more_src_cnt;
26907c478bd9Sstevel@tonic-gate 		}
26917c478bd9Sstevel@tonic-gate 		goto nextpkt;
26927c478bd9Sstevel@tonic-gate 	}
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate free_reclist:
26957c478bd9Sstevel@tonic-gate 	while (reclist != NULL) {
26967c478bd9Sstevel@tonic-gate 		rp = reclist->mrec_next;
26977c478bd9Sstevel@tonic-gate 		mi_free(reclist);
26987c478bd9Sstevel@tonic-gate 		reclist = rp;
26997c478bd9Sstevel@tonic-gate 	}
27007c478bd9Sstevel@tonic-gate }
27017c478bd9Sstevel@tonic-gate 
27027c478bd9Sstevel@tonic-gate static mrec_t *
27037c478bd9Sstevel@tonic-gate mcast_bldmrec(mcast_record_t type, in6_addr_t *grp, slist_t *srclist,
27047c478bd9Sstevel@tonic-gate     mrec_t *next)
27057c478bd9Sstevel@tonic-gate {
27067c478bd9Sstevel@tonic-gate 	mrec_t *rp;
27077c478bd9Sstevel@tonic-gate 	int i;
27087c478bd9Sstevel@tonic-gate 
27097c478bd9Sstevel@tonic-gate 	if ((type == ALLOW_NEW_SOURCES || type == BLOCK_OLD_SOURCES) &&
27107c478bd9Sstevel@tonic-gate 	    SLIST_IS_EMPTY(srclist))
27117c478bd9Sstevel@tonic-gate 		return (next);
27127c478bd9Sstevel@tonic-gate 
27137c478bd9Sstevel@tonic-gate 	rp = (mrec_t *)mi_alloc(sizeof (mrec_t), BPRI_HI);
27147c478bd9Sstevel@tonic-gate 	if (rp == NULL)
27157c478bd9Sstevel@tonic-gate 		return (next);
27167c478bd9Sstevel@tonic-gate 
27177c478bd9Sstevel@tonic-gate 	rp->mrec_next = next;
27187c478bd9Sstevel@tonic-gate 	rp->mrec_type = type;
27197c478bd9Sstevel@tonic-gate 	rp->mrec_auxlen = 0;
27207c478bd9Sstevel@tonic-gate 	rp->mrec_group = *grp;
27217c478bd9Sstevel@tonic-gate 	if (srclist == NULL) {
27227c478bd9Sstevel@tonic-gate 		rp->mrec_srcs.sl_numsrc = 0;
27237c478bd9Sstevel@tonic-gate 	} else {
27247c478bd9Sstevel@tonic-gate 		rp->mrec_srcs.sl_numsrc = srclist->sl_numsrc;
27257c478bd9Sstevel@tonic-gate 		for (i = 0; i < srclist->sl_numsrc; i++)
27267c478bd9Sstevel@tonic-gate 			rp->mrec_srcs.sl_addr[i] = srclist->sl_addr[i];
27277c478bd9Sstevel@tonic-gate 	}
27287c478bd9Sstevel@tonic-gate 
27297c478bd9Sstevel@tonic-gate 	return (rp);
27307c478bd9Sstevel@tonic-gate }
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate /*
27337c478bd9Sstevel@tonic-gate  * Set up initial retransmit state.  If memory cannot be allocated for
27347c478bd9Sstevel@tonic-gate  * the source lists, simply create as much state as is possible; memory
27357c478bd9Sstevel@tonic-gate  * allocation failures are considered one type of transient error that
27367c478bd9Sstevel@tonic-gate  * the retransmissions are designed to overcome (and if they aren't
27377c478bd9Sstevel@tonic-gate  * transient, there are bigger problems than failing to notify the
27387c478bd9Sstevel@tonic-gate  * router about multicast group membership state changes).
27397c478bd9Sstevel@tonic-gate  */
27407c478bd9Sstevel@tonic-gate static void
27417c478bd9Sstevel@tonic-gate mcast_init_rtx(ill_t *ill, rtx_state_t *rtxp, mcast_record_t rtype,
27427c478bd9Sstevel@tonic-gate     slist_t *flist)
27437c478bd9Sstevel@tonic-gate {
27447c478bd9Sstevel@tonic-gate 	/*
27457c478bd9Sstevel@tonic-gate 	 * There are only three possibilities for rtype:
27467c478bd9Sstevel@tonic-gate 	 *	New join, transition from INCLUDE {} to INCLUDE {flist}
27477c478bd9Sstevel@tonic-gate 	 *	  => rtype is ALLOW_NEW_SOURCES
27487c478bd9Sstevel@tonic-gate 	 *	New join, transition from INCLUDE {} to EXCLUDE {flist}
27497c478bd9Sstevel@tonic-gate 	 *	  => rtype is CHANGE_TO_EXCLUDE
27507c478bd9Sstevel@tonic-gate 	 *	State change that involves a filter mode change
27517c478bd9Sstevel@tonic-gate 	 *	  => rtype is either CHANGE_TO_INCLUDE or CHANGE_TO_EXCLUDE
27527c478bd9Sstevel@tonic-gate 	 */
27537c478bd9Sstevel@tonic-gate 	ASSERT(rtype == CHANGE_TO_EXCLUDE || rtype == CHANGE_TO_INCLUDE ||
27547c478bd9Sstevel@tonic-gate 	    rtype == ALLOW_NEW_SOURCES);
27557c478bd9Sstevel@tonic-gate 
27567c478bd9Sstevel@tonic-gate 	rtxp->rtx_cnt = ill->ill_mcast_rv;
27577c478bd9Sstevel@tonic-gate 
27587c478bd9Sstevel@tonic-gate 	switch (rtype) {
27597c478bd9Sstevel@tonic-gate 	case CHANGE_TO_EXCLUDE:
27607c478bd9Sstevel@tonic-gate 		rtxp->rtx_fmode_cnt = ill->ill_mcast_rv;
27617c478bd9Sstevel@tonic-gate 		CLEAR_SLIST(rtxp->rtx_allow);
27627c478bd9Sstevel@tonic-gate 		COPY_SLIST(flist, rtxp->rtx_block);
27637c478bd9Sstevel@tonic-gate 		break;
27647c478bd9Sstevel@tonic-gate 	case ALLOW_NEW_SOURCES:
27657c478bd9Sstevel@tonic-gate 	case CHANGE_TO_INCLUDE:
27667c478bd9Sstevel@tonic-gate 		rtxp->rtx_fmode_cnt =
27677c478bd9Sstevel@tonic-gate 		    rtype == ALLOW_NEW_SOURCES ? 0 : ill->ill_mcast_rv;
27687c478bd9Sstevel@tonic-gate 		CLEAR_SLIST(rtxp->rtx_block);
27697c478bd9Sstevel@tonic-gate 		COPY_SLIST(flist, rtxp->rtx_allow);
27707c478bd9Sstevel@tonic-gate 		break;
27717c478bd9Sstevel@tonic-gate 	}
27727c478bd9Sstevel@tonic-gate }
27737c478bd9Sstevel@tonic-gate 
27747c478bd9Sstevel@tonic-gate /*
27757c478bd9Sstevel@tonic-gate  * The basic strategy here, as extrapolated from RFC 3810 section 6.1 and
27767c478bd9Sstevel@tonic-gate  * RFC 3376 section 5.1, covers three cases:
27777c478bd9Sstevel@tonic-gate  *	* The current state change is a filter mode change
27787c478bd9Sstevel@tonic-gate  *		Set filter mode retransmit counter; set retransmit allow or
27797c478bd9Sstevel@tonic-gate  *		block list to new source list as appropriate, and clear the
27807c478bd9Sstevel@tonic-gate  *		retransmit list that was not set; send TO_IN or TO_EX with
27817c478bd9Sstevel@tonic-gate  *		new source list.
27827c478bd9Sstevel@tonic-gate  *	* The current state change is a source list change, but the filter
27837c478bd9Sstevel@tonic-gate  *	  mode retransmit counter is > 0
27847c478bd9Sstevel@tonic-gate  *		Decrement filter mode retransmit counter; set retransmit
27857c478bd9Sstevel@tonic-gate  *		allow or block list to  new source list as appropriate,
27867c478bd9Sstevel@tonic-gate  *		and clear the retransmit list that was not set; send TO_IN
27877c478bd9Sstevel@tonic-gate  *		or TO_EX with new source list.
27887c478bd9Sstevel@tonic-gate  *	* The current state change is a source list change, and the filter
27897c478bd9Sstevel@tonic-gate  *	  mode retransmit counter is 0.
27907c478bd9Sstevel@tonic-gate  *		Merge existing rtx allow and block lists with new state:
27917c478bd9Sstevel@tonic-gate  *		  rtx_allow = (new allow + rtx_allow) - new block
27927c478bd9Sstevel@tonic-gate  *		  rtx_block = (new block + rtx_block) - new allow
27937c478bd9Sstevel@tonic-gate  *		Send ALLOW and BLOCK records for new retransmit lists;
27947c478bd9Sstevel@tonic-gate  *		decrement retransmit counter.
27957c478bd9Sstevel@tonic-gate  *
27967c478bd9Sstevel@tonic-gate  * As is the case for mcast_init_rtx(), memory allocation failures are
27977c478bd9Sstevel@tonic-gate  * acceptable; we just create as much state as we can.
27987c478bd9Sstevel@tonic-gate  */
27997c478bd9Sstevel@tonic-gate static mrec_t *
28007c478bd9Sstevel@tonic-gate mcast_merge_rtx(ilm_t *ilm, mrec_t *mreclist, slist_t *flist)
28017c478bd9Sstevel@tonic-gate {
28027c478bd9Sstevel@tonic-gate 	ill_t *ill;
28037c478bd9Sstevel@tonic-gate 	rtx_state_t *rtxp = &ilm->ilm_rtx;
28047c478bd9Sstevel@tonic-gate 	mcast_record_t txtype;
28057c478bd9Sstevel@tonic-gate 	mrec_t *rp, *rpnext, *rtnmrec;
28067c478bd9Sstevel@tonic-gate 	boolean_t ovf;
28077c478bd9Sstevel@tonic-gate 
2808bd670b35SErik Nordmark 	ill = ilm->ilm_ill;
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate 	if (mreclist == NULL)
28117c478bd9Sstevel@tonic-gate 		return (mreclist);
28127c478bd9Sstevel@tonic-gate 
28137c478bd9Sstevel@tonic-gate 	/*
28147c478bd9Sstevel@tonic-gate 	 * A filter mode change is indicated by a single mrec, which is
28157c478bd9Sstevel@tonic-gate 	 * either TO_IN or TO_EX.  In this case, we just need to set new
28167c478bd9Sstevel@tonic-gate 	 * retransmit state as if this were an initial join.  There is
28177c478bd9Sstevel@tonic-gate 	 * no change to the mrec list.
28187c478bd9Sstevel@tonic-gate 	 */
28197c478bd9Sstevel@tonic-gate 	if (mreclist->mrec_type == CHANGE_TO_INCLUDE ||
28207c478bd9Sstevel@tonic-gate 	    mreclist->mrec_type == CHANGE_TO_EXCLUDE) {
28217c478bd9Sstevel@tonic-gate 		mcast_init_rtx(ill, rtxp, mreclist->mrec_type,
28227c478bd9Sstevel@tonic-gate 		    &mreclist->mrec_srcs);
28237c478bd9Sstevel@tonic-gate 		return (mreclist);
28247c478bd9Sstevel@tonic-gate 	}
28257c478bd9Sstevel@tonic-gate 
28267c478bd9Sstevel@tonic-gate 	/*
28277c478bd9Sstevel@tonic-gate 	 * Only the source list has changed
28287c478bd9Sstevel@tonic-gate 	 */
28297c478bd9Sstevel@tonic-gate 	rtxp->rtx_cnt = ill->ill_mcast_rv;
28307c478bd9Sstevel@tonic-gate 	if (rtxp->rtx_fmode_cnt > 0) {
28317c478bd9Sstevel@tonic-gate 		/* but we're still sending filter mode change reports */
28327c478bd9Sstevel@tonic-gate 		rtxp->rtx_fmode_cnt--;
28337c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
28347c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_block);
28357c478bd9Sstevel@tonic-gate 			COPY_SLIST(flist, rtxp->rtx_allow);
28367c478bd9Sstevel@tonic-gate 			txtype = CHANGE_TO_INCLUDE;
28377c478bd9Sstevel@tonic-gate 		} else {
28387c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_allow);
28397c478bd9Sstevel@tonic-gate 			COPY_SLIST(flist, rtxp->rtx_block);
28407c478bd9Sstevel@tonic-gate 			txtype = CHANGE_TO_EXCLUDE;
28417c478bd9Sstevel@tonic-gate 		}
28427c478bd9Sstevel@tonic-gate 		/* overwrite first mrec with new info */
28437c478bd9Sstevel@tonic-gate 		mreclist->mrec_type = txtype;
28447c478bd9Sstevel@tonic-gate 		l_copy(flist, &mreclist->mrec_srcs);
28457c478bd9Sstevel@tonic-gate 		/* then free any remaining mrecs */
28467c478bd9Sstevel@tonic-gate 		for (rp = mreclist->mrec_next; rp != NULL; rp = rpnext) {
28477c478bd9Sstevel@tonic-gate 			rpnext = rp->mrec_next;
28487c478bd9Sstevel@tonic-gate 			mi_free(rp);
28497c478bd9Sstevel@tonic-gate 		}
28507c478bd9Sstevel@tonic-gate 		mreclist->mrec_next = NULL;
28517c478bd9Sstevel@tonic-gate 		rtnmrec = mreclist;
28527c478bd9Sstevel@tonic-gate 	} else {
28537c478bd9Sstevel@tonic-gate 		mrec_t *allow_mrec, *block_mrec;
28547c478bd9Sstevel@tonic-gate 		/*
28557c478bd9Sstevel@tonic-gate 		 * Just send the source change reports; but we need to
28567c478bd9Sstevel@tonic-gate 		 * recalculate the ALLOW and BLOCK lists based on previous
28577c478bd9Sstevel@tonic-gate 		 * state and new changes.
28587c478bd9Sstevel@tonic-gate 		 */
28597c478bd9Sstevel@tonic-gate 		rtnmrec = mreclist;
28607c478bd9Sstevel@tonic-gate 		allow_mrec = block_mrec = NULL;
28617c478bd9Sstevel@tonic-gate 		for (rp = mreclist; rp != NULL; rp = rp->mrec_next) {
28627c478bd9Sstevel@tonic-gate 			ASSERT(rp->mrec_type == ALLOW_NEW_SOURCES ||
28637c478bd9Sstevel@tonic-gate 			    rp->mrec_type == BLOCK_OLD_SOURCES);
28647c478bd9Sstevel@tonic-gate 			if (rp->mrec_type == ALLOW_NEW_SOURCES)
28657c478bd9Sstevel@tonic-gate 				allow_mrec = rp;
28667c478bd9Sstevel@tonic-gate 			else
28677c478bd9Sstevel@tonic-gate 				block_mrec = rp;
28687c478bd9Sstevel@tonic-gate 		}
28697c478bd9Sstevel@tonic-gate 		/*
28707c478bd9Sstevel@tonic-gate 		 * Perform calculations:
28717c478bd9Sstevel@tonic-gate 		 *   new_allow = mrec_allow + (rtx_allow - mrec_block)
28727c478bd9Sstevel@tonic-gate 		 *   new_block = mrec_block + (rtx_block - mrec_allow)
28737c478bd9Sstevel@tonic-gate 		 *
28747c478bd9Sstevel@tonic-gate 		 * Each calc requires two steps, for example:
28757c478bd9Sstevel@tonic-gate 		 *   rtx_allow = rtx_allow - mrec_block;
28767c478bd9Sstevel@tonic-gate 		 *   new_allow = mrec_allow + rtx_allow;
28777c478bd9Sstevel@tonic-gate 		 *
28787c478bd9Sstevel@tonic-gate 		 * Store results in mrec lists, and then copy into rtx lists.
28797c478bd9Sstevel@tonic-gate 		 * We do it in this order in case the rtx list hasn't been
28807c478bd9Sstevel@tonic-gate 		 * alloc'd yet; if it hasn't and our alloc fails, that's okay,
28817c478bd9Sstevel@tonic-gate 		 * Overflows are also okay.
28827c478bd9Sstevel@tonic-gate 		 */
28837c478bd9Sstevel@tonic-gate 		if (block_mrec != NULL) {
28847c478bd9Sstevel@tonic-gate 			l_difference_in_a(rtxp->rtx_allow,
28857c478bd9Sstevel@tonic-gate 			    &block_mrec->mrec_srcs);
28867c478bd9Sstevel@tonic-gate 		}
28877c478bd9Sstevel@tonic-gate 		if (allow_mrec != NULL) {
28887c478bd9Sstevel@tonic-gate 			l_difference_in_a(rtxp->rtx_block,
28897c478bd9Sstevel@tonic-gate 			    &allow_mrec->mrec_srcs);
28907c478bd9Sstevel@tonic-gate 			l_union_in_a(&allow_mrec->mrec_srcs, rtxp->rtx_allow,
28917c478bd9Sstevel@tonic-gate 			    &ovf);
28927c478bd9Sstevel@tonic-gate 		}
28937c478bd9Sstevel@tonic-gate 		if (block_mrec != NULL) {
28947c478bd9Sstevel@tonic-gate 			l_union_in_a(&block_mrec->mrec_srcs, rtxp->rtx_block,
28957c478bd9Sstevel@tonic-gate 			    &ovf);
28967c478bd9Sstevel@tonic-gate 			COPY_SLIST(&block_mrec->mrec_srcs, rtxp->rtx_block);
28977c478bd9Sstevel@tonic-gate 		} else {
28987c478bd9Sstevel@tonic-gate 			rtnmrec = mcast_bldmrec(BLOCK_OLD_SOURCES,
28997c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_block, allow_mrec);
29007c478bd9Sstevel@tonic-gate 		}
29017c478bd9Sstevel@tonic-gate 		if (allow_mrec != NULL) {
29027c478bd9Sstevel@tonic-gate 			COPY_SLIST(&allow_mrec->mrec_srcs, rtxp->rtx_allow);
29037c478bd9Sstevel@tonic-gate 		} else {
29047c478bd9Sstevel@tonic-gate 			rtnmrec = mcast_bldmrec(ALLOW_NEW_SOURCES,
29057c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_allow, block_mrec);
29067c478bd9Sstevel@tonic-gate 		}
29077c478bd9Sstevel@tonic-gate 	}
29087c478bd9Sstevel@tonic-gate 
29097c478bd9Sstevel@tonic-gate 	return (rtnmrec);
29107c478bd9Sstevel@tonic-gate }
2911