xref: /illumos-gate/usr/src/uts/common/inet/ip/igmp.c (revision e11c3f44)
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 /*
22*e11c3f44Smeem  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Internet Group Management Protocol (IGMP) routines.
297c478bd9Sstevel@tonic-gate  * Multicast Listener Discovery Protocol (MLD) routines.
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * Written by Steve Deering, Stanford, May 1988.
327c478bd9Sstevel@tonic-gate  * Modified by Rosen Sharma, Stanford, Aug 1994.
337c478bd9Sstevel@tonic-gate  * Modified by Bill Fenner, Xerox PARC, Feb. 1995.
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  * MULTICAST 3.5.1.1
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include <sys/types.h>
397c478bd9Sstevel@tonic-gate #include <sys/stream.h>
407c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
417c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
427c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
437c478bd9Sstevel@tonic-gate #include <sys/systm.h>
447c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
457c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
467c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
477c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
487c478bd9Sstevel@tonic-gate #include <sys/zone.h>
49*e11c3f44Smeem #include <sys/callb.h>
507c478bd9Sstevel@tonic-gate #include <sys/param.h>
517c478bd9Sstevel@tonic-gate #include <sys/socket.h>
527c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
537c478bd9Sstevel@tonic-gate #include <net/if.h>
547c478bd9Sstevel@tonic-gate #include <net/route.h>
557c478bd9Sstevel@tonic-gate #include <netinet/in.h>
567c478bd9Sstevel@tonic-gate #include <netinet/igmp_var.h>
577c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
587c478bd9Sstevel@tonic-gate #include <netinet/icmp6.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>
637c478bd9Sstevel@tonic-gate #include <inet/ip.h>
647c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
657c478bd9Sstevel@tonic-gate #include <inet/ip_multi.h>
667c478bd9Sstevel@tonic-gate #include <inet/ip_listutils.h>
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate #include <netinet/igmp.h>
697c478bd9Sstevel@tonic-gate #include <inet/ip_if.h>
707c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
717c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.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);
797c478bd9Sstevel@tonic-gate static void	igmpv3_sendrpt(ipif_t *ipif, 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);
86*e11c3f44Smeem static void	mcast_signal_restart_thread(ip_stack_t *ipst);
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate /*
897c478bd9Sstevel@tonic-gate  * Macros used to do timer len conversions.  Timer values are always
907c478bd9Sstevel@tonic-gate  * stored and passed to the timer functions as milliseconds; but the
917c478bd9Sstevel@tonic-gate  * default values and values from the wire may not be.
927c478bd9Sstevel@tonic-gate  *
937c478bd9Sstevel@tonic-gate  * And yes, it's obscure, but decisecond is easier to abbreviate than
947c478bd9Sstevel@tonic-gate  * "tenths of a second".
957c478bd9Sstevel@tonic-gate  */
967c478bd9Sstevel@tonic-gate #define	DSEC_TO_MSEC(dsec)	((dsec) * 100)
977c478bd9Sstevel@tonic-gate #define	SEC_TO_MSEC(sec)	((sec) * 1000)
987c478bd9Sstevel@tonic-gate 
998dc47d9fSudpa /*
1008dc47d9fSudpa  * A running timer (scheduled thru timeout) can be cancelled if another
1018dc47d9fSudpa  * timer with a shorter timeout value is scheduled before it has timed
1028dc47d9fSudpa  * out.  When the shorter timer expires, the original timer is updated
1038dc47d9fSudpa  * to account for the time elapsed while the shorter timer ran; but this
1048dc47d9fSudpa  * does not take into account the amount of time already spent in timeout
1058dc47d9fSudpa  * state before being preempted by the shorter timer, that is the time
1068dc47d9fSudpa  * interval between time scheduled to time cancelled.  This can cause
1078dc47d9fSudpa  * delays in sending out multicast membership reports.  To resolve this
1088dc47d9fSudpa  * problem, wallclock time (absolute time) is used instead of deltas
1098dc47d9fSudpa  * (relative time) to track timers.
1108dc47d9fSudpa  *
1118dc47d9fSudpa  * The MACRO below gets the lbolt value, used for proper timer scheduling
1128dc47d9fSudpa  * and firing. Therefore multicast membership reports are sent on time.
1138dc47d9fSudpa  * The timer does not exactly fire at the time it was scehduled to fire,
1148dc47d9fSudpa  * there is a difference of a few milliseconds observed. An offset is used
1158dc47d9fSudpa  * to take care of the difference.
1168dc47d9fSudpa  */
1178dc47d9fSudpa 
1188dc47d9fSudpa #define	CURRENT_MSTIME	((uint_t)TICK_TO_MSEC(ddi_get_lbolt()))
1198dc47d9fSudpa #define	CURRENT_OFFSET	(999)
1208dc47d9fSudpa 
1217c478bd9Sstevel@tonic-gate /*
1227c478bd9Sstevel@tonic-gate  * The first multicast join will trigger the igmp timers / mld timers
1237c478bd9Sstevel@tonic-gate  * The unit for next is milliseconds.
1247c478bd9Sstevel@tonic-gate  */
125*e11c3f44Smeem static void
126f4b3ec61Sdh igmp_start_timers(unsigned next, ip_stack_t *ipst)
1277c478bd9Sstevel@tonic-gate {
1287c478bd9Sstevel@tonic-gate 	int	time_left;
1297c478bd9Sstevel@tonic-gate 	int	ret;
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 		/*
1527c478bd9Sstevel@tonic-gate 		 * The timer is inactive. We need to start a timer
1537c478bd9Sstevel@tonic-gate 		 */
154f4b3ec61Sdh 		ipst->ips_igmp_time_to_next = next;
155f4b3ec61Sdh 		ipst->ips_igmp_timeout_id = timeout(igmp_timeout_handler,
156f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
1578dc47d9fSudpa 		ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
158f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_FALSE;
159f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1607c478bd9Sstevel@tonic-gate 		return;
1617c478bd9Sstevel@tonic-gate 	}
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	/*
1647c478bd9Sstevel@tonic-gate 	 * The timer was scheduled sometime back for firing in
1657c478bd9Sstevel@tonic-gate 	 * 'igmp_time_to_next' ms and is active. We need to
1667c478bd9Sstevel@tonic-gate 	 * reschedule the timeout if the new 'next' will happen
1677c478bd9Sstevel@tonic-gate 	 * earlier than the currently scheduled timeout
1687c478bd9Sstevel@tonic-gate 	 */
1698dc47d9fSudpa 	time_left = ipst->ips_igmp_timer_scheduled_last +
170f4b3ec61Sdh 	    MSEC_TO_TICK(ipst->ips_igmp_time_to_next) - ddi_get_lbolt();
1717c478bd9Sstevel@tonic-gate 	if (time_left < MSEC_TO_TICK(next)) {
172f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_FALSE;
173f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1747c478bd9Sstevel@tonic-gate 		return;
1757c478bd9Sstevel@tonic-gate 	}
1767c478bd9Sstevel@tonic-gate 
177f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
178f4b3ec61Sdh 	ret = untimeout(ipst->ips_igmp_timeout_id);
179f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1807c478bd9Sstevel@tonic-gate 	/*
1817c478bd9Sstevel@tonic-gate 	 * The timeout was cancelled, or the timeout handler
1827c478bd9Sstevel@tonic-gate 	 * completed, while we were blocked in the untimeout.
1837c478bd9Sstevel@tonic-gate 	 * No other thread could have set the timer meanwhile
1847c478bd9Sstevel@tonic-gate 	 * since we serialized all the timer setters. Thus
1857c478bd9Sstevel@tonic-gate 	 * no timer is currently active nor executing nor will
1867c478bd9Sstevel@tonic-gate 	 * any timer fire in the future. We start the timer now
1877c478bd9Sstevel@tonic-gate 	 * if needed.
1887c478bd9Sstevel@tonic-gate 	 */
1897c478bd9Sstevel@tonic-gate 	if (ret == -1) {
190f4b3ec61Sdh 		ASSERT(ipst->ips_igmp_timeout_id == 0);
1917c478bd9Sstevel@tonic-gate 	} else {
192f4b3ec61Sdh 		ASSERT(ipst->ips_igmp_timeout_id != 0);
193f4b3ec61Sdh 		ipst->ips_igmp_timeout_id = 0;
1947c478bd9Sstevel@tonic-gate 	}
195f4b3ec61Sdh 	if (ipst->ips_igmp_time_to_next != 0) {
196f4b3ec61Sdh 		ipst->ips_igmp_time_to_next =
197f4b3ec61Sdh 		    MIN(ipst->ips_igmp_time_to_next, next);
198f4b3ec61Sdh 		ipst->ips_igmp_timeout_id = timeout(igmp_timeout_handler,
199f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
2008dc47d9fSudpa 		ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
2017c478bd9Sstevel@tonic-gate 	}
202f4b3ec61Sdh 	ipst->ips_igmp_timer_setter_active = B_FALSE;
203f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
2047c478bd9Sstevel@tonic-gate }
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate  * mld_start_timers:
2087c478bd9Sstevel@tonic-gate  * The unit for next is milliseconds.
2097c478bd9Sstevel@tonic-gate  */
210*e11c3f44Smeem static void
211f4b3ec61Sdh mld_start_timers(unsigned next, ip_stack_t *ipst)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate 	int	time_left;
2147c478bd9Sstevel@tonic-gate 	int	ret;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	ASSERT(next != 0 && next != INFINITY);
2177c478bd9Sstevel@tonic-gate 
218f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
219f4b3ec61Sdh 	if (ipst->ips_mld_timer_setter_active) {
2207c478bd9Sstevel@tonic-gate 		/*
2217c478bd9Sstevel@tonic-gate 		 * Serialize timer setters, one at a time. If the
2227c478bd9Sstevel@tonic-gate 		 * timer is currently being set by someone,
2237c478bd9Sstevel@tonic-gate 		 * just record the next time when it has to be
2247c478bd9Sstevel@tonic-gate 		 * invoked and return. The current setter will
2257c478bd9Sstevel@tonic-gate 		 * take care.
2267c478bd9Sstevel@tonic-gate 		 */
227f4b3ec61Sdh 		ipst->ips_mld_time_to_next =
228f4b3ec61Sdh 		    MIN(ipst->ips_mld_time_to_next, next);
229f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2307c478bd9Sstevel@tonic-gate 		return;
2317c478bd9Sstevel@tonic-gate 	} else {
232f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_TRUE;
2337c478bd9Sstevel@tonic-gate 	}
234f4b3ec61Sdh 	if (ipst->ips_mld_timeout_id == 0) {
2357c478bd9Sstevel@tonic-gate 		/*
2367c478bd9Sstevel@tonic-gate 		 * The timer is inactive. We need to start a timer
2377c478bd9Sstevel@tonic-gate 		 */
238f4b3ec61Sdh 		ipst->ips_mld_time_to_next = next;
239f4b3ec61Sdh 		ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
240f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_mld_time_to_next));
2418dc47d9fSudpa 		ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
242f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_FALSE;
243f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2447c478bd9Sstevel@tonic-gate 		return;
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	/*
2487c478bd9Sstevel@tonic-gate 	 * The timer was scheduled sometime back for firing in
2497c478bd9Sstevel@tonic-gate 	 * 'igmp_time_to_next' ms and is active. We need to
2507c478bd9Sstevel@tonic-gate 	 * reschedule the timeout if the new 'next' will happen
2517c478bd9Sstevel@tonic-gate 	 * earlier than the currently scheduled timeout
2527c478bd9Sstevel@tonic-gate 	 */
2538dc47d9fSudpa 	time_left = ipst->ips_mld_timer_scheduled_last +
254f4b3ec61Sdh 	    MSEC_TO_TICK(ipst->ips_mld_time_to_next) - ddi_get_lbolt();
2557c478bd9Sstevel@tonic-gate 	if (time_left < MSEC_TO_TICK(next)) {
256f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_FALSE;
257f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2587c478bd9Sstevel@tonic-gate 		return;
2597c478bd9Sstevel@tonic-gate 	}
2607c478bd9Sstevel@tonic-gate 
261f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
262f4b3ec61Sdh 	ret = untimeout(ipst->ips_mld_timeout_id);
263f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
2647c478bd9Sstevel@tonic-gate 	/*
2657c478bd9Sstevel@tonic-gate 	 * The timeout was cancelled, or the timeout handler
2667c478bd9Sstevel@tonic-gate 	 * completed, while we were blocked in the untimeout.
2677c478bd9Sstevel@tonic-gate 	 * No other thread could have set the timer meanwhile
2687c478bd9Sstevel@tonic-gate 	 * since we serialized all the timer setters. Thus
2697c478bd9Sstevel@tonic-gate 	 * no timer is currently active nor executing nor will
2707c478bd9Sstevel@tonic-gate 	 * any timer fire in the future. We start the timer now
2717c478bd9Sstevel@tonic-gate 	 * if needed.
2727c478bd9Sstevel@tonic-gate 	 */
2737c478bd9Sstevel@tonic-gate 	if (ret == -1) {
274f4b3ec61Sdh 		ASSERT(ipst->ips_mld_timeout_id == 0);
2757c478bd9Sstevel@tonic-gate 	} else {
276f4b3ec61Sdh 		ASSERT(ipst->ips_mld_timeout_id != 0);
277f4b3ec61Sdh 		ipst->ips_mld_timeout_id = 0;
2787c478bd9Sstevel@tonic-gate 	}
279f4b3ec61Sdh 	if (ipst->ips_mld_time_to_next != 0) {
280f4b3ec61Sdh 		ipst->ips_mld_time_to_next =
281f4b3ec61Sdh 		    MIN(ipst->ips_mld_time_to_next, next);
282f4b3ec61Sdh 		ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
283f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_mld_time_to_next));
2848dc47d9fSudpa 		ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
2857c478bd9Sstevel@tonic-gate 	}
286f4b3ec61Sdh 	ipst->ips_mld_timer_setter_active = B_FALSE;
287f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
2887c478bd9Sstevel@tonic-gate }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate /*
2917c478bd9Sstevel@tonic-gate  * igmp_input:
2926226e9d9Sethindra  * Return NULL for a bad packet that is discarded here.
2936226e9d9Sethindra  * Return mp if the message is OK and should be handed to "raw" receivers.
2947c478bd9Sstevel@tonic-gate  * Callers of igmp_input() may need to reinitialize variables that were copied
2957c478bd9Sstevel@tonic-gate  * from the mblk as this calls pullupmsg().
2967c478bd9Sstevel@tonic-gate  */
2977c478bd9Sstevel@tonic-gate /* ARGSUSED */
2986226e9d9Sethindra mblk_t *
2997c478bd9Sstevel@tonic-gate igmp_input(queue_t *q, mblk_t *mp, ill_t *ill)
3007c478bd9Sstevel@tonic-gate {
3017c478bd9Sstevel@tonic-gate 	igmpa_t 	*igmpa;
3027c478bd9Sstevel@tonic-gate 	ipha_t		*ipha = (ipha_t *)(mp->b_rptr);
3037c478bd9Sstevel@tonic-gate 	int		iphlen, igmplen, mblklen;
3047c478bd9Sstevel@tonic-gate 	ilm_t 		*ilm;
3057c478bd9Sstevel@tonic-gate 	uint32_t	src, dst;
3067c478bd9Sstevel@tonic-gate 	uint32_t 	group;
3077c478bd9Sstevel@tonic-gate 	uint_t		next;
3087c478bd9Sstevel@tonic-gate 	ipif_t 		*ipif;
309*e11c3f44Smeem 	ip_stack_t	*ipst;
310*e11c3f44Smeem 	ilm_walker_t	ilw;
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	ASSERT(ill != NULL);
3137c478bd9Sstevel@tonic-gate 	ASSERT(!ill->ill_isv6);
314f4b3ec61Sdh 	ipst = ill->ill_ipst;
315f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_rcv_total;
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	mblklen = MBLKL(mp);
3187c478bd9Sstevel@tonic-gate 	if (mblklen < 1 || mblklen < (iphlen = IPH_HDR_LENGTH(ipha))) {
319f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
3206226e9d9Sethindra 		goto bad_pkt;
3217c478bd9Sstevel@tonic-gate 	}
3227c478bd9Sstevel@tonic-gate 	igmplen = ntohs(ipha->ipha_length) - iphlen;
3237c478bd9Sstevel@tonic-gate 	/*
3247c478bd9Sstevel@tonic-gate 	 * Since msg sizes are more variable with v3, just pullup the
3257c478bd9Sstevel@tonic-gate 	 * whole thing now.
3267c478bd9Sstevel@tonic-gate 	 */
3277c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < (igmplen + iphlen)) {
3287c478bd9Sstevel@tonic-gate 		mblk_t *mp1;
3297c478bd9Sstevel@tonic-gate 		if ((mp1 = msgpullup(mp, -1)) == NULL) {
330f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_tooshort;
3316226e9d9Sethindra 			goto bad_pkt;
3327c478bd9Sstevel@tonic-gate 		}
3337c478bd9Sstevel@tonic-gate 		freemsg(mp);
3347c478bd9Sstevel@tonic-gate 		mp = mp1;
3357c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)(mp->b_rptr);
3367c478bd9Sstevel@tonic-gate 	}
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	/*
3397c478bd9Sstevel@tonic-gate 	 * Validate lengths
3407c478bd9Sstevel@tonic-gate 	 */
3417c478bd9Sstevel@tonic-gate 	if (igmplen < IGMP_MINLEN) {
342f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
3436226e9d9Sethindra 		goto bad_pkt;
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate 	/*
3467c478bd9Sstevel@tonic-gate 	 * Validate checksum
3477c478bd9Sstevel@tonic-gate 	 */
3487c478bd9Sstevel@tonic-gate 	if (IP_CSUM(mp, iphlen, 0)) {
349f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_badsum;
3506226e9d9Sethindra 		goto bad_pkt;
3517c478bd9Sstevel@tonic-gate 	}
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	igmpa = (igmpa_t *)(&mp->b_rptr[iphlen]);
3547c478bd9Sstevel@tonic-gate 	src = ipha->ipha_src;
3557c478bd9Sstevel@tonic-gate 	dst = ipha->ipha_dst;
3567c478bd9Sstevel@tonic-gate 	if (ip_debug > 1)
3577c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
3587c478bd9Sstevel@tonic-gate 		    "igmp_input: src 0x%x, dst 0x%x on %s\n",
3597c478bd9Sstevel@tonic-gate 		    (int)ntohl(src), (int)ntohl(dst),
3607c478bd9Sstevel@tonic-gate 		    ill->ill_name);
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	switch (igmpa->igmpa_type) {
3637c478bd9Sstevel@tonic-gate 	case IGMP_MEMBERSHIP_QUERY:
3647c478bd9Sstevel@tonic-gate 		/*
3657c478bd9Sstevel@tonic-gate 		 * packet length differentiates between v1/v2 and v3
3667c478bd9Sstevel@tonic-gate 		 * v1/v2 should be exactly 8 octets long; v3 is >= 12
3677c478bd9Sstevel@tonic-gate 		 */
3688dc47d9fSudpa 		if ((igmplen == IGMP_MINLEN) ||
3698dc47d9fSudpa 		    (ipst->ips_igmp_max_version <= IGMP_V2_ROUTER)) {
3707c478bd9Sstevel@tonic-gate 			next = igmp_query_in(ipha, igmpa, ill);
3717c478bd9Sstevel@tonic-gate 		} else if (igmplen >= IGMP_V3_QUERY_MINLEN) {
3727c478bd9Sstevel@tonic-gate 			next = igmpv3_query_in((igmp3qa_t *)igmpa, ill,
3737c478bd9Sstevel@tonic-gate 			    igmplen);
3747c478bd9Sstevel@tonic-gate 		} else {
375f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_tooshort;
3766226e9d9Sethindra 			goto bad_pkt;
3777c478bd9Sstevel@tonic-gate 		}
3786226e9d9Sethindra 		if (next == 0)
3796226e9d9Sethindra 			goto bad_pkt;
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 		if (next != INFINITY)
382f4b3ec61Sdh 			igmp_start_timers(next, ipst);
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 		break;
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	case IGMP_V1_MEMBERSHIP_REPORT:
3877c478bd9Sstevel@tonic-gate 	case IGMP_V2_MEMBERSHIP_REPORT:
3887c478bd9Sstevel@tonic-gate 		/*
3897c478bd9Sstevel@tonic-gate 		 * For fast leave to work, we have to know that we are the
3907c478bd9Sstevel@tonic-gate 		 * last person to send a report for this group. Reports
3917c478bd9Sstevel@tonic-gate 		 * generated by us are looped back since we could potentially
3927c478bd9Sstevel@tonic-gate 		 * be a multicast router, so discard reports sourced by me.
3937c478bd9Sstevel@tonic-gate 		 */
3947c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
3957c478bd9Sstevel@tonic-gate 		for (ipif = ill->ill_ipif; ipif != NULL;
3967c478bd9Sstevel@tonic-gate 		    ipif = ipif->ipif_next) {
3977c478bd9Sstevel@tonic-gate 			if (ipif->ipif_lcl_addr == src) {
3987c478bd9Sstevel@tonic-gate 				if (ip_debug > 1) {
3997c478bd9Sstevel@tonic-gate 					(void) mi_strlog(ill->ill_rq,
4007c478bd9Sstevel@tonic-gate 					    1,
4017c478bd9Sstevel@tonic-gate 					    SL_TRACE,
4027c478bd9Sstevel@tonic-gate 					    "igmp_input: we are only "
4037c478bd9Sstevel@tonic-gate 					    "member src 0x%x ipif_local 0x%x",
4047c478bd9Sstevel@tonic-gate 					    (int)ntohl(src),
405*e11c3f44Smeem 					    (int)ntohl(ipif->ipif_lcl_addr));
4067c478bd9Sstevel@tonic-gate 				}
4077c478bd9Sstevel@tonic-gate 				mutex_exit(&ill->ill_lock);
4086226e9d9Sethindra 				return (mp);
4097c478bd9Sstevel@tonic-gate 			}
4107c478bd9Sstevel@tonic-gate 		}
4117c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
4127c478bd9Sstevel@tonic-gate 
413f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_reports;
4147c478bd9Sstevel@tonic-gate 		group = igmpa->igmpa_group;
4157c478bd9Sstevel@tonic-gate 		if (!CLASSD(group)) {
416f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_badreports;
4176226e9d9Sethindra 			goto bad_pkt;
4187c478bd9Sstevel@tonic-gate 		}
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 		/*
4217c478bd9Sstevel@tonic-gate 		 * KLUDGE: if the IP source address of the report has an
4227c478bd9Sstevel@tonic-gate 		 * unspecified (i.e., zero) subnet number, as is allowed for
4237c478bd9Sstevel@tonic-gate 		 * a booting host, replace it with the correct subnet number
4247c478bd9Sstevel@tonic-gate 		 * so that a process-level multicast routing demon can
4257c478bd9Sstevel@tonic-gate 		 * determine which subnet it arrived from.  This is necessary
4267c478bd9Sstevel@tonic-gate 		 * to compensate for the lack of any way for a process to
4277c478bd9Sstevel@tonic-gate 		 * determine the arrival interface of an incoming packet.
4287c478bd9Sstevel@tonic-gate 		 *
4297c478bd9Sstevel@tonic-gate 		 * Requires that a copy of *this* message it passed up
4307c478bd9Sstevel@tonic-gate 		 * to the raw interface which is done by our caller.
4317c478bd9Sstevel@tonic-gate 		 */
4327c478bd9Sstevel@tonic-gate 		if ((src & htonl(0xFF000000U)) == 0) {	/* Minimum net mask */
4337c478bd9Sstevel@tonic-gate 			/* Pick the first ipif on this ill */
4347c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
4357c478bd9Sstevel@tonic-gate 			src = ill->ill_ipif->ipif_subnet;
4367c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
4377c478bd9Sstevel@tonic-gate 			ip1dbg(("igmp_input: changed src to 0x%x\n",
4387c478bd9Sstevel@tonic-gate 			    (int)ntohl(src)));
4397c478bd9Sstevel@tonic-gate 			ipha->ipha_src = src;
4407c478bd9Sstevel@tonic-gate 		}
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 		/*
443*e11c3f44Smeem 		 * If our ill has ILMs that belong to the group being
444*e11c3f44Smeem 		 * reported, and we are a 'Delaying Member' in the RFC
445*e11c3f44Smeem 		 * terminology, stop our timer for that group and 'clear
446*e11c3f44Smeem 		 * flag' i.e. mark as IGMP_OTHERMEMBER.
4477c478bd9Sstevel@tonic-gate 		 */
448*e11c3f44Smeem 		ilm = ilm_walker_start(&ilw, ill);
449*e11c3f44Smeem 		for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
450*e11c3f44Smeem 			if (ilm->ilm_addr == group) {
451f4b3ec61Sdh 				++ipst->ips_igmpstat.igps_rcv_ourreports;
4527c478bd9Sstevel@tonic-gate 				ilm->ilm_timer = INFINITY;
4537c478bd9Sstevel@tonic-gate 				ilm->ilm_state = IGMP_OTHERMEMBER;
4547c478bd9Sstevel@tonic-gate 			}
455*e11c3f44Smeem 		}
456*e11c3f44Smeem 		ilm_walker_finish(&ilw);
4577c478bd9Sstevel@tonic-gate 		break;
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	case IGMP_V3_MEMBERSHIP_REPORT:
4607c478bd9Sstevel@tonic-gate 		/*
4617c478bd9Sstevel@tonic-gate 		 * Currently nothing to do here; IGMP router is not
4627c478bd9Sstevel@tonic-gate 		 * implemented in ip, and v3 hosts don't pay attention
4637c478bd9Sstevel@tonic-gate 		 * to membership reports.
4647c478bd9Sstevel@tonic-gate 		 */
4657c478bd9Sstevel@tonic-gate 		break;
4667c478bd9Sstevel@tonic-gate 	}
4677c478bd9Sstevel@tonic-gate 	/*
4687c478bd9Sstevel@tonic-gate 	 * Pass all valid IGMP packets up to any process(es) listening
4697c478bd9Sstevel@tonic-gate 	 * on a raw IGMP socket. Do not free the packet.
4707c478bd9Sstevel@tonic-gate 	 */
4716226e9d9Sethindra 	return (mp);
4726226e9d9Sethindra 
4736226e9d9Sethindra bad_pkt:
4746226e9d9Sethindra 	freemsg(mp);
4756226e9d9Sethindra 	return (NULL);
4767c478bd9Sstevel@tonic-gate }
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate static uint_t
4797c478bd9Sstevel@tonic-gate igmp_query_in(ipha_t *ipha, igmpa_t *igmpa, ill_t *ill)
4807c478bd9Sstevel@tonic-gate {
4817c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
4827c478bd9Sstevel@tonic-gate 	int	timer;
4838dc47d9fSudpa 	uint_t	next, current;
484f4b3ec61Sdh 	ip_stack_t	 *ipst;
485*e11c3f44Smeem 	ilm_walker_t 	ilw;
4867c478bd9Sstevel@tonic-gate 
487f4b3ec61Sdh 	ipst = ill->ill_ipst;
488f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_rcv_queries;
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	/*
4917c478bd9Sstevel@tonic-gate 	 * In the IGMPv2 specification, there are 3 states and a flag.
4927c478bd9Sstevel@tonic-gate 	 *
4937c478bd9Sstevel@tonic-gate 	 * In Non-Member state, we simply don't have a membership record.
4947c478bd9Sstevel@tonic-gate 	 * In Delaying Member state, our timer is running (ilm->ilm_timer
4957c478bd9Sstevel@tonic-gate 	 * < INFINITY).  In Idle Member state, our timer is not running
4967c478bd9Sstevel@tonic-gate 	 * (ilm->ilm_timer == INFINITY).
4977c478bd9Sstevel@tonic-gate 	 *
4987c478bd9Sstevel@tonic-gate 	 * The flag is ilm->ilm_state, it is set to IGMP_OTHERMEMBER if
4997c478bd9Sstevel@tonic-gate 	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
5007c478bd9Sstevel@tonic-gate 	 * if I sent the last report.
5017c478bd9Sstevel@tonic-gate 	 */
5028dc47d9fSudpa 	if ((igmpa->igmpa_code == 0) ||
5038dc47d9fSudpa 	    (ipst->ips_igmp_max_version == IGMP_V1_ROUTER)) {
5047c478bd9Sstevel@tonic-gate 		/*
5057c478bd9Sstevel@tonic-gate 		 * Query from an old router.
5067c478bd9Sstevel@tonic-gate 		 * Remember that the querier on this interface is old,
5077c478bd9Sstevel@tonic-gate 		 * and set the timer to the value in RFC 1112.
5087c478bd9Sstevel@tonic-gate 		 */
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
5127c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v1_time = 0;
5137c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v1_tset = 1;
5147c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type != IGMP_V1_ROUTER) {
5157c478bd9Sstevel@tonic-gate 			ip1dbg(("Received IGMPv1 Query on %s, switching mode "
5167c478bd9Sstevel@tonic-gate 			    "to IGMP_V1_ROUTER\n", ill->ill_name));
5177c478bd9Sstevel@tonic-gate 			atomic_add_16(&ill->ill_ifptr->illif_mcast_v1, 1);
5187c478bd9Sstevel@tonic-gate 			ill->ill_mcast_type = IGMP_V1_ROUTER;
5197c478bd9Sstevel@tonic-gate 		}
5207c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 		timer = SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY);
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 		if (ipha->ipha_dst != htonl(INADDR_ALLHOSTS_GROUP) ||
5257c478bd9Sstevel@tonic-gate 		    igmpa->igmpa_group != 0) {
526f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_badqueries;
5277c478bd9Sstevel@tonic-gate 			return (0);
5287c478bd9Sstevel@tonic-gate 		}
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	} else {
5317c478bd9Sstevel@tonic-gate 		in_addr_t group;
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 		/*
5347c478bd9Sstevel@tonic-gate 		 * Query from a new router
5357c478bd9Sstevel@tonic-gate 		 * Simply do a validity check
5367c478bd9Sstevel@tonic-gate 		 */
5377c478bd9Sstevel@tonic-gate 		group = igmpa->igmpa_group;
5387c478bd9Sstevel@tonic-gate 		if (group != 0 && (!CLASSD(group))) {
539f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_badqueries;
5407c478bd9Sstevel@tonic-gate 			return (0);
5417c478bd9Sstevel@tonic-gate 		}
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 		/*
5447c478bd9Sstevel@tonic-gate 		 * Switch interface state to v2 on receipt of a v2 query
5457c478bd9Sstevel@tonic-gate 		 * ONLY IF current state is v3.  Let things be if current
5467c478bd9Sstevel@tonic-gate 		 * state if v1 but do reset the v2-querier-present timer.
5477c478bd9Sstevel@tonic-gate 		 */
5487c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
5497c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V3_ROUTER) {
5507c478bd9Sstevel@tonic-gate 			ip1dbg(("Received IGMPv2 Query on %s, switching mode "
5517c478bd9Sstevel@tonic-gate 			    "to IGMP_V2_ROUTER", ill->ill_name));
5527c478bd9Sstevel@tonic-gate 			atomic_add_16(&ill->ill_ifptr->illif_mcast_v2, 1);
5537c478bd9Sstevel@tonic-gate 			ill->ill_mcast_type = IGMP_V2_ROUTER;
5547c478bd9Sstevel@tonic-gate 		}
5557c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v2_time = 0;
5567c478bd9Sstevel@tonic-gate 		ill->ill_mcast_v2_tset = 1;
5577c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 		timer = DSEC_TO_MSEC((int)igmpa->igmpa_code);
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
5637c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
5647c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
5657c478bd9Sstevel@tonic-gate 		    "igmp_input: TIMER = igmp_code %d igmp_type 0x%x",
5667c478bd9Sstevel@tonic-gate 		    (int)ntohs(igmpa->igmpa_code),
5677c478bd9Sstevel@tonic-gate 		    (int)ntohs(igmpa->igmpa_type));
5687c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
5697c478bd9Sstevel@tonic-gate 	}
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	/*
5727c478bd9Sstevel@tonic-gate 	 * -Start the timers in all of our membership records
5737c478bd9Sstevel@tonic-gate 	 *  for the physical interface on which the query
5747c478bd9Sstevel@tonic-gate 	 *  arrived, excluding those that belong to the "all
5757c478bd9Sstevel@tonic-gate 	 *  hosts" group (224.0.0.1).
5767c478bd9Sstevel@tonic-gate 	 *
5777c478bd9Sstevel@tonic-gate 	 * -Restart any timer that is already running but has
5787c478bd9Sstevel@tonic-gate 	 *  a value longer than the requested timeout.
5797c478bd9Sstevel@tonic-gate 	 *
5807c478bd9Sstevel@tonic-gate 	 * -Use the value specified in the query message as
5817c478bd9Sstevel@tonic-gate 	 *  the maximum timeout.
5827c478bd9Sstevel@tonic-gate 	 */
5837c478bd9Sstevel@tonic-gate 	next = (unsigned)INFINITY;
5848dc47d9fSudpa 
585*e11c3f44Smeem 	ilm = ilm_walker_start(&ilw, ill);
586*e11c3f44Smeem 	mutex_enter(&ill->ill_lock);
5878dc47d9fSudpa 	current = CURRENT_MSTIME;
5887c478bd9Sstevel@tonic-gate 
589*e11c3f44Smeem 	for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
5907c478bd9Sstevel@tonic-gate 		/*
5917c478bd9Sstevel@tonic-gate 		 * A multicast router joins INADDR_ANY address
5927c478bd9Sstevel@tonic-gate 		 * to enable promiscuous reception of all
5937c478bd9Sstevel@tonic-gate 		 * mcasts from the interface. This INADDR_ANY
5947c478bd9Sstevel@tonic-gate 		 * is stored in the ilm_v6addr as V6 unspec addr
5957c478bd9Sstevel@tonic-gate 		 */
5967c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr))
5977c478bd9Sstevel@tonic-gate 			continue;
5987c478bd9Sstevel@tonic-gate 		if (ilm->ilm_addr == htonl(INADDR_ANY))
5997c478bd9Sstevel@tonic-gate 			continue;
6007c478bd9Sstevel@tonic-gate 		if (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP) &&
6017c478bd9Sstevel@tonic-gate 		    (igmpa->igmpa_group == 0) ||
6027c478bd9Sstevel@tonic-gate 		    (igmpa->igmpa_group == ilm->ilm_addr)) {
6037c478bd9Sstevel@tonic-gate 			if (ilm->ilm_timer > timer) {
6047c478bd9Sstevel@tonic-gate 				MCAST_RANDOM_DELAY(ilm->ilm_timer, timer);
6057c478bd9Sstevel@tonic-gate 				if (ilm->ilm_timer < next)
6067c478bd9Sstevel@tonic-gate 					next = ilm->ilm_timer;
6078dc47d9fSudpa 				ilm->ilm_timer += current;
6087c478bd9Sstevel@tonic-gate 			}
6097c478bd9Sstevel@tonic-gate 		}
6107c478bd9Sstevel@tonic-gate 	}
6117c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
612*e11c3f44Smeem 	ilm_walker_finish(&ilw);
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	return (next);
6157c478bd9Sstevel@tonic-gate }
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate static uint_t
6187c478bd9Sstevel@tonic-gate igmpv3_query_in(igmp3qa_t *igmp3qa, ill_t *ill, int igmplen)
6197c478bd9Sstevel@tonic-gate {
6207c478bd9Sstevel@tonic-gate 	uint_t		i, next, mrd, qqi, timer, delay, numsrc;
6218dc47d9fSudpa 	uint_t		current;
6227c478bd9Sstevel@tonic-gate 	ilm_t		*ilm;
6237c478bd9Sstevel@tonic-gate 	ipaddr_t	*src_array;
6247c478bd9Sstevel@tonic-gate 	uint8_t		qrv;
625f4b3ec61Sdh 	ip_stack_t	 *ipst;
626*e11c3f44Smeem 	ilm_walker_t	ilw;
6277c478bd9Sstevel@tonic-gate 
628f4b3ec61Sdh 	ipst = ill->ill_ipst;
6297c478bd9Sstevel@tonic-gate 	/* make sure numsrc matches packet size */
6307c478bd9Sstevel@tonic-gate 	numsrc = ntohs(igmp3qa->igmp3qa_numsrc);
6317c478bd9Sstevel@tonic-gate 	if (igmplen < IGMP_V3_QUERY_MINLEN + (numsrc * sizeof (ipaddr_t))) {
632f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
6337c478bd9Sstevel@tonic-gate 		return (0);
6347c478bd9Sstevel@tonic-gate 	}
6357c478bd9Sstevel@tonic-gate 	src_array = (ipaddr_t *)&igmp3qa[1];
6367c478bd9Sstevel@tonic-gate 
637f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_rcv_queries;
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	if ((mrd = (uint_t)igmp3qa->igmp3qa_mxrc) >= IGMP_V3_MAXRT_FPMIN) {
6407c478bd9Sstevel@tonic-gate 		uint_t hdrval, mant, exp;
6417c478bd9Sstevel@tonic-gate 		hdrval = (uint_t)igmp3qa->igmp3qa_mxrc;
6427c478bd9Sstevel@tonic-gate 		mant = hdrval & IGMP_V3_MAXRT_MANT_MASK;
6437c478bd9Sstevel@tonic-gate 		exp = (hdrval & IGMP_V3_MAXRT_EXP_MASK) >> 4;
6447c478bd9Sstevel@tonic-gate 		mrd = (mant | 0x10) << (exp + 3);
6457c478bd9Sstevel@tonic-gate 	}
6467c478bd9Sstevel@tonic-gate 	if (mrd == 0)
6477c478bd9Sstevel@tonic-gate 		mrd = MCAST_DEF_QUERY_RESP_INTERVAL;
6487c478bd9Sstevel@tonic-gate 	timer = DSEC_TO_MSEC(mrd);
6497c478bd9Sstevel@tonic-gate 	MCAST_RANDOM_DELAY(delay, timer);
6507c478bd9Sstevel@tonic-gate 	next = (unsigned)INFINITY;
6518dc47d9fSudpa 	current = CURRENT_MSTIME;
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 	if ((qrv = igmp3qa->igmp3qa_sqrv & IGMP_V3_RV_MASK) == 0)
6547c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = MCAST_DEF_ROBUSTNESS;
6557c478bd9Sstevel@tonic-gate 	else
6567c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = qrv;
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 	if ((qqi = (uint_t)igmp3qa->igmp3qa_qqic) >= IGMP_V3_QQI_FPMIN) {
6597c478bd9Sstevel@tonic-gate 		uint_t hdrval, mant, exp;
6607c478bd9Sstevel@tonic-gate 		hdrval = (uint_t)igmp3qa->igmp3qa_qqic;
6617c478bd9Sstevel@tonic-gate 		mant = hdrval & IGMP_V3_QQI_MANT_MASK;
6627c478bd9Sstevel@tonic-gate 		exp = (hdrval & IGMP_V3_QQI_EXP_MASK) >> 4;
6637c478bd9Sstevel@tonic-gate 		qqi = (mant | 0x10) << (exp + 3);
6647c478bd9Sstevel@tonic-gate 	}
6657c478bd9Sstevel@tonic-gate 	ill->ill_mcast_qi = (qqi == 0) ? MCAST_DEF_QUERY_INTERVAL : qqi;
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	/*
6687c478bd9Sstevel@tonic-gate 	 * If we have a pending general query response that's scheduled
6697c478bd9Sstevel@tonic-gate 	 * sooner than the delay we calculated for this response, then
6707c478bd9Sstevel@tonic-gate 	 * no action is required (RFC3376 section 5.2 rule 1)
6717c478bd9Sstevel@tonic-gate 	 */
6727c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
6738dc47d9fSudpa 	if (ill->ill_global_timer < (current + delay)) {
6747c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
6757c478bd9Sstevel@tonic-gate 		return (next);
6767c478bd9Sstevel@tonic-gate 	}
6777c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	/*
6807c478bd9Sstevel@tonic-gate 	 * Now take action depending upon query type:
6817c478bd9Sstevel@tonic-gate 	 * general, group specific, or group/source specific.
6827c478bd9Sstevel@tonic-gate 	 */
6837c478bd9Sstevel@tonic-gate 	if ((numsrc == 0) && (igmp3qa->igmp3qa_group == INADDR_ANY)) {
6847c478bd9Sstevel@tonic-gate 		/*
6857c478bd9Sstevel@tonic-gate 		 * general query
6867c478bd9Sstevel@tonic-gate 		 * We know global timer is either not running or is
6877c478bd9Sstevel@tonic-gate 		 * greater than our calculated delay, so reset it to
6887c478bd9Sstevel@tonic-gate 		 * our delay (random value in range [0, response time]).
6897c478bd9Sstevel@tonic-gate 		 */
6907c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
6918dc47d9fSudpa 		ill->ill_global_timer =  current + delay;
6927c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
6938dc47d9fSudpa 		next = delay;
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	} else {
6967c478bd9Sstevel@tonic-gate 		/* group or group/source specific query */
697*e11c3f44Smeem 		ilm = ilm_walker_start(&ilw, ill);
6987c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
699*e11c3f44Smeem 		for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
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 		mutex_exit(&ill->ill_lock);
754*e11c3f44Smeem 		ilm_walker_finish(&ilw);
7557c478bd9Sstevel@tonic-gate 	}
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	return (next);
7587c478bd9Sstevel@tonic-gate }
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate void
7617c478bd9Sstevel@tonic-gate igmp_joingroup(ilm_t *ilm)
7627c478bd9Sstevel@tonic-gate {
7638dc47d9fSudpa 	uint_t	timer;
7647c478bd9Sstevel@tonic-gate 	ill_t	*ill;
765f4b3ec61Sdh 	ip_stack_t	*ipst = ilm->ilm_ipst;
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	ill = ilm->ilm_ipif->ipif_ill;
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
7707c478bd9Sstevel@tonic-gate 	ASSERT(ilm->ilm_ill == NULL && !ilm->ilm_ipif->ipif_isv6);
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
7737c478bd9Sstevel@tonic-gate 	if (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP)) {
7747c478bd9Sstevel@tonic-gate 		ilm->ilm_rtx.rtx_timer = INFINITY;
7757c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_OTHERMEMBER;
7767c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
7777c478bd9Sstevel@tonic-gate 	} else {
7787c478bd9Sstevel@tonic-gate 		ip1dbg(("Querier mode %d, sending report, group %x\n",
7797c478bd9Sstevel@tonic-gate 		    ill->ill_mcast_type, htonl(ilm->ilm_addr)));
7807c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
7817c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
7827c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
7837c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
7847c478bd9Sstevel@tonic-gate 		} else if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
7857c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
7867c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
7877c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
7887c478bd9Sstevel@tonic-gate 		} else if (ill->ill_mcast_type == IGMP_V3_ROUTER) {
7897c478bd9Sstevel@tonic-gate 			mrec_t *rp;
7907c478bd9Sstevel@tonic-gate 			mcast_record_t rtype;
7917c478bd9Sstevel@tonic-gate 			/*
7927c478bd9Sstevel@tonic-gate 			 * The possible state changes we need to handle here:
7937c478bd9Sstevel@tonic-gate 			 *   Old State	New State	Report
7947c478bd9Sstevel@tonic-gate 			 *
7957c478bd9Sstevel@tonic-gate 			 *   INCLUDE(0)	INCLUDE(X)	ALLOW(X),BLOCK(0)
7967c478bd9Sstevel@tonic-gate 			 *   INCLUDE(0)	EXCLUDE(X)	TO_EX(X)
7977c478bd9Sstevel@tonic-gate 			 *
7987c478bd9Sstevel@tonic-gate 			 * No need to send the BLOCK(0) report; ALLOW(X)
7997c478bd9Sstevel@tonic-gate 			 * is enough.
8007c478bd9Sstevel@tonic-gate 			 */
8017c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
8027c478bd9Sstevel@tonic-gate 			    ALLOW_NEW_SOURCES : CHANGE_TO_EXCLUDE;
8037c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
8047c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
8057c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
8067c478bd9Sstevel@tonic-gate 			igmpv3_sendrpt(ilm->ilm_ipif, rp);
8077c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
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 */
8177c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
8187c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
8198dc47d9fSudpa 		timer = ilm->ilm_rtx.rtx_timer;
8208dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
8217c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
8227c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 		/*
825*e11c3f44Smeem 		 * We need to restart the IGMP timers, but we can't do it here
826*e11c3f44Smeem 		 * since we're inside the IPSQ and thus igmp_start_timers() ->
827*e11c3f44Smeem 		 * untimeout() (inside the IPSQ, waiting for a running timeout
828*e11c3f44Smeem 		 * to finish) could deadlock with igmp_timeout_handler() ->
829*e11c3f44Smeem 		 * ipsq_enter() (running the timeout, waiting to get inside
830*e11c3f44Smeem 		 * the IPSQ).  We also can't just delay it until after we
831*e11c3f44Smeem 		 * ipsq_exit() since we could be inside more than one IPSQ and
832*e11c3f44Smeem 		 * thus still have the other IPSQs pinned after we exit -- and
833*e11c3f44Smeem 		 * igmp_start_timers() may be trying to enter one of those.
834*e11c3f44Smeem 		 * Instead, signal a dedicated thread that will do it for us.
8357c478bd9Sstevel@tonic-gate 		 */
836f4b3ec61Sdh 		mutex_enter(&ipst->ips_igmp_timer_lock);
8378dc47d9fSudpa 		ipst->ips_igmp_deferred_next = MIN(timer,
838f4b3ec61Sdh 		    ipst->ips_igmp_deferred_next);
839f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
840*e11c3f44Smeem 		mcast_signal_restart_thread(ipst);
8417c478bd9Sstevel@tonic-gate 	}
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
8447c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ilm->ilm_ipif->ipif_ill->ill_rq, 1, SL_TRACE,
8457c478bd9Sstevel@tonic-gate 		    "igmp_joingroup: multicast_type %d timer %d",
8467c478bd9Sstevel@tonic-gate 		    (ilm->ilm_ipif->ipif_ill->ill_mcast_type),
8478dc47d9fSudpa 		    (int)ntohl(timer));
8487c478bd9Sstevel@tonic-gate 	}
8497c478bd9Sstevel@tonic-gate }
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate void
8527c478bd9Sstevel@tonic-gate mld_joingroup(ilm_t *ilm)
8537c478bd9Sstevel@tonic-gate {
8548dc47d9fSudpa 	uint_t	timer;
8557c478bd9Sstevel@tonic-gate 	ill_t	*ill;
856f4b3ec61Sdh 	ip_stack_t	*ipst = ilm->ilm_ipst;
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	ill = ilm->ilm_ill;
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
8617c478bd9Sstevel@tonic-gate 	ASSERT(ilm->ilm_ipif == NULL && ill->ill_isv6);
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
8647c478bd9Sstevel@tonic-gate 	if (IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr)) {
8657c478bd9Sstevel@tonic-gate 		ilm->ilm_rtx.rtx_timer = INFINITY;
8667c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_OTHERMEMBER;
8677c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
8687c478bd9Sstevel@tonic-gate 	} else {
8697c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
8707c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
8717c478bd9Sstevel@tonic-gate 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
8727c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
8737c478bd9Sstevel@tonic-gate 		} else {
8747c478bd9Sstevel@tonic-gate 			mrec_t *rp;
8757c478bd9Sstevel@tonic-gate 			mcast_record_t rtype;
8767c478bd9Sstevel@tonic-gate 			/*
8777c478bd9Sstevel@tonic-gate 			 * The possible state changes we need to handle here:
8787c478bd9Sstevel@tonic-gate 			 *	Old State   New State	Report
8797c478bd9Sstevel@tonic-gate 			 *
8807c478bd9Sstevel@tonic-gate 			 *	INCLUDE(0)  INCLUDE(X)	ALLOW(X),BLOCK(0)
8817c478bd9Sstevel@tonic-gate 			 *	INCLUDE(0)  EXCLUDE(X)	TO_EX(X)
8827c478bd9Sstevel@tonic-gate 			 *
8837c478bd9Sstevel@tonic-gate 			 * No need to send the BLOCK(0) report; ALLOW(X)
8847c478bd9Sstevel@tonic-gate 			 * is enough
8857c478bd9Sstevel@tonic-gate 			 */
8867c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
8877c478bd9Sstevel@tonic-gate 			    ALLOW_NEW_SOURCES : CHANGE_TO_EXCLUDE;
8887c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
8897c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
8907c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
8917c478bd9Sstevel@tonic-gate 			mldv2_sendrpt(ill, rp);
8927c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
8937c478bd9Sstevel@tonic-gate 			/*
8947c478bd9Sstevel@tonic-gate 			 * Set up retransmission state.  Timer is set below,
8957c478bd9Sstevel@tonic-gate 			 * for both v2 and v1.
8967c478bd9Sstevel@tonic-gate 			 */
8977c478bd9Sstevel@tonic-gate 			mcast_init_rtx(ill, &ilm->ilm_rtx, rtype,
8987c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter);
8997c478bd9Sstevel@tonic-gate 		}
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 		/* Set the ilm timer value */
9027c478bd9Sstevel@tonic-gate 		ASSERT(ill->ill_mcast_type != MLD_V2_ROUTER ||
9037c478bd9Sstevel@tonic-gate 		    ilm->ilm_rtx.rtx_cnt > 0);
9047c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
9057c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
9068dc47d9fSudpa 		timer = ilm->ilm_rtx.rtx_timer;
9078dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
9087c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
9097c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 		/*
912*e11c3f44Smeem 		 * Signal another thread to restart the timers.  See the
913*e11c3f44Smeem 		 * comment in igmp_joingroup() for details.
9147c478bd9Sstevel@tonic-gate 		 */
915f4b3ec61Sdh 		mutex_enter(&ipst->ips_mld_timer_lock);
9168dc47d9fSudpa 		ipst->ips_mld_deferred_next = MIN(timer,
917f4b3ec61Sdh 		    ipst->ips_mld_deferred_next);
918f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
919*e11c3f44Smeem 		mcast_signal_restart_thread(ipst);
9207c478bd9Sstevel@tonic-gate 	}
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
9237c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ilm->ilm_ill->ill_rq, 1, SL_TRACE,
9247c478bd9Sstevel@tonic-gate 		    "mld_joingroup: multicast_type %d timer %d",
9257c478bd9Sstevel@tonic-gate 		    (ilm->ilm_ill->ill_mcast_type),
9268dc47d9fSudpa 		    (int)ntohl(timer));
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate }
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate void
9317c478bd9Sstevel@tonic-gate igmp_leavegroup(ilm_t *ilm)
9327c478bd9Sstevel@tonic-gate {
9337c478bd9Sstevel@tonic-gate 	ill_t *ill = ilm->ilm_ipif->ipif_ill;
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	ASSERT(ilm->ilm_ill == NULL);
9367c478bd9Sstevel@tonic-gate 	ASSERT(!ill->ill_isv6);
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
9397c478bd9Sstevel@tonic-gate 	if (ilm->ilm_state == IGMP_IREPORTEDLAST &&
9407c478bd9Sstevel@tonic-gate 	    ill->ill_mcast_type == IGMP_V2_ROUTER &&
9417c478bd9Sstevel@tonic-gate 	    (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP))) {
9427c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
9437c478bd9Sstevel@tonic-gate 		igmp_sendpkt(ilm, IGMP_V2_LEAVE_GROUP,
9447c478bd9Sstevel@tonic-gate 		    (htonl(INADDR_ALLRTRS_GROUP)));
9457c478bd9Sstevel@tonic-gate 		return;
9467c478bd9Sstevel@tonic-gate 	} else if ((ill->ill_mcast_type == IGMP_V3_ROUTER) &&
9477c478bd9Sstevel@tonic-gate 	    (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP))) {
9487c478bd9Sstevel@tonic-gate 		mrec_t *rp;
9497c478bd9Sstevel@tonic-gate 		/*
9507c478bd9Sstevel@tonic-gate 		 * The possible state changes we need to handle here:
9517c478bd9Sstevel@tonic-gate 		 *	Old State	New State	Report
9527c478bd9Sstevel@tonic-gate 		 *
9537c478bd9Sstevel@tonic-gate 		 *	INCLUDE(X)	INCLUDE(0)	ALLOW(0),BLOCK(X)
9547c478bd9Sstevel@tonic-gate 		 *	EXCLUDE(X)	INCLUDE(0)	TO_IN(0)
9557c478bd9Sstevel@tonic-gate 		 *
9567c478bd9Sstevel@tonic-gate 		 * No need to send the ALLOW(0) report; BLOCK(X) is enough
9577c478bd9Sstevel@tonic-gate 		 */
9587c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
9597c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
9607c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
9617c478bd9Sstevel@tonic-gate 		} else {
9627c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr,
9637c478bd9Sstevel@tonic-gate 			    NULL, NULL);
9647c478bd9Sstevel@tonic-gate 		}
9657c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
9667c478bd9Sstevel@tonic-gate 		igmpv3_sendrpt(ilm->ilm_ipif, rp);
9677c478bd9Sstevel@tonic-gate 		return;
9687c478bd9Sstevel@tonic-gate 	}
9697c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
9707c478bd9Sstevel@tonic-gate }
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate void
9737c478bd9Sstevel@tonic-gate mld_leavegroup(ilm_t *ilm)
9747c478bd9Sstevel@tonic-gate {
9757c478bd9Sstevel@tonic-gate 	ill_t *ill = ilm->ilm_ill;
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 	ASSERT(ilm->ilm_ipif == NULL);
9787c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_isv6);
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
9817c478bd9Sstevel@tonic-gate 	if (ilm->ilm_state == IGMP_IREPORTEDLAST &&
9827c478bd9Sstevel@tonic-gate 	    ill->ill_mcast_type == MLD_V1_ROUTER &&
9837c478bd9Sstevel@tonic-gate 	    (!IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr))) {
9847c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
9857c478bd9Sstevel@tonic-gate 		mld_sendpkt(ilm, MLD_LISTENER_REDUCTION, &ipv6_all_rtrs_mcast);
9867c478bd9Sstevel@tonic-gate 		return;
9877c478bd9Sstevel@tonic-gate 	} else if ((ill->ill_mcast_type == MLD_V2_ROUTER) &&
9887c478bd9Sstevel@tonic-gate 	    (!IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr))) {
9897c478bd9Sstevel@tonic-gate 		mrec_t *rp;
9907c478bd9Sstevel@tonic-gate 		/*
9917c478bd9Sstevel@tonic-gate 		 * The possible state changes we need to handle here:
9927c478bd9Sstevel@tonic-gate 		 *	Old State	New State	Report
9937c478bd9Sstevel@tonic-gate 		 *
9947c478bd9Sstevel@tonic-gate 		 *	INCLUDE(X)	INCLUDE(0)	ALLOW(0),BLOCK(X)
9957c478bd9Sstevel@tonic-gate 		 *	EXCLUDE(X)	INCLUDE(0)	TO_IN(0)
9967c478bd9Sstevel@tonic-gate 		 *
9977c478bd9Sstevel@tonic-gate 		 * No need to send the ALLOW(0) report; BLOCK(X) is enough
9987c478bd9Sstevel@tonic-gate 		 */
9997c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
10007c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
10017c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, NULL);
10027c478bd9Sstevel@tonic-gate 		} else {
10037c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr,
10047c478bd9Sstevel@tonic-gate 			    NULL, NULL);
10057c478bd9Sstevel@tonic-gate 		}
10067c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
10077c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rp);
10087c478bd9Sstevel@tonic-gate 		return;
10097c478bd9Sstevel@tonic-gate 	}
10107c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
10117c478bd9Sstevel@tonic-gate }
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate void
10147c478bd9Sstevel@tonic-gate igmp_statechange(ilm_t *ilm, mcast_record_t fmode, slist_t *flist)
10157c478bd9Sstevel@tonic-gate {
10167c478bd9Sstevel@tonic-gate 	ill_t *ill;
10177c478bd9Sstevel@tonic-gate 	mrec_t *rp;
1018f4b3ec61Sdh 	ip_stack_t	*ipst = ilm->ilm_ipst;
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 	ASSERT(ilm != NULL);
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	/* state change reports should only be sent if the router is v3 */
10237c478bd9Sstevel@tonic-gate 	if (ilm->ilm_ipif->ipif_ill->ill_mcast_type != IGMP_V3_ROUTER)
10247c478bd9Sstevel@tonic-gate 		return;
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	if (ilm->ilm_ill == NULL) {
10277c478bd9Sstevel@tonic-gate 		ASSERT(ilm->ilm_ipif != NULL);
10287c478bd9Sstevel@tonic-gate 		ill = ilm->ilm_ipif->ipif_ill;
10297c478bd9Sstevel@tonic-gate 	} else {
10307c478bd9Sstevel@tonic-gate 		ill = ilm->ilm_ill;
10317c478bd9Sstevel@tonic-gate 	}
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate 	/*
10367c478bd9Sstevel@tonic-gate 	 * Compare existing(old) state with the new state and prepare
10377c478bd9Sstevel@tonic-gate 	 * State Change Report, according to the rules in RFC 3376:
10387c478bd9Sstevel@tonic-gate 	 *
10397c478bd9Sstevel@tonic-gate 	 *	Old State	New State	State Change Report
10407c478bd9Sstevel@tonic-gate 	 *
10417c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	INCLUDE(B)	ALLOW(B-A),BLOCK(A-B)
10427c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	EXCLUDE(B)	ALLOW(A-B),BLOCK(B-A)
10437c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	EXCLUDE(B)	TO_EX(B)
10447c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	INCLUDE(B)	TO_IN(B)
10457c478bd9Sstevel@tonic-gate 	 */
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate 	if (ilm->ilm_fmode == fmode) {
10487c478bd9Sstevel@tonic-gate 		slist_t	*a_minus_b = NULL, *b_minus_a = NULL;
10497c478bd9Sstevel@tonic-gate 		slist_t *allow, *block;
10507c478bd9Sstevel@tonic-gate 		if (((a_minus_b = l_alloc()) == NULL) ||
10517c478bd9Sstevel@tonic-gate 		    ((b_minus_a = l_alloc()) == NULL)) {
10527c478bd9Sstevel@tonic-gate 			l_free(a_minus_b);
10537c478bd9Sstevel@tonic-gate 			if (ilm->ilm_fmode == MODE_IS_INCLUDE)
10547c478bd9Sstevel@tonic-gate 				goto send_to_ex;
10557c478bd9Sstevel@tonic-gate 			else
10567c478bd9Sstevel@tonic-gate 				goto send_to_in;
10577c478bd9Sstevel@tonic-gate 		}
10587c478bd9Sstevel@tonic-gate 		l_difference(ilm->ilm_filter, flist, a_minus_b);
10597c478bd9Sstevel@tonic-gate 		l_difference(flist, ilm->ilm_filter, b_minus_a);
10607c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
10617c478bd9Sstevel@tonic-gate 			allow = b_minus_a;
10627c478bd9Sstevel@tonic-gate 			block = a_minus_b;
10637c478bd9Sstevel@tonic-gate 		} else {
10647c478bd9Sstevel@tonic-gate 			allow = a_minus_b;
10657c478bd9Sstevel@tonic-gate 			block = b_minus_a;
10667c478bd9Sstevel@tonic-gate 		}
10677c478bd9Sstevel@tonic-gate 		rp = NULL;
10687c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(allow))
10697c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(ALLOW_NEW_SOURCES, &ilm->ilm_v6addr,
10707c478bd9Sstevel@tonic-gate 			    allow, rp);
10717c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(block))
10727c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
10737c478bd9Sstevel@tonic-gate 			    block, rp);
10747c478bd9Sstevel@tonic-gate 		l_free(a_minus_b);
10757c478bd9Sstevel@tonic-gate 		l_free(b_minus_a);
10767c478bd9Sstevel@tonic-gate 	} else if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
10777c478bd9Sstevel@tonic-gate send_to_ex:
10787c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_EXCLUDE, &ilm->ilm_v6addr, flist,
10797c478bd9Sstevel@tonic-gate 		    NULL);
10807c478bd9Sstevel@tonic-gate 	} else {
10817c478bd9Sstevel@tonic-gate send_to_in:
10827c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr, flist,
10837c478bd9Sstevel@tonic-gate 		    NULL);
10847c478bd9Sstevel@tonic-gate 	}
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 	/*
10877c478bd9Sstevel@tonic-gate 	 * Need to set up retransmission state; merge the new info with the
10887c478bd9Sstevel@tonic-gate 	 * current state (which may be null).  If the timer is not currently
1089*e11c3f44Smeem 	 * running, signal a thread to restart it -- see the comment in
1090*e11c3f44Smeem 	 * igmp_joingroup() for details.
10917c478bd9Sstevel@tonic-gate 	 */
10927c478bd9Sstevel@tonic-gate 	rp = mcast_merge_rtx(ilm, rp, flist);
10937c478bd9Sstevel@tonic-gate 	if (ilm->ilm_rtx.rtx_timer == INFINITY) {
10947c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
10957c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
1096f4b3ec61Sdh 		mutex_enter(&ipst->ips_igmp_timer_lock);
1097f4b3ec61Sdh 		ipst->ips_igmp_deferred_next = MIN(ipst->ips_igmp_deferred_next,
10987c478bd9Sstevel@tonic-gate 		    ilm->ilm_rtx.rtx_timer);
10998dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
1100f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1101*e11c3f44Smeem 		mcast_signal_restart_thread(ipst);
11027c478bd9Sstevel@tonic-gate 	}
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
11057c478bd9Sstevel@tonic-gate 	igmpv3_sendrpt(ilm->ilm_ipif, rp);
11067c478bd9Sstevel@tonic-gate }
11077c478bd9Sstevel@tonic-gate 
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;
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	/* only need to send if we have an mldv2-capable router */
11207c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
11217c478bd9Sstevel@tonic-gate 	if (ill->ill_mcast_type != MLD_V2_ROUTER) {
11227c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
11237c478bd9Sstevel@tonic-gate 		return;
11247c478bd9Sstevel@tonic-gate 	}
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate 	/*
11277c478bd9Sstevel@tonic-gate 	 * Compare existing (old) state with the new state passed in
11287c478bd9Sstevel@tonic-gate 	 * and send appropriate MLDv2 State Change Report.
11297c478bd9Sstevel@tonic-gate 	 *
11307c478bd9Sstevel@tonic-gate 	 *	Old State	New State	State Change Report
11317c478bd9Sstevel@tonic-gate 	 *
11327c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	INCLUDE(B)	ALLOW(B-A),BLOCK(A-B)
11337c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	EXCLUDE(B)	ALLOW(A-B),BLOCK(B-A)
11347c478bd9Sstevel@tonic-gate 	 *	INCLUDE(A)	EXCLUDE(B)	TO_EX(B)
11357c478bd9Sstevel@tonic-gate 	 *	EXCLUDE(A)	INCLUDE(B)	TO_IN(B)
11367c478bd9Sstevel@tonic-gate 	 */
11377c478bd9Sstevel@tonic-gate 	if (ilm->ilm_fmode == fmode) {
11387c478bd9Sstevel@tonic-gate 		slist_t	*a_minus_b = NULL, *b_minus_a = NULL;
11397c478bd9Sstevel@tonic-gate 		slist_t *allow, *block;
11407c478bd9Sstevel@tonic-gate 		if (((a_minus_b = l_alloc()) == NULL) ||
11417c478bd9Sstevel@tonic-gate 		    ((b_minus_a = l_alloc()) == NULL)) {
11427c478bd9Sstevel@tonic-gate 			l_free(a_minus_b);
11437c478bd9Sstevel@tonic-gate 			if (ilm->ilm_fmode == MODE_IS_INCLUDE)
11447c478bd9Sstevel@tonic-gate 				goto send_to_ex;
11457c478bd9Sstevel@tonic-gate 			else
11467c478bd9Sstevel@tonic-gate 				goto send_to_in;
11477c478bd9Sstevel@tonic-gate 		}
11487c478bd9Sstevel@tonic-gate 		l_difference(ilm->ilm_filter, flist, a_minus_b);
11497c478bd9Sstevel@tonic-gate 		l_difference(flist, ilm->ilm_filter, b_minus_a);
11507c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
11517c478bd9Sstevel@tonic-gate 			allow = b_minus_a;
11527c478bd9Sstevel@tonic-gate 			block = a_minus_b;
11537c478bd9Sstevel@tonic-gate 		} else {
11547c478bd9Sstevel@tonic-gate 			allow = a_minus_b;
11557c478bd9Sstevel@tonic-gate 			block = b_minus_a;
11567c478bd9Sstevel@tonic-gate 		}
11577c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(allow))
11587c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(ALLOW_NEW_SOURCES, &ilm->ilm_v6addr,
11597c478bd9Sstevel@tonic-gate 			    allow, rp);
11607c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(block))
11617c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
11627c478bd9Sstevel@tonic-gate 			    block, rp);
11637c478bd9Sstevel@tonic-gate 		l_free(a_minus_b);
11647c478bd9Sstevel@tonic-gate 		l_free(b_minus_a);
11657c478bd9Sstevel@tonic-gate 	} else if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
11667c478bd9Sstevel@tonic-gate send_to_ex:
11677c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_EXCLUDE, &ilm->ilm_v6addr, flist,
11687c478bd9Sstevel@tonic-gate 		    NULL);
11697c478bd9Sstevel@tonic-gate 	} else {
11707c478bd9Sstevel@tonic-gate send_to_in:
11717c478bd9Sstevel@tonic-gate 		rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr, flist,
11727c478bd9Sstevel@tonic-gate 		    NULL);
11737c478bd9Sstevel@tonic-gate 	}
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 	/*
11767c478bd9Sstevel@tonic-gate 	 * Need to set up retransmission state; merge the new info with the
11777c478bd9Sstevel@tonic-gate 	 * current state (which may be null).  If the timer is not currently
1178*e11c3f44Smeem 	 * running, signal a thread to restart it -- see the comment in
1179*e11c3f44Smeem 	 * igmp_joingroup() for details.
11807c478bd9Sstevel@tonic-gate 	 */
11817c478bd9Sstevel@tonic-gate 	rp = mcast_merge_rtx(ilm, rp, flist);
11827c478bd9Sstevel@tonic-gate 	ASSERT(ilm->ilm_rtx.rtx_cnt > 0);
11837c478bd9Sstevel@tonic-gate 	if (ilm->ilm_rtx.rtx_timer == INFINITY) {
11847c478bd9Sstevel@tonic-gate 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
11857c478bd9Sstevel@tonic-gate 		    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
1186f4b3ec61Sdh 		mutex_enter(&ipst->ips_mld_timer_lock);
1187f4b3ec61Sdh 		ipst->ips_mld_deferred_next =
1188f4b3ec61Sdh 		    MIN(ipst->ips_mld_deferred_next, ilm->ilm_rtx.rtx_timer);
11898dc47d9fSudpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
1190f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
1191*e11c3f44Smeem 		mcast_signal_restart_thread(ipst);
11927c478bd9Sstevel@tonic-gate 	}
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
11957c478bd9Sstevel@tonic-gate 	mldv2_sendrpt(ill, rp);
11967c478bd9Sstevel@tonic-gate }
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate uint_t
11998dc47d9fSudpa igmp_timeout_handler_per_ill(ill_t *ill)
12007c478bd9Sstevel@tonic-gate {
12018dc47d9fSudpa 	uint_t	next = INFINITY, current;
12027c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
12037c478bd9Sstevel@tonic-gate 	ipif_t	*ipif;
12047c478bd9Sstevel@tonic-gate 	mrec_t	*rp = NULL;
12057c478bd9Sstevel@tonic-gate 	mrec_t	*rtxrp = NULL;
12067c478bd9Sstevel@tonic-gate 	rtx_state_t *rtxp;
12077c478bd9Sstevel@tonic-gate 	mcast_record_t	rtype;
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
12107c478bd9Sstevel@tonic-gate 
12117c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
12127c478bd9Sstevel@tonic-gate 
12138dc47d9fSudpa 	current = CURRENT_MSTIME;
12147c478bd9Sstevel@tonic-gate 	/* First check the global timer on this interface */
12157c478bd9Sstevel@tonic-gate 	if (ill->ill_global_timer == INFINITY)
12167c478bd9Sstevel@tonic-gate 		goto per_ilm_timer;
12178dc47d9fSudpa 	if (ill->ill_global_timer <= (current + CURRENT_OFFSET)) {
12187c478bd9Sstevel@tonic-gate 		ill->ill_global_timer = INFINITY;
12197c478bd9Sstevel@tonic-gate 		/*
12207c478bd9Sstevel@tonic-gate 		 * Send report for each group on this interface.
12217c478bd9Sstevel@tonic-gate 		 * Since we just set the global timer (received a v3 general
12227c478bd9Sstevel@tonic-gate 		 * query), need to skip the all hosts addr (224.0.0.1), per
12237c478bd9Sstevel@tonic-gate 		 * RFC 3376 section 5.
12247c478bd9Sstevel@tonic-gate 		 */
12257c478bd9Sstevel@tonic-gate 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
12267c478bd9Sstevel@tonic-gate 			if (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP))
12277c478bd9Sstevel@tonic-gate 				continue;
12287c478bd9Sstevel@tonic-gate 			ASSERT(ilm->ilm_ipif != NULL);
12297c478bd9Sstevel@tonic-gate 			ilm->ilm_ipif->ipif_igmp_rpt =
12307c478bd9Sstevel@tonic-gate 			    mcast_bldmrec(ilm->ilm_fmode, &ilm->ilm_v6addr,
12317c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, ilm->ilm_ipif->ipif_igmp_rpt);
12327c478bd9Sstevel@tonic-gate 			/*
12337c478bd9Sstevel@tonic-gate 			 * Since we're sending a report on this group, okay
12347c478bd9Sstevel@tonic-gate 			 * to delete pending group-specific timers.  Note
12357c478bd9Sstevel@tonic-gate 			 * that group-specific retransmit timers still need
12367c478bd9Sstevel@tonic-gate 			 * to be checked in the per_ilm_timer for-loop.
12377c478bd9Sstevel@tonic-gate 			 */
12387c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = INFINITY;
12397c478bd9Sstevel@tonic-gate 			ilm->ilm_state = IGMP_IREPORTEDLAST;
12407c478bd9Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_pendsrcs);
12417c478bd9Sstevel@tonic-gate 			ilm->ilm_pendsrcs = NULL;
12427c478bd9Sstevel@tonic-gate 		}
12437c478bd9Sstevel@tonic-gate 		/*
12447c478bd9Sstevel@tonic-gate 		 * We've built per-ipif mrec lists; walk the ill's ipif list
12457c478bd9Sstevel@tonic-gate 		 * and send a report for each ipif that has an mrec list.
12467c478bd9Sstevel@tonic-gate 		 */
12477c478bd9Sstevel@tonic-gate 		for (ipif = ill->ill_ipif; ipif != NULL;
12487c478bd9Sstevel@tonic-gate 		    ipif = ipif->ipif_next) {
12497c478bd9Sstevel@tonic-gate 			if (ipif->ipif_igmp_rpt == NULL)
12507c478bd9Sstevel@tonic-gate 				continue;
12517c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
12527c478bd9Sstevel@tonic-gate 			igmpv3_sendrpt(ipif, ipif->ipif_igmp_rpt);
12537c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
12547c478bd9Sstevel@tonic-gate 			/* mrec list was freed by igmpv3_sendrpt() */
12557c478bd9Sstevel@tonic-gate 			ipif->ipif_igmp_rpt = NULL;
12567c478bd9Sstevel@tonic-gate 		}
12577c478bd9Sstevel@tonic-gate 	} else {
12588dc47d9fSudpa 		if ((ill->ill_global_timer - current) < next)
12598dc47d9fSudpa 			next = ill->ill_global_timer - current;
12607c478bd9Sstevel@tonic-gate 	}
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate per_ilm_timer:
12637c478bd9Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
12647c478bd9Sstevel@tonic-gate 		if (ilm->ilm_timer == INFINITY)
12657c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
12667c478bd9Sstevel@tonic-gate 
12678dc47d9fSudpa 		if (ilm->ilm_timer > (current + CURRENT_OFFSET)) {
12688dc47d9fSudpa 			if ((ilm->ilm_timer - current) < next)
12698dc47d9fSudpa 				next = ilm->ilm_timer - current;
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 			if (ip_debug > 1) {
12727c478bd9Sstevel@tonic-gate 				(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
12738dc47d9fSudpa 				    "igmp_timo_hlr 2: ilm_timr %d "
12747c478bd9Sstevel@tonic-gate 				    "typ %d nxt %d",
12758dc47d9fSudpa 				    (int)ntohl(ilm->ilm_timer - current),
12767c478bd9Sstevel@tonic-gate 				    (ill->ill_mcast_type), next);
12777c478bd9Sstevel@tonic-gate 			}
12787c478bd9Sstevel@tonic-gate 
12797c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
12807c478bd9Sstevel@tonic-gate 		}
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 		/* the timer has expired, need to take action */
12837c478bd9Sstevel@tonic-gate 		ilm->ilm_timer = INFINITY;
12847c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
12857c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
12867c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
12877c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
12887c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
12897c478bd9Sstevel@tonic-gate 		} else if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
12907c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
12917c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
12927c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
12937c478bd9Sstevel@tonic-gate 		} else {
12947c478bd9Sstevel@tonic-gate 			slist_t *rsp;
12957c478bd9Sstevel@tonic-gate 			if (!SLIST_IS_EMPTY(ilm->ilm_pendsrcs) &&
12967c478bd9Sstevel@tonic-gate 			    (rsp = l_alloc()) != NULL) {
12977c478bd9Sstevel@tonic-gate 				/*
12987c478bd9Sstevel@tonic-gate 				 * Contents of reply depend on pending
12997c478bd9Sstevel@tonic-gate 				 * requested source list.
13007c478bd9Sstevel@tonic-gate 				 */
13017c478bd9Sstevel@tonic-gate 				if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
13027c478bd9Sstevel@tonic-gate 					l_intersection(ilm->ilm_filter,
13037c478bd9Sstevel@tonic-gate 					    ilm->ilm_pendsrcs, rsp);
13047c478bd9Sstevel@tonic-gate 				} else {
13057c478bd9Sstevel@tonic-gate 					l_difference(ilm->ilm_pendsrcs,
13067c478bd9Sstevel@tonic-gate 					    ilm->ilm_filter, rsp);
13077c478bd9Sstevel@tonic-gate 				}
13087c478bd9Sstevel@tonic-gate 				FREE_SLIST(ilm->ilm_pendsrcs);
13097c478bd9Sstevel@tonic-gate 				ilm->ilm_pendsrcs = NULL;
13107c478bd9Sstevel@tonic-gate 				if (!SLIST_IS_EMPTY(rsp))
13117c478bd9Sstevel@tonic-gate 					rp = mcast_bldmrec(MODE_IS_INCLUDE,
13127c478bd9Sstevel@tonic-gate 					    &ilm->ilm_v6addr, rsp, rp);
13137c478bd9Sstevel@tonic-gate 				FREE_SLIST(rsp);
13147c478bd9Sstevel@tonic-gate 			} else {
13157c478bd9Sstevel@tonic-gate 				/*
13167c478bd9Sstevel@tonic-gate 				 * Either the pending request is just group-
13177c478bd9Sstevel@tonic-gate 				 * specific, or we couldn't get the resources
13187c478bd9Sstevel@tonic-gate 				 * (rsp) to build a source-specific reply.
13197c478bd9Sstevel@tonic-gate 				 */
13207c478bd9Sstevel@tonic-gate 				rp = mcast_bldmrec(ilm->ilm_fmode,
13217c478bd9Sstevel@tonic-gate 				    &ilm->ilm_v6addr, ilm->ilm_filter, rp);
13227c478bd9Sstevel@tonic-gate 			}
13237c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
13247c478bd9Sstevel@tonic-gate 			igmpv3_sendrpt(ill->ill_ipif, rp);
13257c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
13267c478bd9Sstevel@tonic-gate 			rp = NULL;
13277c478bd9Sstevel@tonic-gate 		}
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate per_ilm_rtxtimer:
13307c478bd9Sstevel@tonic-gate 		rtxp = &ilm->ilm_rtx;
13317c478bd9Sstevel@tonic-gate 
13327c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_timer == INFINITY)
13337c478bd9Sstevel@tonic-gate 			continue;
13348dc47d9fSudpa 		if (rtxp->rtx_timer > (current + CURRENT_OFFSET)) {
13358dc47d9fSudpa 			if ((rtxp->rtx_timer - current) < next)
13368dc47d9fSudpa 				next = rtxp->rtx_timer - current;
13377c478bd9Sstevel@tonic-gate 			continue;
13387c478bd9Sstevel@tonic-gate 		}
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate 		rtxp->rtx_timer = INFINITY;
13417c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
13427c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
13437c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
13447c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
13457c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
13467c478bd9Sstevel@tonic-gate 			continue;
13477c478bd9Sstevel@tonic-gate 		} else if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
13487c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
13497c478bd9Sstevel@tonic-gate 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
13507c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
13517c478bd9Sstevel@tonic-gate 			continue;
13527c478bd9Sstevel@tonic-gate 		}
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 		/*
13557c478bd9Sstevel@tonic-gate 		 * The retransmit timer has popped, and our router is
13567c478bd9Sstevel@tonic-gate 		 * IGMPv3.  We have to delve into the retransmit state
13577c478bd9Sstevel@tonic-gate 		 * stored in the ilm.
13587c478bd9Sstevel@tonic-gate 		 *
13597c478bd9Sstevel@tonic-gate 		 * Decrement the retransmit count.  If the fmode rtx
13607c478bd9Sstevel@tonic-gate 		 * count is active, decrement it, and send a filter
13617c478bd9Sstevel@tonic-gate 		 * mode change report with the ilm's source list.
13627c478bd9Sstevel@tonic-gate 		 * Otherwise, send a source list change report with
13637c478bd9Sstevel@tonic-gate 		 * the current retransmit lists.
13647c478bd9Sstevel@tonic-gate 		 */
13657c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt > 0);
13667c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt >= rtxp->rtx_fmode_cnt);
13677c478bd9Sstevel@tonic-gate 		rtxp->rtx_cnt--;
13687c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_fmode_cnt > 0) {
13697c478bd9Sstevel@tonic-gate 			rtxp->rtx_fmode_cnt--;
13707c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
13717c478bd9Sstevel@tonic-gate 			    CHANGE_TO_INCLUDE : CHANGE_TO_EXCLUDE;
13727c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
13737c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, rtxrp);
13747c478bd9Sstevel@tonic-gate 		} else {
13757c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(ALLOW_NEW_SOURCES,
13767c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_allow, rtxrp);
13777c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(BLOCK_OLD_SOURCES,
13787c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_block, rtxrp);
13797c478bd9Sstevel@tonic-gate 		}
13807c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_cnt > 0) {
13817c478bd9Sstevel@tonic-gate 			MCAST_RANDOM_DELAY(rtxp->rtx_timer,
13827c478bd9Sstevel@tonic-gate 			    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
13837c478bd9Sstevel@tonic-gate 			if (rtxp->rtx_timer < next)
13847c478bd9Sstevel@tonic-gate 				next = rtxp->rtx_timer;
13858dc47d9fSudpa 			rtxp->rtx_timer += current;
13867c478bd9Sstevel@tonic-gate 		} else {
13877c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_allow);
13887c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_block);
13897c478bd9Sstevel@tonic-gate 		}
13907c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
13917c478bd9Sstevel@tonic-gate 		igmpv3_sendrpt(ilm->ilm_ipif, rtxrp);
13927c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
13937c478bd9Sstevel@tonic-gate 		rtxrp = NULL;
13947c478bd9Sstevel@tonic-gate 	}
13957c478bd9Sstevel@tonic-gate 
13967c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate 	return (next);
13997c478bd9Sstevel@tonic-gate }
14007c478bd9Sstevel@tonic-gate 
14017c478bd9Sstevel@tonic-gate /*
14027c478bd9Sstevel@tonic-gate  * igmp_timeout_handler:
14037c478bd9Sstevel@tonic-gate  * Called when there are timeout events, every next * TMEOUT_INTERVAL (tick).
14047c478bd9Sstevel@tonic-gate  * Returns number of ticks to next event (or 0 if none).
14057c478bd9Sstevel@tonic-gate  *
14067c478bd9Sstevel@tonic-gate  * As part of multicast join and leave igmp we may need to send out an
14077c478bd9Sstevel@tonic-gate  * igmp request. The igmp related state variables in the ilm are protected
14087c478bd9Sstevel@tonic-gate  * by ill_lock. A single global igmp timer is used to track igmp timeouts.
14097c478bd9Sstevel@tonic-gate  * igmp_timer_lock protects the global igmp_timeout_id. igmp_start_timers
14107c478bd9Sstevel@tonic-gate  * starts the igmp timer if needed. It serializes multiple threads trying to
14117c478bd9Sstevel@tonic-gate  * simultaneously start the timer using the igmp_timer_setter_active flag.
14127c478bd9Sstevel@tonic-gate  *
14137c478bd9Sstevel@tonic-gate  * igmp_input() receives igmp queries and responds to the queries
14147c478bd9Sstevel@tonic-gate  * in a delayed fashion by posting a timer i.e. it calls igmp_start_timers().
1415*e11c3f44Smeem  * Later the igmp_timer fires, the timeout handler igmp_timeout_handler()
14167c478bd9Sstevel@tonic-gate  * performs the action exclusively after entering each ill's ipsq as writer.
1417*e11c3f44Smeem  * (The need to enter the IPSQ is largely historical but there are still some
1418*e11c3f44Smeem  * fields like ilm_filter that rely on it.)
14197c478bd9Sstevel@tonic-gate  *
14207c478bd9Sstevel@tonic-gate  * The igmp_slowtimeo() function is called thru another timer.
14217c478bd9Sstevel@tonic-gate  * igmp_slowtimeout_lock protects the igmp_slowtimeout_id
14227c478bd9Sstevel@tonic-gate  */
14237c478bd9Sstevel@tonic-gate void
14247c478bd9Sstevel@tonic-gate igmp_timeout_handler(void *arg)
14257c478bd9Sstevel@tonic-gate {
14267c478bd9Sstevel@tonic-gate 	ill_t	*ill;
14277c478bd9Sstevel@tonic-gate 	uint_t  global_next = INFINITY;
14287c478bd9Sstevel@tonic-gate 	uint_t  next;
14297c478bd9Sstevel@tonic-gate 	ill_walk_context_t ctx;
14307c478bd9Sstevel@tonic-gate 	boolean_t success;
1431328c7d1fSmeem 	ip_stack_t *ipst = arg;
1432f4b3ec61Sdh 
1433f4b3ec61Sdh 	ASSERT(arg != NULL);
1434f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1435f4b3ec61Sdh 	ASSERT(ipst->ips_igmp_timeout_id != 0);
14368dc47d9fSudpa 	ipst->ips_igmp_timer_scheduled_last = 0;
1437f4b3ec61Sdh 	ipst->ips_igmp_time_to_next = 0;
1438f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
1439f4b3ec61Sdh 
1440f4b3ec61Sdh 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
1441f4b3ec61Sdh 	ill = ILL_START_WALK_V4(&ctx, ipst);
14427c478bd9Sstevel@tonic-gate 	for (; ill != NULL; ill = ill_next(&ctx, ill)) {
14437c478bd9Sstevel@tonic-gate 		ASSERT(!ill->ill_isv6);
14447c478bd9Sstevel@tonic-gate 		/*
14457c478bd9Sstevel@tonic-gate 		 * We may not be able to refhold the ill if the ill/ipif
14467c478bd9Sstevel@tonic-gate 		 * is changing. But we need to make sure that the ill will
14477c478bd9Sstevel@tonic-gate 		 * not vanish. So we just bump up the ill_waiter count.
14487c478bd9Sstevel@tonic-gate 		 */
14497c478bd9Sstevel@tonic-gate 		if (!ill_waiter_inc(ill))
14507c478bd9Sstevel@tonic-gate 			continue;
1451f4b3ec61Sdh 		rw_exit(&ipst->ips_ill_g_lock);
1452da14cebeSEric Cheng 		success = ipsq_enter(ill, B_TRUE, NEW_OP);
14537c478bd9Sstevel@tonic-gate 		if (success) {
14548dc47d9fSudpa 			next = igmp_timeout_handler_per_ill(ill);
14557c478bd9Sstevel@tonic-gate 			if (next < global_next)
14567c478bd9Sstevel@tonic-gate 				global_next = next;
1457328c7d1fSmeem 			ipsq_exit(ill->ill_phyint->phyint_ipsq);
14587c478bd9Sstevel@tonic-gate 		}
1459f4b3ec61Sdh 		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
14607c478bd9Sstevel@tonic-gate 		ill_waiter_dcr(ill);
14617c478bd9Sstevel@tonic-gate 	}
1462f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
14637c478bd9Sstevel@tonic-gate 
1464f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1465f4b3ec61Sdh 	ASSERT(ipst->ips_igmp_timeout_id != 0);
1466f4b3ec61Sdh 	ipst->ips_igmp_timeout_id = 0;
1467f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	if (global_next != INFINITY)
1470f4b3ec61Sdh 		igmp_start_timers(global_next, ipst);
14717c478bd9Sstevel@tonic-gate }
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate /*
14747c478bd9Sstevel@tonic-gate  * mld_timeout_handler:
14757c478bd9Sstevel@tonic-gate  * Called when there are timeout events, every next (tick).
14767c478bd9Sstevel@tonic-gate  * Returns number of ticks to next event (or 0 if none).
14777c478bd9Sstevel@tonic-gate  */
14787c478bd9Sstevel@tonic-gate /* ARGSUSED */
14797c478bd9Sstevel@tonic-gate uint_t
14808dc47d9fSudpa mld_timeout_handler_per_ill(ill_t *ill)
14817c478bd9Sstevel@tonic-gate {
14827c478bd9Sstevel@tonic-gate 	ilm_t 	*ilm;
14838dc47d9fSudpa 	uint_t	next = INFINITY, current;
14847c478bd9Sstevel@tonic-gate 	mrec_t	*rp, *rtxrp;
14857c478bd9Sstevel@tonic-gate 	rtx_state_t *rtxp;
14867c478bd9Sstevel@tonic-gate 	mcast_record_t	rtype;
14877c478bd9Sstevel@tonic-gate 
14887c478bd9Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
14897c478bd9Sstevel@tonic-gate 
14907c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
14917c478bd9Sstevel@tonic-gate 
14928dc47d9fSudpa 	current = CURRENT_MSTIME;
14937c478bd9Sstevel@tonic-gate 	/*
14947c478bd9Sstevel@tonic-gate 	 * First check the global timer on this interface; the global timer
14957c478bd9Sstevel@tonic-gate 	 * is not used for MLDv1, so if it's set we can assume we're v2.
14967c478bd9Sstevel@tonic-gate 	 */
14977c478bd9Sstevel@tonic-gate 	if (ill->ill_global_timer == INFINITY)
14987c478bd9Sstevel@tonic-gate 		goto per_ilm_timer;
14998dc47d9fSudpa 	if (ill->ill_global_timer <= (current + CURRENT_OFFSET)) {
15007c478bd9Sstevel@tonic-gate 		ill->ill_global_timer = INFINITY;
15017c478bd9Sstevel@tonic-gate 		/*
15027c478bd9Sstevel@tonic-gate 		 * Send report for each group on this interface.
15037c478bd9Sstevel@tonic-gate 		 * Since we just set the global timer (received a v2 general
15047c478bd9Sstevel@tonic-gate 		 * query), need to skip the all hosts addr (ff02::1), per
15057c478bd9Sstevel@tonic-gate 		 * RFC 3810 section 6.
15067c478bd9Sstevel@tonic-gate 		 */
15077c478bd9Sstevel@tonic-gate 		rp = NULL;
15087c478bd9Sstevel@tonic-gate 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
15097c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr,
15107c478bd9Sstevel@tonic-gate 			    &ipv6_all_hosts_mcast))
15117c478bd9Sstevel@tonic-gate 				continue;
15127c478bd9Sstevel@tonic-gate 			rp = mcast_bldmrec(ilm->ilm_fmode, &ilm->ilm_v6addr,
15137c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, rp);
15147c478bd9Sstevel@tonic-gate 			/*
15157c478bd9Sstevel@tonic-gate 			 * Since we're sending a report on this group, okay
15167c478bd9Sstevel@tonic-gate 			 * to delete pending group-specific timers.  Note
15177c478bd9Sstevel@tonic-gate 			 * that group-specific retransmit timers still need
15187c478bd9Sstevel@tonic-gate 			 * to be checked in the per_ilm_timer for-loop.
15197c478bd9Sstevel@tonic-gate 			 */
15207c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = INFINITY;
15217c478bd9Sstevel@tonic-gate 			ilm->ilm_state = IGMP_IREPORTEDLAST;
15227c478bd9Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_pendsrcs);
15237c478bd9Sstevel@tonic-gate 			ilm->ilm_pendsrcs = NULL;
15247c478bd9Sstevel@tonic-gate 		}
15257c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
15267c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rp);
15277c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
15287c478bd9Sstevel@tonic-gate 	} else {
15298dc47d9fSudpa 		if ((ill->ill_global_timer - current) < next)
15308dc47d9fSudpa 			next = ill->ill_global_timer - current;
15317c478bd9Sstevel@tonic-gate 	}
15327c478bd9Sstevel@tonic-gate 
15337c478bd9Sstevel@tonic-gate per_ilm_timer:
15347c478bd9Sstevel@tonic-gate 	rp = rtxrp = NULL;
15357c478bd9Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
15367c478bd9Sstevel@tonic-gate 		if (ilm->ilm_timer == INFINITY)
15377c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
15387c478bd9Sstevel@tonic-gate 
15398dc47d9fSudpa 		if (ilm->ilm_timer > (current + CURRENT_OFFSET)) {
15408dc47d9fSudpa 			if ((ilm->ilm_timer - current) < next)
15418dc47d9fSudpa 				next = ilm->ilm_timer - current;
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 			if (ip_debug > 1) {
15447c478bd9Sstevel@tonic-gate 				(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
15457c478bd9Sstevel@tonic-gate 				    "igmp_timo_hlr 2: ilm_timr"
15468dc47d9fSudpa 				    " %d typ %d nxt %d",
15478dc47d9fSudpa 				    (int)ntohl(ilm->ilm_timer - current),
15487c478bd9Sstevel@tonic-gate 				    (ill->ill_mcast_type), next);
15497c478bd9Sstevel@tonic-gate 			}
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 			goto per_ilm_rtxtimer;
15527c478bd9Sstevel@tonic-gate 		}
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 		/* the timer has expired, need to take action */
15557c478bd9Sstevel@tonic-gate 		ilm->ilm_timer = INFINITY;
15567c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
15577c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
15587c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
15597c478bd9Sstevel@tonic-gate 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
15607c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
15617c478bd9Sstevel@tonic-gate 		} else {
15627c478bd9Sstevel@tonic-gate 			slist_t *rsp;
15637c478bd9Sstevel@tonic-gate 			if (!SLIST_IS_EMPTY(ilm->ilm_pendsrcs) &&
15647c478bd9Sstevel@tonic-gate 			    (rsp = l_alloc()) != NULL) {
15657c478bd9Sstevel@tonic-gate 				/*
15667c478bd9Sstevel@tonic-gate 				 * Contents of reply depend on pending
15677c478bd9Sstevel@tonic-gate 				 * requested source list.
15687c478bd9Sstevel@tonic-gate 				 */
15697c478bd9Sstevel@tonic-gate 				if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
15707c478bd9Sstevel@tonic-gate 					l_intersection(ilm->ilm_filter,
15717c478bd9Sstevel@tonic-gate 					    ilm->ilm_pendsrcs, rsp);
15727c478bd9Sstevel@tonic-gate 				} else {
15737c478bd9Sstevel@tonic-gate 					l_difference(ilm->ilm_pendsrcs,
15747c478bd9Sstevel@tonic-gate 					    ilm->ilm_filter, rsp);
15757c478bd9Sstevel@tonic-gate 				}
15767c478bd9Sstevel@tonic-gate 				FREE_SLIST(ilm->ilm_pendsrcs);
15777c478bd9Sstevel@tonic-gate 				ilm->ilm_pendsrcs = NULL;
15787c478bd9Sstevel@tonic-gate 				if (!SLIST_IS_EMPTY(rsp))
15797c478bd9Sstevel@tonic-gate 					rp = mcast_bldmrec(MODE_IS_INCLUDE,
15807c478bd9Sstevel@tonic-gate 					    &ilm->ilm_v6addr, rsp, rp);
15817c478bd9Sstevel@tonic-gate 				FREE_SLIST(rsp);
15827c478bd9Sstevel@tonic-gate 			} else {
15837c478bd9Sstevel@tonic-gate 				rp = mcast_bldmrec(ilm->ilm_fmode,
15847c478bd9Sstevel@tonic-gate 				    &ilm->ilm_v6addr, ilm->ilm_filter, rp);
15857c478bd9Sstevel@tonic-gate 			}
15867c478bd9Sstevel@tonic-gate 		}
15877c478bd9Sstevel@tonic-gate 
15887c478bd9Sstevel@tonic-gate per_ilm_rtxtimer:
15897c478bd9Sstevel@tonic-gate 		rtxp = &ilm->ilm_rtx;
15907c478bd9Sstevel@tonic-gate 
15917c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_timer == INFINITY)
15927c478bd9Sstevel@tonic-gate 			continue;
15938dc47d9fSudpa 		if (rtxp->rtx_timer > (current + CURRENT_OFFSET)) {
15948dc47d9fSudpa 			if ((rtxp->rtx_timer - current) < next)
15958dc47d9fSudpa 				next = rtxp->rtx_timer - current;
15967c478bd9Sstevel@tonic-gate 			continue;
15977c478bd9Sstevel@tonic-gate 		}
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 		rtxp->rtx_timer = INFINITY;
16007c478bd9Sstevel@tonic-gate 		ilm->ilm_state = IGMP_IREPORTEDLAST;
16017c478bd9Sstevel@tonic-gate 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
16027c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
16037c478bd9Sstevel@tonic-gate 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
16047c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
16057c478bd9Sstevel@tonic-gate 			continue;
16067c478bd9Sstevel@tonic-gate 		}
16077c478bd9Sstevel@tonic-gate 
16087c478bd9Sstevel@tonic-gate 		/*
16097c478bd9Sstevel@tonic-gate 		 * The retransmit timer has popped, and our router is
16107c478bd9Sstevel@tonic-gate 		 * MLDv2.  We have to delve into the retransmit state
16117c478bd9Sstevel@tonic-gate 		 * stored in the ilm.
16127c478bd9Sstevel@tonic-gate 		 *
16137c478bd9Sstevel@tonic-gate 		 * Decrement the retransmit count.  If the fmode rtx
16147c478bd9Sstevel@tonic-gate 		 * count is active, decrement it, and send a filter
16157c478bd9Sstevel@tonic-gate 		 * mode change report with the ilm's source list.
16167c478bd9Sstevel@tonic-gate 		 * Otherwise, send a source list change report with
16177c478bd9Sstevel@tonic-gate 		 * the current retransmit lists.
16187c478bd9Sstevel@tonic-gate 		 */
16197c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt > 0);
16207c478bd9Sstevel@tonic-gate 		ASSERT(rtxp->rtx_cnt >= rtxp->rtx_fmode_cnt);
16217c478bd9Sstevel@tonic-gate 		rtxp->rtx_cnt--;
16227c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_fmode_cnt > 0) {
16237c478bd9Sstevel@tonic-gate 			rtxp->rtx_fmode_cnt--;
16247c478bd9Sstevel@tonic-gate 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
16257c478bd9Sstevel@tonic-gate 			    CHANGE_TO_INCLUDE : CHANGE_TO_EXCLUDE;
16267c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
16277c478bd9Sstevel@tonic-gate 			    ilm->ilm_filter, rtxrp);
16287c478bd9Sstevel@tonic-gate 		} else {
16297c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(ALLOW_NEW_SOURCES,
16307c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_allow, rtxrp);
16317c478bd9Sstevel@tonic-gate 			rtxrp = mcast_bldmrec(BLOCK_OLD_SOURCES,
16327c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_block, rtxrp);
16337c478bd9Sstevel@tonic-gate 		}
16347c478bd9Sstevel@tonic-gate 		if (rtxp->rtx_cnt > 0) {
16357c478bd9Sstevel@tonic-gate 			MCAST_RANDOM_DELAY(rtxp->rtx_timer,
16367c478bd9Sstevel@tonic-gate 			    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
16377c478bd9Sstevel@tonic-gate 			if (rtxp->rtx_timer < next)
16387c478bd9Sstevel@tonic-gate 				next = rtxp->rtx_timer;
16398dc47d9fSudpa 			rtxp->rtx_timer += current;
16407c478bd9Sstevel@tonic-gate 		} else {
16417c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_allow);
16427c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_block);
16437c478bd9Sstevel@tonic-gate 		}
16447c478bd9Sstevel@tonic-gate 	}
16457c478bd9Sstevel@tonic-gate 
16467c478bd9Sstevel@tonic-gate 	if (ill->ill_mcast_type == MLD_V2_ROUTER) {
16477c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
16487c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rp);
16497c478bd9Sstevel@tonic-gate 		mldv2_sendrpt(ill, rtxrp);
16507c478bd9Sstevel@tonic-gate 		return (next);
16517c478bd9Sstevel@tonic-gate 	}
16527c478bd9Sstevel@tonic-gate 
16537c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
16547c478bd9Sstevel@tonic-gate 
16557c478bd9Sstevel@tonic-gate 	return (next);
16567c478bd9Sstevel@tonic-gate }
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate /*
16597c478bd9Sstevel@tonic-gate  * mld_timeout_handler:
16607c478bd9Sstevel@tonic-gate  * Called when there are timeout events, every next * TMEOUT_INTERVAL (tick).
16617c478bd9Sstevel@tonic-gate  * Returns number of ticks to next event (or 0 if none).
16627c478bd9Sstevel@tonic-gate  * MT issues are same as igmp_timeout_handler
16637c478bd9Sstevel@tonic-gate  */
16647c478bd9Sstevel@tonic-gate void
16657c478bd9Sstevel@tonic-gate mld_timeout_handler(void *arg)
16667c478bd9Sstevel@tonic-gate {
16677c478bd9Sstevel@tonic-gate 	ill_t	*ill;
16687c478bd9Sstevel@tonic-gate 	uint_t  global_next = INFINITY;
16697c478bd9Sstevel@tonic-gate 	uint_t  next;
16707c478bd9Sstevel@tonic-gate 	ill_walk_context_t ctx;
16717c478bd9Sstevel@tonic-gate 	boolean_t success;
1672328c7d1fSmeem 	ip_stack_t *ipst = arg;
1673f4b3ec61Sdh 
1674f4b3ec61Sdh 	ASSERT(arg != NULL);
1675f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
1676f4b3ec61Sdh 	ASSERT(ipst->ips_mld_timeout_id != 0);
16778dc47d9fSudpa 	ipst->ips_mld_timer_scheduled_last = 0;
1678f4b3ec61Sdh 	ipst->ips_mld_time_to_next = 0;
1679f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
1680f4b3ec61Sdh 
1681f4b3ec61Sdh 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
1682f4b3ec61Sdh 	ill = ILL_START_WALK_V6(&ctx, ipst);
16837c478bd9Sstevel@tonic-gate 	for (; ill != NULL; ill = ill_next(&ctx, ill)) {
16847c478bd9Sstevel@tonic-gate 		ASSERT(ill->ill_isv6);
16857c478bd9Sstevel@tonic-gate 		/*
16867c478bd9Sstevel@tonic-gate 		 * We may not be able to refhold the ill if the ill/ipif
16877c478bd9Sstevel@tonic-gate 		 * is changing. But we need to make sure that the ill will
16887c478bd9Sstevel@tonic-gate 		 * not vanish. So we just bump up the ill_waiter count.
16897c478bd9Sstevel@tonic-gate 		 */
16907c478bd9Sstevel@tonic-gate 		if (!ill_waiter_inc(ill))
16917c478bd9Sstevel@tonic-gate 			continue;
1692f4b3ec61Sdh 		rw_exit(&ipst->ips_ill_g_lock);
1693da14cebeSEric Cheng 		success = ipsq_enter(ill, B_TRUE, NEW_OP);
16947c478bd9Sstevel@tonic-gate 		if (success) {
16958dc47d9fSudpa 			next = mld_timeout_handler_per_ill(ill);
16967c478bd9Sstevel@tonic-gate 			if (next < global_next)
16977c478bd9Sstevel@tonic-gate 				global_next = next;
1698328c7d1fSmeem 			ipsq_exit(ill->ill_phyint->phyint_ipsq);
16997c478bd9Sstevel@tonic-gate 		}
1700f4b3ec61Sdh 		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
17017c478bd9Sstevel@tonic-gate 		ill_waiter_dcr(ill);
17027c478bd9Sstevel@tonic-gate 	}
1703f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
17047c478bd9Sstevel@tonic-gate 
1705f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
1706f4b3ec61Sdh 	ASSERT(ipst->ips_mld_timeout_id != 0);
1707f4b3ec61Sdh 	ipst->ips_mld_timeout_id = 0;
1708f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 	if (global_next != INFINITY)
1711f4b3ec61Sdh 		mld_start_timers(global_next, ipst);
17127c478bd9Sstevel@tonic-gate }
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate /*
17157c478bd9Sstevel@tonic-gate  * Calculate the Older Version Querier Present timeout value, in number
17167c478bd9Sstevel@tonic-gate  * of slowtimo intervals, for the given ill.
17177c478bd9Sstevel@tonic-gate  */
17187c478bd9Sstevel@tonic-gate #define	OVQP(ill) \
17197c478bd9Sstevel@tonic-gate 	((1000 * (((ill)->ill_mcast_rv * (ill)->ill_mcast_qi) \
17207c478bd9Sstevel@tonic-gate 	+ MCAST_QUERY_RESP_INTERVAL)) / MCAST_SLOWTIMO_INTERVAL)
17217c478bd9Sstevel@tonic-gate 
17227c478bd9Sstevel@tonic-gate /*
17237c478bd9Sstevel@tonic-gate  * igmp_slowtimo:
17247c478bd9Sstevel@tonic-gate  * - Resets to new router if we didnt we hear from the router
17257c478bd9Sstevel@tonic-gate  *   in IGMP_AGE_THRESHOLD seconds.
17267c478bd9Sstevel@tonic-gate  * - Resets slowtimeout.
17278dc47d9fSudpa  * Check for ips_igmp_max_version ensures that we don't revert to a higher
17288dc47d9fSudpa  * IGMP version than configured.
17297c478bd9Sstevel@tonic-gate  */
17307c478bd9Sstevel@tonic-gate void
17317c478bd9Sstevel@tonic-gate igmp_slowtimo(void *arg)
17327c478bd9Sstevel@tonic-gate {
17337c478bd9Sstevel@tonic-gate 	ill_t	*ill;
17347c478bd9Sstevel@tonic-gate 	ill_if_t *ifp;
17357c478bd9Sstevel@tonic-gate 	avl_tree_t *avl_tree;
1736f4b3ec61Sdh 	ip_stack_t *ipst = (ip_stack_t *)arg;
17377c478bd9Sstevel@tonic-gate 
1738f4b3ec61Sdh 	ASSERT(arg != NULL);
17397c478bd9Sstevel@tonic-gate 	/* Hold the ill_g_lock so that we can safely walk the ill list */
1740f4b3ec61Sdh 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 	/*
17437c478bd9Sstevel@tonic-gate 	 * The ill_if_t list is circular, hence the odd loop parameters.
17447c478bd9Sstevel@tonic-gate 	 *
17457c478bd9Sstevel@tonic-gate 	 * We can't use the ILL_START_WALK and ill_next() wrappers for this
17467c478bd9Sstevel@tonic-gate 	 * walk, as we need to check the illif_mcast_* fields in the ill_if_t
17477c478bd9Sstevel@tonic-gate 	 * structure (allowing us to skip if none of the instances have timers
17487c478bd9Sstevel@tonic-gate 	 * running).
17497c478bd9Sstevel@tonic-gate 	 */
1750f4b3ec61Sdh 	for (ifp = IP_V4_ILL_G_LIST(ipst);
1751f4b3ec61Sdh 	    ifp != (ill_if_t *)&IP_V4_ILL_G_LIST(ipst);
17527c478bd9Sstevel@tonic-gate 	    ifp = ifp->illif_next) {
17537c478bd9Sstevel@tonic-gate 		/*
17547c478bd9Sstevel@tonic-gate 		 * illif_mcast_v[12] are set using atomics. If an ill hears
17557c478bd9Sstevel@tonic-gate 		 * a V1 or V2 query now and we miss seeing the count now,
17567c478bd9Sstevel@tonic-gate 		 * we will see it the next time igmp_slowtimo is called.
17577c478bd9Sstevel@tonic-gate 		 */
17587c478bd9Sstevel@tonic-gate 		if (ifp->illif_mcast_v1 == 0 && ifp->illif_mcast_v2 == 0)
17597c478bd9Sstevel@tonic-gate 			continue;
17607c478bd9Sstevel@tonic-gate 
17617c478bd9Sstevel@tonic-gate 		avl_tree = &ifp->illif_avl_by_ppa;
17627c478bd9Sstevel@tonic-gate 		for (ill = avl_first(avl_tree); ill != NULL;
17637c478bd9Sstevel@tonic-gate 		    ill = avl_walk(avl_tree, ill, AVL_AFTER)) {
17647c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
17657c478bd9Sstevel@tonic-gate 			if (ill->ill_mcast_v1_tset == 1)
17667c478bd9Sstevel@tonic-gate 				ill->ill_mcast_v1_time++;
17677c478bd9Sstevel@tonic-gate 			if (ill->ill_mcast_v2_tset == 1)
17687c478bd9Sstevel@tonic-gate 				ill->ill_mcast_v2_time++;
17698dc47d9fSudpa 			if ((ill->ill_mcast_type == IGMP_V1_ROUTER) &&
17708dc47d9fSudpa 			    (ipst->ips_igmp_max_version >= IGMP_V2_ROUTER) &&
17718dc47d9fSudpa 			    (ill->ill_mcast_v1_time >= OVQP(ill))) {
17728dc47d9fSudpa 				if ((ill->ill_mcast_v2_tset > 0) ||
17738dc47d9fSudpa 				    (ipst->ips_igmp_max_version ==
17748dc47d9fSudpa 				    IGMP_V2_ROUTER)) {
17758dc47d9fSudpa 					ip1dbg(("V1 query timer "
17768dc47d9fSudpa 					    "expired on %s; switching "
17778dc47d9fSudpa 					    "mode to IGMP_V2\n",
17788dc47d9fSudpa 					    ill->ill_name));
17798dc47d9fSudpa 					ill->ill_mcast_type =
17808dc47d9fSudpa 					    IGMP_V2_ROUTER;
17818dc47d9fSudpa 				} else {
17828dc47d9fSudpa 					ip1dbg(("V1 query timer "
17838dc47d9fSudpa 					    "expired on %s; switching "
17848dc47d9fSudpa 					    "mode to IGMP_V3\n",
17857c478bd9Sstevel@tonic-gate 					    ill->ill_name));
17868dc47d9fSudpa 					ill->ill_mcast_type =
17878dc47d9fSudpa 					    IGMP_V3_ROUTER;
17887c478bd9Sstevel@tonic-gate 				}
17898dc47d9fSudpa 				ill->ill_mcast_v1_time = 0;
17908dc47d9fSudpa 				ill->ill_mcast_v1_tset = 0;
17918dc47d9fSudpa 				atomic_add_16(&ifp->illif_mcast_v1, -1);
17928dc47d9fSudpa 			}
17938dc47d9fSudpa 			if ((ill->ill_mcast_type == IGMP_V2_ROUTER) &&
17948dc47d9fSudpa 			    (ipst->ips_igmp_max_version >= IGMP_V3_ROUTER) &&
17958dc47d9fSudpa 			    (ill->ill_mcast_v2_time >= OVQP(ill))) {
17968dc47d9fSudpa 				ip1dbg(("V2 query timer expired on "
17978dc47d9fSudpa 				    "%s; switching mode to IGMP_V3\n",
17988dc47d9fSudpa 				    ill->ill_name));
17998dc47d9fSudpa 				ill->ill_mcast_type = IGMP_V3_ROUTER;
18008dc47d9fSudpa 				ill->ill_mcast_v2_time = 0;
18018dc47d9fSudpa 				ill->ill_mcast_v2_tset = 0;
18028dc47d9fSudpa 				atomic_add_16(&ifp->illif_mcast_v2, -1);
18037c478bd9Sstevel@tonic-gate 			}
18047c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
18057c478bd9Sstevel@tonic-gate 		}
18067c478bd9Sstevel@tonic-gate 	}
1807f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
1808f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_slowtimeout_lock);
1809f4b3ec61Sdh 	ipst->ips_igmp_slowtimeout_id = timeout(igmp_slowtimo, (void *)ipst,
1810328c7d1fSmeem 	    MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
1811f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_slowtimeout_lock);
18127c478bd9Sstevel@tonic-gate }
18137c478bd9Sstevel@tonic-gate 
18147c478bd9Sstevel@tonic-gate /*
18157c478bd9Sstevel@tonic-gate  * mld_slowtimo:
18167c478bd9Sstevel@tonic-gate  * - Resets to newer version if we didn't hear from the older version router
18177c478bd9Sstevel@tonic-gate  *   in MLD_AGE_THRESHOLD seconds.
18187c478bd9Sstevel@tonic-gate  * - Restarts slowtimeout.
18198dc47d9fSudpa  * Check for ips_mld_max_version ensures that we don't revert to a higher
18208dc47d9fSudpa  * IGMP version than configured.
18217c478bd9Sstevel@tonic-gate  */
18227c478bd9Sstevel@tonic-gate /* ARGSUSED */
18237c478bd9Sstevel@tonic-gate void
18247c478bd9Sstevel@tonic-gate mld_slowtimo(void *arg)
18257c478bd9Sstevel@tonic-gate {
18267c478bd9Sstevel@tonic-gate 	ill_t *ill;
18277c478bd9Sstevel@tonic-gate 	ill_if_t *ifp;
18287c478bd9Sstevel@tonic-gate 	avl_tree_t *avl_tree;
1829f4b3ec61Sdh 	ip_stack_t *ipst = (ip_stack_t *)arg;
18307c478bd9Sstevel@tonic-gate 
1831f4b3ec61Sdh 	ASSERT(arg != NULL);
18327c478bd9Sstevel@tonic-gate 	/* See comments in igmp_slowtimo() above... */
1833f4b3ec61Sdh 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
1834f4b3ec61Sdh 	for (ifp = IP_V6_ILL_G_LIST(ipst);
1835f4b3ec61Sdh 	    ifp != (ill_if_t *)&IP_V6_ILL_G_LIST(ipst);
18367c478bd9Sstevel@tonic-gate 	    ifp = ifp->illif_next) {
18377c478bd9Sstevel@tonic-gate 		if (ifp->illif_mcast_v1 == 0)
18387c478bd9Sstevel@tonic-gate 			continue;
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate 		avl_tree = &ifp->illif_avl_by_ppa;
18417c478bd9Sstevel@tonic-gate 		for (ill = avl_first(avl_tree); ill != NULL;
18427c478bd9Sstevel@tonic-gate 		    ill = avl_walk(avl_tree, ill, AVL_AFTER)) {
18437c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
18447c478bd9Sstevel@tonic-gate 			if (ill->ill_mcast_v1_tset == 1)
18457c478bd9Sstevel@tonic-gate 				ill->ill_mcast_v1_time++;
18468dc47d9fSudpa 			if ((ill->ill_mcast_type == MLD_V1_ROUTER) &&
18478dc47d9fSudpa 			    (ipst->ips_mld_max_version >= MLD_V2_ROUTER) &&
18488dc47d9fSudpa 			    (ill->ill_mcast_v1_time >= OVQP(ill))) {
18498dc47d9fSudpa 				ip1dbg(("MLD query timer expired on"
18508dc47d9fSudpa 				    " %s; switching mode to MLD_V2\n",
18518dc47d9fSudpa 				    ill->ill_name));
18528dc47d9fSudpa 				ill->ill_mcast_type = MLD_V2_ROUTER;
18538dc47d9fSudpa 				ill->ill_mcast_v1_time = 0;
18548dc47d9fSudpa 				ill->ill_mcast_v1_tset = 0;
18558dc47d9fSudpa 				atomic_add_16(&ifp->illif_mcast_v1, -1);
18567c478bd9Sstevel@tonic-gate 			}
18577c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
18587c478bd9Sstevel@tonic-gate 		}
18597c478bd9Sstevel@tonic-gate 	}
1860f4b3ec61Sdh 	rw_exit(&ipst->ips_ill_g_lock);
1861f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_slowtimeout_lock);
1862f4b3ec61Sdh 	ipst->ips_mld_slowtimeout_id = timeout(mld_slowtimo, (void *)ipst,
18637c478bd9Sstevel@tonic-gate 	    MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
1864f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_slowtimeout_lock);
18657c478bd9Sstevel@tonic-gate }
18667c478bd9Sstevel@tonic-gate 
18677c478bd9Sstevel@tonic-gate /*
18687c478bd9Sstevel@tonic-gate  * igmp_sendpkt:
18697c478bd9Sstevel@tonic-gate  * This will send to ip_wput like icmp_inbound.
18707c478bd9Sstevel@tonic-gate  * Note that the lower ill (on which the membership is kept) is used
18717c478bd9Sstevel@tonic-gate  * as an upper ill to pass in the multicast parameters.
18727c478bd9Sstevel@tonic-gate  */
18737c478bd9Sstevel@tonic-gate static void
18747c478bd9Sstevel@tonic-gate igmp_sendpkt(ilm_t *ilm, uchar_t type, ipaddr_t addr)
18757c478bd9Sstevel@tonic-gate {
18767c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
18777c478bd9Sstevel@tonic-gate 	igmpa_t	*igmpa;
18787c478bd9Sstevel@tonic-gate 	uint8_t *rtralert;
18797c478bd9Sstevel@tonic-gate 	ipha_t	*ipha;
18807c478bd9Sstevel@tonic-gate 	int	hdrlen = sizeof (ipha_t) + RTRALERT_LEN;
18817c478bd9Sstevel@tonic-gate 	size_t	size  = hdrlen + sizeof (igmpa_t);
18827c478bd9Sstevel@tonic-gate 	ipif_t 	*ipif = ilm->ilm_ipif;
1883*e11c3f44Smeem 	ill_t 	*ill  = ipif->ipif_ill;
18847c478bd9Sstevel@tonic-gate 	mblk_t	*first_mp;
18857c478bd9Sstevel@tonic-gate 	ipsec_out_t *io;
188645916cd2Sjpk 	zoneid_t zoneid;
1887f4b3ec61Sdh 	ip_stack_t *ipst = ill->ill_ipst;
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	/*
18907c478bd9Sstevel@tonic-gate 	 * We need to make sure this packet goes out on an ipif. If
18917c478bd9Sstevel@tonic-gate 	 * there is some global policy match in ip_wput_ire, we need
18927c478bd9Sstevel@tonic-gate 	 * to get to the right interface after IPSEC processing.
18937c478bd9Sstevel@tonic-gate 	 * To make sure this multicast packet goes out on the right
18947c478bd9Sstevel@tonic-gate 	 * interface, we attach an ipsec_out and initialize ill_index
18957c478bd9Sstevel@tonic-gate 	 * like we did in ip_wput. To make sure that this packet does
18967c478bd9Sstevel@tonic-gate 	 * not get forwarded on other interfaces or looped back, we
18977c478bd9Sstevel@tonic-gate 	 * set ipsec_out_dontroute to B_TRUE and ipsec_out_multicast_loop
18987c478bd9Sstevel@tonic-gate 	 * to B_FALSE.
18997c478bd9Sstevel@tonic-gate 	 */
19007c478bd9Sstevel@tonic-gate 	first_mp = allocb(sizeof (ipsec_info_t), BPRI_HI);
19017c478bd9Sstevel@tonic-gate 	if (first_mp == NULL)
19027c478bd9Sstevel@tonic-gate 		return;
19037c478bd9Sstevel@tonic-gate 
19047c478bd9Sstevel@tonic-gate 	first_mp->b_datap->db_type = M_CTL;
19057c478bd9Sstevel@tonic-gate 	first_mp->b_wptr += sizeof (ipsec_info_t);
19067c478bd9Sstevel@tonic-gate 	bzero(first_mp->b_rptr, sizeof (ipsec_info_t));
19077c478bd9Sstevel@tonic-gate 	/* ipsec_out_secure is B_FALSE now */
19087c478bd9Sstevel@tonic-gate 	io = (ipsec_out_t *)first_mp->b_rptr;
19097c478bd9Sstevel@tonic-gate 	io->ipsec_out_type = IPSEC_OUT;
19107c478bd9Sstevel@tonic-gate 	io->ipsec_out_len = sizeof (ipsec_out_t);
19117c478bd9Sstevel@tonic-gate 	io->ipsec_out_use_global_policy = B_TRUE;
19127c478bd9Sstevel@tonic-gate 	io->ipsec_out_ill_index = ill->ill_phyint->phyint_ifindex;
19137c478bd9Sstevel@tonic-gate 	io->ipsec_out_multicast_loop = B_FALSE;
19147c478bd9Sstevel@tonic-gate 	io->ipsec_out_dontroute = B_TRUE;
191545916cd2Sjpk 	if ((zoneid = ilm->ilm_zoneid) == ALL_ZONES)
191645916cd2Sjpk 		zoneid = GLOBAL_ZONEID;
191745916cd2Sjpk 	io->ipsec_out_zoneid = zoneid;
1918f4b3ec61Sdh 	io->ipsec_out_ns = ipst->ips_netstack;	/* No netstack_hold */
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
19217c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
19227c478bd9Sstevel@tonic-gate 		freemsg(first_mp);
19237c478bd9Sstevel@tonic-gate 		return;
19247c478bd9Sstevel@tonic-gate 	}
19257c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + size;
19267c478bd9Sstevel@tonic-gate 	first_mp->b_cont = mp;
19277c478bd9Sstevel@tonic-gate 
19287c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
19297c478bd9Sstevel@tonic-gate 	rtralert = (uint8_t *)&(ipha[1]);
19307c478bd9Sstevel@tonic-gate 	igmpa = (igmpa_t *)&(rtralert[RTRALERT_LEN]);
19317c478bd9Sstevel@tonic-gate 	igmpa->igmpa_type   = type;
19327c478bd9Sstevel@tonic-gate 	igmpa->igmpa_code   = 0;
19337c478bd9Sstevel@tonic-gate 	igmpa->igmpa_group  = ilm->ilm_addr;
19347c478bd9Sstevel@tonic-gate 	igmpa->igmpa_cksum  = 0;
19357c478bd9Sstevel@tonic-gate 	igmpa->igmpa_cksum  = IP_CSUM(mp, hdrlen, 0);
19367c478bd9Sstevel@tonic-gate 
1937c0ad9723Srk 	rtralert[0] = IPOPT_COPY | IPOPT_RTRALERT;
19387c478bd9Sstevel@tonic-gate 	rtralert[1] = RTRALERT_LEN;
19397c478bd9Sstevel@tonic-gate 	rtralert[2] = 0;
19407c478bd9Sstevel@tonic-gate 	rtralert[3] = 0;
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length = (IP_VERSION << 4)
19437c478bd9Sstevel@tonic-gate 	    | (IP_SIMPLE_HDR_LENGTH_IN_WORDS + RTRALERT_LEN_IN_WORDS);
19447c478bd9Sstevel@tonic-gate 	ipha->ipha_type_of_service 	= 0;
19457c478bd9Sstevel@tonic-gate 	ipha->ipha_length = htons(size);
19467c478bd9Sstevel@tonic-gate 	ipha->ipha_ident = 0;
19477c478bd9Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags = 0;
19487c478bd9Sstevel@tonic-gate 	ipha->ipha_ttl 		= IGMP_TTL;
19497c478bd9Sstevel@tonic-gate 	ipha->ipha_protocol 	= IPPROTO_IGMP;
19507c478bd9Sstevel@tonic-gate 	ipha->ipha_hdr_checksum 	= 0;
19517c478bd9Sstevel@tonic-gate 	ipha->ipha_dst 		= addr ? addr : igmpa->igmpa_group;
19527c478bd9Sstevel@tonic-gate 	ipha->ipha_src 		= ipif->ipif_src_addr;
19537c478bd9Sstevel@tonic-gate 	/*
19547c478bd9Sstevel@tonic-gate 	 * Request loopback of the report if we are acting as a multicast
19557c478bd9Sstevel@tonic-gate 	 * router, so that the process-level routing demon can hear it.
19567c478bd9Sstevel@tonic-gate 	 */
19577c478bd9Sstevel@tonic-gate 	/*
19587c478bd9Sstevel@tonic-gate 	 * This will run multiple times for the same group if there are members
19597c478bd9Sstevel@tonic-gate 	 * on the same group for multiple ipif's on the same ill. The
19607c478bd9Sstevel@tonic-gate 	 * igmp_input code will suppress this due to the loopback thus we
19617c478bd9Sstevel@tonic-gate 	 * always loopback membership report.
19627c478bd9Sstevel@tonic-gate 	 */
19637c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_rq != NULL);
19647c478bd9Sstevel@tonic-gate 	ip_multicast_loopback(ill->ill_rq, ill, first_mp, 0, ilm->ilm_zoneid);
19657c478bd9Sstevel@tonic-gate 
19665597b60aSnordmark 	ip_wput_multicast(ill->ill_wq, first_mp, ipif, zoneid);
19677c478bd9Sstevel@tonic-gate 
1968f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_snd_reports;
19697c478bd9Sstevel@tonic-gate }
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate /*
19727c478bd9Sstevel@tonic-gate  * Sends an IGMP_V3_MEMBERSHIP_REPORT message out the ill associated
19737c478bd9Sstevel@tonic-gate  * with the passed-in ipif.  The report will contain one group record
19747c478bd9Sstevel@tonic-gate  * for each element of reclist.  If this causes packet length to
19757c478bd9Sstevel@tonic-gate  * exceed ipif->ipif_ill->ill_max_frag, multiple reports are sent.
19767c478bd9Sstevel@tonic-gate  * reclist is assumed to be made up of buffers allocated by mcast_bldmrec(),
19777c478bd9Sstevel@tonic-gate  * and those buffers are freed here.
19787c478bd9Sstevel@tonic-gate  */
19797c478bd9Sstevel@tonic-gate static void
19807c478bd9Sstevel@tonic-gate igmpv3_sendrpt(ipif_t *ipif, mrec_t *reclist)
19817c478bd9Sstevel@tonic-gate {
19827c478bd9Sstevel@tonic-gate 	ipsec_out_t *io;
19837c478bd9Sstevel@tonic-gate 	igmp3ra_t *igmp3ra;
19847c478bd9Sstevel@tonic-gate 	grphdra_t *grphdr;
19857c478bd9Sstevel@tonic-gate 	mblk_t *first_mp, *mp;
19867c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
19877c478bd9Sstevel@tonic-gate 	uint8_t *rtralert;
19887c478bd9Sstevel@tonic-gate 	ipaddr_t *src_array;
19897c478bd9Sstevel@tonic-gate 	int i, j, numrec, more_src_cnt;
19907c478bd9Sstevel@tonic-gate 	size_t hdrsize, size, rsize;
19917c478bd9Sstevel@tonic-gate 	ill_t *ill = ipif->ipif_ill;
19927c478bd9Sstevel@tonic-gate 	mrec_t *rp, *cur_reclist;
19937c478bd9Sstevel@tonic-gate 	mrec_t *next_reclist = reclist;
19947c478bd9Sstevel@tonic-gate 	boolean_t morepkts;
199545916cd2Sjpk 	zoneid_t zoneid;
1996f4b3ec61Sdh 	ip_stack_t	 *ipst = ill->ill_ipst;
19977c478bd9Sstevel@tonic-gate 
1998*e11c3f44Smeem 	ASSERT(IAM_WRITER_IPIF(ipif));
1999*e11c3f44Smeem 
20007c478bd9Sstevel@tonic-gate 	/* if there aren't any records, there's nothing to send */
20017c478bd9Sstevel@tonic-gate 	if (reclist == NULL)
20027c478bd9Sstevel@tonic-gate 		return;
20037c478bd9Sstevel@tonic-gate 
20047c478bd9Sstevel@tonic-gate 	hdrsize = sizeof (ipha_t) + RTRALERT_LEN;
20057c478bd9Sstevel@tonic-gate nextpkt:
20067c478bd9Sstevel@tonic-gate 	size = hdrsize + sizeof (igmp3ra_t);
20077c478bd9Sstevel@tonic-gate 	morepkts = B_FALSE;
20087c478bd9Sstevel@tonic-gate 	more_src_cnt = 0;
20097c478bd9Sstevel@tonic-gate 	cur_reclist = next_reclist;
20107c478bd9Sstevel@tonic-gate 	numrec = 0;
20117c478bd9Sstevel@tonic-gate 	for (rp = cur_reclist; rp != NULL; rp = rp->mrec_next) {
20127c478bd9Sstevel@tonic-gate 		rsize = sizeof (grphdra_t) +
20137c478bd9Sstevel@tonic-gate 		    (rp->mrec_srcs.sl_numsrc * sizeof (ipaddr_t));
20147c478bd9Sstevel@tonic-gate 		if (size + rsize > ill->ill_max_frag) {
20157c478bd9Sstevel@tonic-gate 			if (rp == cur_reclist) {
20167c478bd9Sstevel@tonic-gate 				/*
20177c478bd9Sstevel@tonic-gate 				 * If the first mrec we looked at is too big
20187c478bd9Sstevel@tonic-gate 				 * to fit in a single packet (i.e the source
20197c478bd9Sstevel@tonic-gate 				 * list is too big), we must either truncate
20207c478bd9Sstevel@tonic-gate 				 * the list (if TO_EX or IS_EX), or send
20217c478bd9Sstevel@tonic-gate 				 * multiple reports for the same group (all
20227c478bd9Sstevel@tonic-gate 				 * other types).
20237c478bd9Sstevel@tonic-gate 				 */
20247c478bd9Sstevel@tonic-gate 				int srcspace, srcsperpkt;
20257c478bd9Sstevel@tonic-gate 				srcspace = ill->ill_max_frag - (size +
20267c478bd9Sstevel@tonic-gate 				    sizeof (grphdra_t));
2027*e11c3f44Smeem 
2028*e11c3f44Smeem 				/*
2029*e11c3f44Smeem 				 * Skip if there's not even enough room in
2030*e11c3f44Smeem 				 * a single packet to send something useful.
2031*e11c3f44Smeem 				 */
2032*e11c3f44Smeem 				if (srcspace <= sizeof (ipaddr_t))
2033*e11c3f44Smeem 					continue;
2034*e11c3f44Smeem 
20357c478bd9Sstevel@tonic-gate 				srcsperpkt = srcspace / sizeof (ipaddr_t);
20367c478bd9Sstevel@tonic-gate 				/*
20377c478bd9Sstevel@tonic-gate 				 * Increment size and numrec, because we will
20387c478bd9Sstevel@tonic-gate 				 * be sending a record for the mrec we're
20397c478bd9Sstevel@tonic-gate 				 * looking at now.
20407c478bd9Sstevel@tonic-gate 				 */
20417c478bd9Sstevel@tonic-gate 				size += sizeof (grphdra_t) +
20427c478bd9Sstevel@tonic-gate 				    (srcsperpkt * sizeof (ipaddr_t));
20437c478bd9Sstevel@tonic-gate 				numrec++;
20447c478bd9Sstevel@tonic-gate 				if (rp->mrec_type == MODE_IS_EXCLUDE ||
20457c478bd9Sstevel@tonic-gate 				    rp->mrec_type == CHANGE_TO_EXCLUDE) {
20467c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
20477c478bd9Sstevel@tonic-gate 					if (rp->mrec_next == NULL) {
20487c478bd9Sstevel@tonic-gate 						/* no more packets to send */
20497c478bd9Sstevel@tonic-gate 						break;
20507c478bd9Sstevel@tonic-gate 					} else {
20517c478bd9Sstevel@tonic-gate 						/*
20527c478bd9Sstevel@tonic-gate 						 * more packets, but we're
20537c478bd9Sstevel@tonic-gate 						 * done with this mrec.
20547c478bd9Sstevel@tonic-gate 						 */
20557c478bd9Sstevel@tonic-gate 						next_reclist = rp->mrec_next;
20567c478bd9Sstevel@tonic-gate 					}
20577c478bd9Sstevel@tonic-gate 				} else {
20587c478bd9Sstevel@tonic-gate 					more_src_cnt = rp->mrec_srcs.sl_numsrc
20597c478bd9Sstevel@tonic-gate 					    - srcsperpkt;
20607c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
20617c478bd9Sstevel@tonic-gate 					/*
20627c478bd9Sstevel@tonic-gate 					 * We'll fix up this mrec (remove the
20637c478bd9Sstevel@tonic-gate 					 * srcs we've already sent) before
20647c478bd9Sstevel@tonic-gate 					 * returning to nextpkt above.
20657c478bd9Sstevel@tonic-gate 					 */
20667c478bd9Sstevel@tonic-gate 					next_reclist = rp;
20677c478bd9Sstevel@tonic-gate 				}
20687c478bd9Sstevel@tonic-gate 			} else {
20697c478bd9Sstevel@tonic-gate 				next_reclist = rp;
20707c478bd9Sstevel@tonic-gate 			}
20717c478bd9Sstevel@tonic-gate 			morepkts = B_TRUE;
20727c478bd9Sstevel@tonic-gate 			break;
20737c478bd9Sstevel@tonic-gate 		}
20747c478bd9Sstevel@tonic-gate 		size += rsize;
20757c478bd9Sstevel@tonic-gate 		numrec++;
20767c478bd9Sstevel@tonic-gate 	}
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 	/*
20797c478bd9Sstevel@tonic-gate 	 * See comments in igmp_sendpkt() about initializing for ipsec and
20807c478bd9Sstevel@tonic-gate 	 * load balancing requirements.
20817c478bd9Sstevel@tonic-gate 	 */
20827c478bd9Sstevel@tonic-gate 	first_mp = allocb(sizeof (ipsec_info_t), BPRI_HI);
20837c478bd9Sstevel@tonic-gate 	if (first_mp == NULL)
20847c478bd9Sstevel@tonic-gate 		goto free_reclist;
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 	first_mp->b_datap->db_type = M_CTL;
20877c478bd9Sstevel@tonic-gate 	first_mp->b_wptr += sizeof (ipsec_info_t);
20887c478bd9Sstevel@tonic-gate 	bzero(first_mp->b_rptr, sizeof (ipsec_info_t));
20897c478bd9Sstevel@tonic-gate 	/* ipsec_out_secure is B_FALSE now */
20907c478bd9Sstevel@tonic-gate 	io = (ipsec_out_t *)first_mp->b_rptr;
20917c478bd9Sstevel@tonic-gate 	io->ipsec_out_type = IPSEC_OUT;
20927c478bd9Sstevel@tonic-gate 	io->ipsec_out_len = sizeof (ipsec_out_t);
20937c478bd9Sstevel@tonic-gate 	io->ipsec_out_use_global_policy = B_TRUE;
20947c478bd9Sstevel@tonic-gate 	io->ipsec_out_ill_index = ill->ill_phyint->phyint_ifindex;
20957c478bd9Sstevel@tonic-gate 	io->ipsec_out_multicast_loop = B_FALSE;
20967c478bd9Sstevel@tonic-gate 	io->ipsec_out_dontroute = B_TRUE;
209745916cd2Sjpk 	if ((zoneid = ipif->ipif_zoneid) == ALL_ZONES)
209845916cd2Sjpk 		zoneid = GLOBAL_ZONEID;
209945916cd2Sjpk 	io->ipsec_out_zoneid = zoneid;
21007c478bd9Sstevel@tonic-gate 
21017c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
21027c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
21037c478bd9Sstevel@tonic-gate 		freemsg(first_mp);
21047c478bd9Sstevel@tonic-gate 		goto free_reclist;
21057c478bd9Sstevel@tonic-gate 	}
21067c478bd9Sstevel@tonic-gate 	bzero((char *)mp->b_rptr, size);
21077c478bd9Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)(mp->b_rptr + size);
21087c478bd9Sstevel@tonic-gate 	first_mp->b_cont = mp;
21097c478bd9Sstevel@tonic-gate 
21107c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
21117c478bd9Sstevel@tonic-gate 	rtralert = (uint8_t *)&(ipha[1]);
21127c478bd9Sstevel@tonic-gate 	igmp3ra = (igmp3ra_t *)&(rtralert[RTRALERT_LEN]);
21137c478bd9Sstevel@tonic-gate 	grphdr = (grphdra_t *)&(igmp3ra[1]);
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 	rp = cur_reclist;
21167c478bd9Sstevel@tonic-gate 	for (i = 0; i < numrec; i++) {
21177c478bd9Sstevel@tonic-gate 		grphdr->grphdra_type = rp->mrec_type;
21187c478bd9Sstevel@tonic-gate 		grphdr->grphdra_numsrc = htons(rp->mrec_srcs.sl_numsrc);
21197c478bd9Sstevel@tonic-gate 		grphdr->grphdra_group = V4_PART_OF_V6(rp->mrec_group);
21207c478bd9Sstevel@tonic-gate 		src_array = (ipaddr_t *)&(grphdr[1]);
21217c478bd9Sstevel@tonic-gate 
21227c478bd9Sstevel@tonic-gate 		for (j = 0; j < rp->mrec_srcs.sl_numsrc; j++)
21237c478bd9Sstevel@tonic-gate 			src_array[j] = V4_PART_OF_V6(rp->mrec_srcs.sl_addr[j]);
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 		grphdr = (grphdra_t *)&(src_array[j]);
21267c478bd9Sstevel@tonic-gate 		rp = rp->mrec_next;
21277c478bd9Sstevel@tonic-gate 	}
21287c478bd9Sstevel@tonic-gate 
21297c478bd9Sstevel@tonic-gate 	igmp3ra->igmp3ra_type = IGMP_V3_MEMBERSHIP_REPORT;
21307c478bd9Sstevel@tonic-gate 	igmp3ra->igmp3ra_numrec = htons(numrec);
21317c478bd9Sstevel@tonic-gate 	igmp3ra->igmp3ra_cksum = IP_CSUM(mp, hdrsize, 0);
21327c478bd9Sstevel@tonic-gate 
2133c0ad9723Srk 	rtralert[0] = IPOPT_COPY | IPOPT_RTRALERT;
21347c478bd9Sstevel@tonic-gate 	rtralert[1] = RTRALERT_LEN;
21357c478bd9Sstevel@tonic-gate 	rtralert[2] = 0;
21367c478bd9Sstevel@tonic-gate 	rtralert[3] = 0;
21377c478bd9Sstevel@tonic-gate 
21387c478bd9Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length = IP_VERSION << 4
21397c478bd9Sstevel@tonic-gate 	    | (IP_SIMPLE_HDR_LENGTH_IN_WORDS + RTRALERT_LEN_IN_WORDS);
21407c478bd9Sstevel@tonic-gate 	ipha->ipha_type_of_service = IPTOS_PREC_INTERNETCONTROL;
21417c478bd9Sstevel@tonic-gate 	ipha->ipha_length = htons(size);
21427c478bd9Sstevel@tonic-gate 	ipha->ipha_ttl = IGMP_TTL;
21437c478bd9Sstevel@tonic-gate 	ipha->ipha_protocol = IPPROTO_IGMP;
21447c478bd9Sstevel@tonic-gate 	ipha->ipha_dst = htonl(INADDR_ALLRPTS_GROUP);
21457c478bd9Sstevel@tonic-gate 	ipha->ipha_src = ipif->ipif_src_addr;
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate 	/*
21487c478bd9Sstevel@tonic-gate 	 * Request loopback of the report if we are acting as a multicast
21497c478bd9Sstevel@tonic-gate 	 * router, so that the process-level routing daemon can hear it.
21507c478bd9Sstevel@tonic-gate 	 *
21517c478bd9Sstevel@tonic-gate 	 * This will run multiple times for the same group if there are
21527c478bd9Sstevel@tonic-gate 	 * members on the same group for multiple ipifs on the same ill.
21537c478bd9Sstevel@tonic-gate 	 * The igmp_input code will suppress this due to the loopback;
21547c478bd9Sstevel@tonic-gate 	 * thus we always loopback membership report.
21557c478bd9Sstevel@tonic-gate 	 */
21567c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_rq != NULL);
21577c478bd9Sstevel@tonic-gate 	ip_multicast_loopback(ill->ill_rq, ill, mp, 0, ipif->ipif_zoneid);
21587c478bd9Sstevel@tonic-gate 
21595597b60aSnordmark 	ip_wput_multicast(ill->ill_wq, first_mp, ipif, zoneid);
21607c478bd9Sstevel@tonic-gate 
2161f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_snd_reports;
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate 	if (morepkts) {
21647c478bd9Sstevel@tonic-gate 		if (more_src_cnt > 0) {
21657c478bd9Sstevel@tonic-gate 			int index, mvsize;
21667c478bd9Sstevel@tonic-gate 			slist_t *sl = &next_reclist->mrec_srcs;
21677c478bd9Sstevel@tonic-gate 			index = sl->sl_numsrc;
21687c478bd9Sstevel@tonic-gate 			mvsize = more_src_cnt * sizeof (in6_addr_t);
21697c478bd9Sstevel@tonic-gate 			(void) memmove(&sl->sl_addr[0], &sl->sl_addr[index],
21707c478bd9Sstevel@tonic-gate 			    mvsize);
21717c478bd9Sstevel@tonic-gate 			sl->sl_numsrc = more_src_cnt;
21727c478bd9Sstevel@tonic-gate 		}
21737c478bd9Sstevel@tonic-gate 		goto nextpkt;
21747c478bd9Sstevel@tonic-gate 	}
21757c478bd9Sstevel@tonic-gate 
21767c478bd9Sstevel@tonic-gate free_reclist:
21777c478bd9Sstevel@tonic-gate 	while (reclist != NULL) {
21787c478bd9Sstevel@tonic-gate 		rp = reclist->mrec_next;
21797c478bd9Sstevel@tonic-gate 		mi_free(reclist);
21807c478bd9Sstevel@tonic-gate 		reclist = rp;
21817c478bd9Sstevel@tonic-gate 	}
21827c478bd9Sstevel@tonic-gate }
21837c478bd9Sstevel@tonic-gate 
21847c478bd9Sstevel@tonic-gate /*
21857c478bd9Sstevel@tonic-gate  * mld_input:
21867c478bd9Sstevel@tonic-gate  */
21877c478bd9Sstevel@tonic-gate /* ARGSUSED */
21887c478bd9Sstevel@tonic-gate void
21897c478bd9Sstevel@tonic-gate mld_input(queue_t *q, mblk_t *mp, ill_t *ill)
21907c478bd9Sstevel@tonic-gate {
21917c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h = (ip6_t *)(mp->b_rptr);
21927c478bd9Sstevel@tonic-gate 	mld_hdr_t	*mldh;
21937c478bd9Sstevel@tonic-gate 	ilm_t		*ilm;
21947c478bd9Sstevel@tonic-gate 	ipif_t		*ipif;
21957c478bd9Sstevel@tonic-gate 	uint16_t	hdr_length, exthdr_length;
21967c478bd9Sstevel@tonic-gate 	in6_addr_t	*v6group_ptr, *lcladdr_ptr;
21977c478bd9Sstevel@tonic-gate 	uint_t		next;
21987c478bd9Sstevel@tonic-gate 	int		mldlen;
2199f4b3ec61Sdh 	ip_stack_t	*ipst = ill->ill_ipst;
2200*e11c3f44Smeem 	ilm_walker_t	ilw;
22017c478bd9Sstevel@tonic-gate 
22027c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembTotal);
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 	/* Make sure the src address of the packet is link-local */
22057c478bd9Sstevel@tonic-gate 	if (!(IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src))) {
22067c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
22077c478bd9Sstevel@tonic-gate 		freemsg(mp);
22087c478bd9Sstevel@tonic-gate 		return;
22097c478bd9Sstevel@tonic-gate 	}
22107c478bd9Sstevel@tonic-gate 
22117c478bd9Sstevel@tonic-gate 	if (ip6h->ip6_hlim != 1) {
22127c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpBadHoplimit);
22137c478bd9Sstevel@tonic-gate 		freemsg(mp);
22147c478bd9Sstevel@tonic-gate 		return;
22157c478bd9Sstevel@tonic-gate 	}
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate 	/* Get to the icmp header part */
22187c478bd9Sstevel@tonic-gate 	if (ip6h->ip6_nxt != IPPROTO_ICMPV6) {
22197c478bd9Sstevel@tonic-gate 		hdr_length = ip_hdr_length_v6(mp, ip6h);
22207c478bd9Sstevel@tonic-gate 		exthdr_length = hdr_length - IPV6_HDR_LEN;
22217c478bd9Sstevel@tonic-gate 	} else {
22227c478bd9Sstevel@tonic-gate 		hdr_length = IPV6_HDR_LEN;
22237c478bd9Sstevel@tonic-gate 		exthdr_length = 0;
22247c478bd9Sstevel@tonic-gate 	}
22257c478bd9Sstevel@tonic-gate 	mldlen = ntohs(ip6h->ip6_plen) - exthdr_length;
22267c478bd9Sstevel@tonic-gate 
22277c478bd9Sstevel@tonic-gate 	/* An MLD packet must at least be 24 octets to be valid */
22287c478bd9Sstevel@tonic-gate 	if (mldlen < MLD_MINLEN) {
22297c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
22307c478bd9Sstevel@tonic-gate 		freemsg(mp);
22317c478bd9Sstevel@tonic-gate 		return;
22327c478bd9Sstevel@tonic-gate 	}
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 	mldh = (mld_hdr_t *)(&mp->b_rptr[hdr_length]);
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate 	switch (mldh->mld_type) {
22377c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_QUERY:
22387c478bd9Sstevel@tonic-gate 		/*
22397c478bd9Sstevel@tonic-gate 		 * packet length differentiates between v1 and v2.  v1
22407c478bd9Sstevel@tonic-gate 		 * query should be exactly 24 octets long; v2 is >= 28.
22417c478bd9Sstevel@tonic-gate 		 */
22428dc47d9fSudpa 		if ((mldlen == MLD_MINLEN) ||
22438dc47d9fSudpa 		    (ipst->ips_mld_max_version < MLD_V2_ROUTER)) {
22447c478bd9Sstevel@tonic-gate 			next = mld_query_in(mldh, ill);
22457c478bd9Sstevel@tonic-gate 		} else if (mldlen >= MLD_V2_QUERY_MINLEN) {
22467c478bd9Sstevel@tonic-gate 			next = mldv2_query_in((mld2q_t *)mldh, ill, mldlen);
22477c478bd9Sstevel@tonic-gate 		} else {
22487c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
22497c478bd9Sstevel@tonic-gate 			freemsg(mp);
22507c478bd9Sstevel@tonic-gate 			return;
22517c478bd9Sstevel@tonic-gate 		}
22527c478bd9Sstevel@tonic-gate 		if (next == 0) {
22537c478bd9Sstevel@tonic-gate 			freemsg(mp);
22547c478bd9Sstevel@tonic-gate 			return;
22557c478bd9Sstevel@tonic-gate 		}
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 		if (next != INFINITY)
2258f4b3ec61Sdh 			mld_start_timers(next, ipst);
22597c478bd9Sstevel@tonic-gate 		break;
22607c478bd9Sstevel@tonic-gate 
22617c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_REPORT: {
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 		ASSERT(ill->ill_ipif != NULL);
22647c478bd9Sstevel@tonic-gate 		/*
22657c478bd9Sstevel@tonic-gate 		 * For fast leave to work, we have to know that we are the
22667c478bd9Sstevel@tonic-gate 		 * last person to send a report for this group.  Reports
22677c478bd9Sstevel@tonic-gate 		 * generated by us are looped back since we could potentially
22687c478bd9Sstevel@tonic-gate 		 * be a multicast router, so discard reports sourced by me.
22697c478bd9Sstevel@tonic-gate 		 */
22707c478bd9Sstevel@tonic-gate 		lcladdr_ptr = &(ill->ill_ipif->ipif_v6subnet);
22717c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
22727c478bd9Sstevel@tonic-gate 		for (ipif = ill->ill_ipif; ipif != NULL;
22737c478bd9Sstevel@tonic-gate 		    ipif = ipif->ipif_next) {
22747c478bd9Sstevel@tonic-gate 			if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr,
22757c478bd9Sstevel@tonic-gate 			    lcladdr_ptr)) {
22767c478bd9Sstevel@tonic-gate 				if (ip_debug > 1) {
22777c478bd9Sstevel@tonic-gate 					char    buf1[INET6_ADDRSTRLEN];
22787c478bd9Sstevel@tonic-gate 					char	buf2[INET6_ADDRSTRLEN];
22797c478bd9Sstevel@tonic-gate 
22807c478bd9Sstevel@tonic-gate 					(void) mi_strlog(ill->ill_rq,
22817c478bd9Sstevel@tonic-gate 					    1,
22827c478bd9Sstevel@tonic-gate 					    SL_TRACE,
22837c478bd9Sstevel@tonic-gate 					    "mld_input: we are only "
22847c478bd9Sstevel@tonic-gate 					    "member src %s ipif_local %s",
22857c478bd9Sstevel@tonic-gate 					    inet_ntop(AF_INET6, lcladdr_ptr,
22867c478bd9Sstevel@tonic-gate 					    buf1, sizeof (buf1)),
22877c478bd9Sstevel@tonic-gate 					    inet_ntop(AF_INET6,
22887c478bd9Sstevel@tonic-gate 					    &ipif->ipif_v6lcl_addr,
22897c478bd9Sstevel@tonic-gate 					    buf2, sizeof (buf2)));
22907c478bd9Sstevel@tonic-gate 				}
22917c478bd9Sstevel@tonic-gate 				mutex_exit(&ill->ill_lock);
22927c478bd9Sstevel@tonic-gate 				freemsg(mp);
22937c478bd9Sstevel@tonic-gate 				return;
22947c478bd9Sstevel@tonic-gate 			}
22957c478bd9Sstevel@tonic-gate 		}
22967c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
22977c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembResponses);
22987c478bd9Sstevel@tonic-gate 
22997c478bd9Sstevel@tonic-gate 		v6group_ptr = &mldh->mld_addr;
23007c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_MULTICAST(v6group_ptr)) {
23017c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib,
23027c478bd9Sstevel@tonic-gate 			    ipv6IfIcmpInGroupMembBadReports);
23037c478bd9Sstevel@tonic-gate 			freemsg(mp);
23047c478bd9Sstevel@tonic-gate 			return;
23057c478bd9Sstevel@tonic-gate 		}
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate 		/*
23087c478bd9Sstevel@tonic-gate 		 * If we belong to the group being reported, and we are a
23097c478bd9Sstevel@tonic-gate 		 * 'Delaying member' per the RFC terminology, stop our timer
23107c478bd9Sstevel@tonic-gate 		 * for that group and 'clear flag' i.e. mark ilm_state as
23117c478bd9Sstevel@tonic-gate 		 * IGMP_OTHERMEMBER. With zones, there can be multiple group
23127c478bd9Sstevel@tonic-gate 		 * membership entries for the same group address (one per zone)
23137c478bd9Sstevel@tonic-gate 		 * so we need to walk the ill_ilm list.
23147c478bd9Sstevel@tonic-gate 		 */
2315*e11c3f44Smeem 		ilm = ilm_walker_start(&ilw, ill);
2316*e11c3f44Smeem 		for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
23177c478bd9Sstevel@tonic-gate 			if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group_ptr))
2318328c7d1fSmeem 				continue;
23197c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib,
23207c478bd9Sstevel@tonic-gate 			    ipv6IfIcmpInGroupMembOurReports);
23217c478bd9Sstevel@tonic-gate 
23227c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = INFINITY;
23237c478bd9Sstevel@tonic-gate 			ilm->ilm_state = IGMP_OTHERMEMBER;
23247c478bd9Sstevel@tonic-gate 		}
2325*e11c3f44Smeem 		ilm_walker_finish(&ilw);
23267c478bd9Sstevel@tonic-gate 		break;
23277c478bd9Sstevel@tonic-gate 	}
23287c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_REDUCTION:
23297c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembReductions);
23307c478bd9Sstevel@tonic-gate 		break;
23317c478bd9Sstevel@tonic-gate 	}
23327c478bd9Sstevel@tonic-gate 	/*
23337c478bd9Sstevel@tonic-gate 	 * All MLD packets have already been passed up to any
23347c478bd9Sstevel@tonic-gate 	 * process(es) listening on a ICMP6 raw socket. This
23357c478bd9Sstevel@tonic-gate 	 * has been accomplished in ip_deliver_local_v6 prior to
23367c478bd9Sstevel@tonic-gate 	 * this function call. It is assumed that the multicast daemon
23377c478bd9Sstevel@tonic-gate 	 * will have a SOCK_RAW IPPROTO_ICMPV6 (and presumbly use the
23387c478bd9Sstevel@tonic-gate 	 * ICMP6_FILTER socket option to only receive the MLD messages)
23397c478bd9Sstevel@tonic-gate 	 * Thus we can free the MLD message block here
23407c478bd9Sstevel@tonic-gate 	 */
23417c478bd9Sstevel@tonic-gate 	freemsg(mp);
23427c478bd9Sstevel@tonic-gate }
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate /*
23457c478bd9Sstevel@tonic-gate  * Handles an MLDv1 Listener Query.  Returns 0 on error, or the appropriate
23467c478bd9Sstevel@tonic-gate  * (non-zero, unsigned) timer value to be set on success.
23477c478bd9Sstevel@tonic-gate  */
23487c478bd9Sstevel@tonic-gate static uint_t
23497c478bd9Sstevel@tonic-gate mld_query_in(mld_hdr_t *mldh, ill_t *ill)
23507c478bd9Sstevel@tonic-gate {
23517c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
23527c478bd9Sstevel@tonic-gate 	int	timer;
23538dc47d9fSudpa 	uint_t	next, current;
23547c478bd9Sstevel@tonic-gate 	in6_addr_t *v6group;
2355*e11c3f44Smeem 	ilm_walker_t ilw;
23567c478bd9Sstevel@tonic-gate 
23577c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembQueries);
23587c478bd9Sstevel@tonic-gate 
23597c478bd9Sstevel@tonic-gate 	/*
23607c478bd9Sstevel@tonic-gate 	 * In the MLD specification, there are 3 states and a flag.
23617c478bd9Sstevel@tonic-gate 	 *
23627c478bd9Sstevel@tonic-gate 	 * In Non-Listener state, we simply don't have a membership record.
23637c478bd9Sstevel@tonic-gate 	 * In Delaying state, our timer is running (ilm->ilm_timer < INFINITY)
23647c478bd9Sstevel@tonic-gate 	 * In Idle Member state, our timer is not running (ilm->ilm_timer ==
23657c478bd9Sstevel@tonic-gate 	 * INFINITY)
23667c478bd9Sstevel@tonic-gate 	 *
23677c478bd9Sstevel@tonic-gate 	 * The flag is ilm->ilm_state, it is set to IGMP_OTHERMEMBER if
23687c478bd9Sstevel@tonic-gate 	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
23697c478bd9Sstevel@tonic-gate 	 * if I sent the last report.
23707c478bd9Sstevel@tonic-gate 	 */
23717c478bd9Sstevel@tonic-gate 	v6group = &mldh->mld_addr;
23727c478bd9Sstevel@tonic-gate 	if (!(IN6_IS_ADDR_UNSPECIFIED(v6group)) &&
23737c478bd9Sstevel@tonic-gate 	    ((!IN6_IS_ADDR_MULTICAST(v6group)))) {
23747c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembBadQueries);
23757c478bd9Sstevel@tonic-gate 		return (0);
23767c478bd9Sstevel@tonic-gate 	}
23777c478bd9Sstevel@tonic-gate 
23787c478bd9Sstevel@tonic-gate 	/* Need to do compatibility mode checking */
23797c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
23807c478bd9Sstevel@tonic-gate 	ill->ill_mcast_v1_time = 0;
23817c478bd9Sstevel@tonic-gate 	ill->ill_mcast_v1_tset = 1;
23827c478bd9Sstevel@tonic-gate 	if (ill->ill_mcast_type == MLD_V2_ROUTER) {
23837c478bd9Sstevel@tonic-gate 		ip1dbg(("Received MLDv1 Query on %s, switching mode to "
23847c478bd9Sstevel@tonic-gate 		    "MLD_V1_ROUTER\n", ill->ill_name));
23857c478bd9Sstevel@tonic-gate 		atomic_add_16(&ill->ill_ifptr->illif_mcast_v1, 1);
23867c478bd9Sstevel@tonic-gate 		ill->ill_mcast_type = MLD_V1_ROUTER;
23877c478bd9Sstevel@tonic-gate 	}
23887c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
23897c478bd9Sstevel@tonic-gate 
23907c478bd9Sstevel@tonic-gate 	timer = (int)ntohs(mldh->mld_maxdelay);
23917c478bd9Sstevel@tonic-gate 	if (ip_debug > 1) {
23927c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
23937c478bd9Sstevel@tonic-gate 		    "mld_input: TIMER = mld_maxdelay %d mld_type 0x%x",
23947c478bd9Sstevel@tonic-gate 		    timer, (int)mldh->mld_type);
23957c478bd9Sstevel@tonic-gate 	}
23967c478bd9Sstevel@tonic-gate 
23977c478bd9Sstevel@tonic-gate 	/*
23987c478bd9Sstevel@tonic-gate 	 * -Start the timers in all of our membership records for
23997c478bd9Sstevel@tonic-gate 	 * the physical interface on which the query arrived,
24007c478bd9Sstevel@tonic-gate 	 * excl:
24017c478bd9Sstevel@tonic-gate 	 *	1.  those that belong to the "all hosts" group,
24027c478bd9Sstevel@tonic-gate 	 *	2.  those with 0 scope, or 1 node-local scope.
24037c478bd9Sstevel@tonic-gate 	 *
24047c478bd9Sstevel@tonic-gate 	 * -Restart any timer that is already running but has a value
24057c478bd9Sstevel@tonic-gate 	 * longer that the requested timeout.
24067c478bd9Sstevel@tonic-gate 	 * -Use the value specified in the query message as the
24077c478bd9Sstevel@tonic-gate 	 * maximum timeout.
24087c478bd9Sstevel@tonic-gate 	 */
24097c478bd9Sstevel@tonic-gate 	next = INFINITY;
24108dc47d9fSudpa 
2411*e11c3f44Smeem 	ilm = ilm_walker_start(&ilw, ill);
2412*e11c3f44Smeem 	mutex_enter(&ill->ill_lock);
24138dc47d9fSudpa 	current = CURRENT_MSTIME;
2414*e11c3f44Smeem 
2415*e11c3f44Smeem 	for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
24167c478bd9Sstevel@tonic-gate 		ASSERT(!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr));
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr) ||
24197c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_MC_NODELOCAL(&ilm->ilm_v6addr) ||
24207c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_MC_RESERVED(&ilm->ilm_v6addr))
24217c478bd9Sstevel@tonic-gate 			continue;
24227c478bd9Sstevel@tonic-gate 		if ((!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr,
24237c478bd9Sstevel@tonic-gate 		    &ipv6_all_hosts_mcast)) &&
24247c478bd9Sstevel@tonic-gate 		    (IN6_IS_ADDR_UNSPECIFIED(v6group)) ||
24257c478bd9Sstevel@tonic-gate 		    (IN6_ARE_ADDR_EQUAL(v6group, &ilm->ilm_v6addr))) {
24267c478bd9Sstevel@tonic-gate 			if (timer == 0) {
24277c478bd9Sstevel@tonic-gate 				/* Respond immediately */
24287c478bd9Sstevel@tonic-gate 				ilm->ilm_timer = INFINITY;
24297c478bd9Sstevel@tonic-gate 				ilm->ilm_state = IGMP_IREPORTEDLAST;
24307c478bd9Sstevel@tonic-gate 				mutex_exit(&ill->ill_lock);
24317c478bd9Sstevel@tonic-gate 				mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
24327c478bd9Sstevel@tonic-gate 				mutex_enter(&ill->ill_lock);
24337c478bd9Sstevel@tonic-gate 				break;
24347c478bd9Sstevel@tonic-gate 			}
24357c478bd9Sstevel@tonic-gate 			if (ilm->ilm_timer > timer) {
24367c478bd9Sstevel@tonic-gate 				MCAST_RANDOM_DELAY(ilm->ilm_timer, timer);
24377c478bd9Sstevel@tonic-gate 				if (ilm->ilm_timer < next)
24387c478bd9Sstevel@tonic-gate 					next = ilm->ilm_timer;
24398dc47d9fSudpa 				ilm->ilm_timer += current;
24407c478bd9Sstevel@tonic-gate 			}
24417c478bd9Sstevel@tonic-gate 			break;
24427c478bd9Sstevel@tonic-gate 		}
24437c478bd9Sstevel@tonic-gate 	}
24447c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
2445*e11c3f44Smeem 	ilm_walker_finish(&ilw);
24467c478bd9Sstevel@tonic-gate 
24477c478bd9Sstevel@tonic-gate 	return (next);
24487c478bd9Sstevel@tonic-gate }
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate /*
24517c478bd9Sstevel@tonic-gate  * Handles an MLDv2 Listener Query.  On error, returns 0; on success,
24527c478bd9Sstevel@tonic-gate  * returns the appropriate (non-zero, unsigned) timer value (which may
24537c478bd9Sstevel@tonic-gate  * be INFINITY) to be set.
24547c478bd9Sstevel@tonic-gate  */
24557c478bd9Sstevel@tonic-gate static uint_t
24567c478bd9Sstevel@tonic-gate mldv2_query_in(mld2q_t *mld2q, ill_t *ill, int mldlen)
24577c478bd9Sstevel@tonic-gate {
24587c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
24597c478bd9Sstevel@tonic-gate 	in6_addr_t *v6group, *src_array;
24608dc47d9fSudpa 	uint_t	next, numsrc, i, mrd, delay, qqi, current;
24617c478bd9Sstevel@tonic-gate 	uint8_t	qrv;
2462*e11c3f44Smeem 	ilm_walker_t ilw;
24637c478bd9Sstevel@tonic-gate 
24647c478bd9Sstevel@tonic-gate 	v6group = &mld2q->mld2q_addr;
24657c478bd9Sstevel@tonic-gate 	numsrc = ntohs(mld2q->mld2q_numsrc);
24667c478bd9Sstevel@tonic-gate 
24677c478bd9Sstevel@tonic-gate 	/* make sure numsrc matches packet size */
24687c478bd9Sstevel@tonic-gate 	if (mldlen < MLD_V2_QUERY_MINLEN + (numsrc * sizeof (in6_addr_t))) {
24697c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
24707c478bd9Sstevel@tonic-gate 		return (0);
24717c478bd9Sstevel@tonic-gate 	}
24727c478bd9Sstevel@tonic-gate 	src_array = (in6_addr_t *)&mld2q[1];
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembQueries);
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate 	/* extract Maximum Response Delay from code in header */
24777c478bd9Sstevel@tonic-gate 	mrd = ntohs(mld2q->mld2q_mxrc);
24787c478bd9Sstevel@tonic-gate 	if (mrd >= MLD_V2_MAXRT_FPMIN) {
24797c478bd9Sstevel@tonic-gate 		uint_t hdrval, mant, exp;
24807c478bd9Sstevel@tonic-gate 		hdrval = mrd;
24817c478bd9Sstevel@tonic-gate 		mant = hdrval & MLD_V2_MAXRT_MANT_MASK;
24827c478bd9Sstevel@tonic-gate 		exp = (hdrval & MLD_V2_MAXRT_EXP_MASK) >> 12;
24837c478bd9Sstevel@tonic-gate 		mrd = (mant | 0x1000) << (exp + 3);
24847c478bd9Sstevel@tonic-gate 	}
24858dc47d9fSudpa 	if (mrd == 0)
24868dc47d9fSudpa 		mrd = DSEC_TO_MSEC(MCAST_DEF_QUERY_RESP_INTERVAL);
24878dc47d9fSudpa 
24887c478bd9Sstevel@tonic-gate 	MCAST_RANDOM_DELAY(delay, mrd);
24897c478bd9Sstevel@tonic-gate 	next = (unsigned)INFINITY;
24908dc47d9fSudpa 	current = CURRENT_MSTIME;
24917c478bd9Sstevel@tonic-gate 
24927c478bd9Sstevel@tonic-gate 	if ((qrv = mld2q->mld2q_sqrv & MLD_V2_RV_MASK) == 0)
24937c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = MCAST_DEF_ROBUSTNESS;
24947c478bd9Sstevel@tonic-gate 	else
24957c478bd9Sstevel@tonic-gate 		ill->ill_mcast_rv = qrv;
24967c478bd9Sstevel@tonic-gate 
24977c478bd9Sstevel@tonic-gate 	if ((qqi = (uint_t)mld2q->mld2q_qqic) >= MLD_V2_QQI_FPMIN) {
24987c478bd9Sstevel@tonic-gate 		uint_t mant, exp;
24997c478bd9Sstevel@tonic-gate 		mant = qqi & MLD_V2_QQI_MANT_MASK;
25007c478bd9Sstevel@tonic-gate 		exp = (qqi & MLD_V2_QQI_EXP_MASK) >> 12;
25017c478bd9Sstevel@tonic-gate 		qqi = (mant | 0x10) << (exp + 3);
25027c478bd9Sstevel@tonic-gate 	}
25037c478bd9Sstevel@tonic-gate 	ill->ill_mcast_qi = (qqi == 0) ? MCAST_DEF_QUERY_INTERVAL : qqi;
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	/*
25067c478bd9Sstevel@tonic-gate 	 * If we have a pending general query response that's scheduled
25077c478bd9Sstevel@tonic-gate 	 * sooner than the delay we calculated for this response, then
25087c478bd9Sstevel@tonic-gate 	 * no action is required (MLDv2 draft section 6.2 rule 1)
25097c478bd9Sstevel@tonic-gate 	 */
25107c478bd9Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
25118dc47d9fSudpa 	if (ill->ill_global_timer < (current + delay)) {
25127c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
25137c478bd9Sstevel@tonic-gate 		return (next);
25147c478bd9Sstevel@tonic-gate 	}
25157c478bd9Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
25167c478bd9Sstevel@tonic-gate 
25177c478bd9Sstevel@tonic-gate 	/*
25187c478bd9Sstevel@tonic-gate 	 * Now take action depending on query type: general,
25197c478bd9Sstevel@tonic-gate 	 * group specific, or group/source specific.
25207c478bd9Sstevel@tonic-gate 	 */
25217c478bd9Sstevel@tonic-gate 	if ((numsrc == 0) && IN6_IS_ADDR_UNSPECIFIED(v6group)) {
25227c478bd9Sstevel@tonic-gate 		/*
25237c478bd9Sstevel@tonic-gate 		 * general query
25247c478bd9Sstevel@tonic-gate 		 * We know global timer is either not running or is
25257c478bd9Sstevel@tonic-gate 		 * greater than our calculated delay, so reset it to
25267c478bd9Sstevel@tonic-gate 		 * our delay (random value in range [0, response time])
25277c478bd9Sstevel@tonic-gate 		 */
25287c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
25298dc47d9fSudpa 		ill->ill_global_timer = current + delay;
25307c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
25318dc47d9fSudpa 		next = delay;
25327c478bd9Sstevel@tonic-gate 
25337c478bd9Sstevel@tonic-gate 	} else {
25347c478bd9Sstevel@tonic-gate 		/* group or group/source specific query */
2535*e11c3f44Smeem 		ilm = ilm_walker_start(&ilw, ill);
25367c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
2537*e11c3f44Smeem 		for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
25387c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr) ||
25397c478bd9Sstevel@tonic-gate 			    IN6_IS_ADDR_MC_NODELOCAL(&ilm->ilm_v6addr) ||
25407c478bd9Sstevel@tonic-gate 			    IN6_IS_ADDR_MC_RESERVED(&ilm->ilm_v6addr) ||
25417c478bd9Sstevel@tonic-gate 			    !IN6_ARE_ADDR_EQUAL(v6group, &ilm->ilm_v6addr))
25427c478bd9Sstevel@tonic-gate 				continue;
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate 			/*
25457c478bd9Sstevel@tonic-gate 			 * If the query is group specific or we have a
25467c478bd9Sstevel@tonic-gate 			 * pending group specific query, the response is
25477c478bd9Sstevel@tonic-gate 			 * group specific (pending sources list should be
25487c478bd9Sstevel@tonic-gate 			 * empty).  Otherwise, need to update the pending
25497c478bd9Sstevel@tonic-gate 			 * sources list for the group and source specific
25507c478bd9Sstevel@tonic-gate 			 * response.
25517c478bd9Sstevel@tonic-gate 			 */
25527c478bd9Sstevel@tonic-gate 			if (numsrc == 0 || (ilm->ilm_timer < INFINITY &&
25537c478bd9Sstevel@tonic-gate 			    SLIST_IS_EMPTY(ilm->ilm_pendsrcs))) {
25547c478bd9Sstevel@tonic-gate group_query:
25557c478bd9Sstevel@tonic-gate 				FREE_SLIST(ilm->ilm_pendsrcs);
25567c478bd9Sstevel@tonic-gate 				ilm->ilm_pendsrcs = NULL;
25577c478bd9Sstevel@tonic-gate 			} else {
25587c478bd9Sstevel@tonic-gate 				boolean_t overflow;
25597c478bd9Sstevel@tonic-gate 				slist_t *pktl;
25607c478bd9Sstevel@tonic-gate 				if (numsrc > MAX_FILTER_SIZE ||
25617c478bd9Sstevel@tonic-gate 				    (ilm->ilm_pendsrcs == NULL &&
25627c478bd9Sstevel@tonic-gate 				    (ilm->ilm_pendsrcs = l_alloc()) == NULL)) {
25637c478bd9Sstevel@tonic-gate 					/*
25647c478bd9Sstevel@tonic-gate 					 * We've been sent more sources than
25657c478bd9Sstevel@tonic-gate 					 * we can deal with; or we can't deal
25667c478bd9Sstevel@tonic-gate 					 * with a source list at all. Revert
25677c478bd9Sstevel@tonic-gate 					 * to a group specific query.
25687c478bd9Sstevel@tonic-gate 					 */
25697c478bd9Sstevel@tonic-gate 					goto group_query;
25707c478bd9Sstevel@tonic-gate 				}
25717c478bd9Sstevel@tonic-gate 				if ((pktl = l_alloc()) == NULL)
25727c478bd9Sstevel@tonic-gate 					goto group_query;
25737c478bd9Sstevel@tonic-gate 				pktl->sl_numsrc = numsrc;
25747c478bd9Sstevel@tonic-gate 				for (i = 0; i < numsrc; i++)
25757c478bd9Sstevel@tonic-gate 					pktl->sl_addr[i] = src_array[i];
25767c478bd9Sstevel@tonic-gate 				l_union_in_a(ilm->ilm_pendsrcs, pktl,
25777c478bd9Sstevel@tonic-gate 				    &overflow);
25787c478bd9Sstevel@tonic-gate 				l_free(pktl);
25797c478bd9Sstevel@tonic-gate 				if (overflow)
25807c478bd9Sstevel@tonic-gate 					goto group_query;
25817c478bd9Sstevel@tonic-gate 			}
25828dc47d9fSudpa 			ilm->ilm_timer = (ilm->ilm_timer == INFINITY) ?
25838dc47d9fSudpa 			    INFINITY : (ilm->ilm_timer - current);
25847c478bd9Sstevel@tonic-gate 			/* set timer to soonest value */
25857c478bd9Sstevel@tonic-gate 			ilm->ilm_timer = MIN(ilm->ilm_timer, delay);
25867c478bd9Sstevel@tonic-gate 			if (ilm->ilm_timer < next)
25877c478bd9Sstevel@tonic-gate 				next = ilm->ilm_timer;
25888dc47d9fSudpa 			ilm->ilm_timer += current;
25897c478bd9Sstevel@tonic-gate 			break;
25907c478bd9Sstevel@tonic-gate 		}
25917c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
2592*e11c3f44Smeem 		ilm_walker_finish(&ilw);
25937c478bd9Sstevel@tonic-gate 	}
25947c478bd9Sstevel@tonic-gate 
25957c478bd9Sstevel@tonic-gate 	return (next);
25967c478bd9Sstevel@tonic-gate }
25977c478bd9Sstevel@tonic-gate 
25987c478bd9Sstevel@tonic-gate /*
25997c478bd9Sstevel@tonic-gate  * Send MLDv1 response packet with hoplimit 1
26007c478bd9Sstevel@tonic-gate  */
26017c478bd9Sstevel@tonic-gate static void
26027c478bd9Sstevel@tonic-gate mld_sendpkt(ilm_t *ilm, uchar_t type, const in6_addr_t *v6addr)
26037c478bd9Sstevel@tonic-gate {
26047c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
26057c478bd9Sstevel@tonic-gate 	mld_hdr_t	*mldh;
26067c478bd9Sstevel@tonic-gate 	ip6_t 		*ip6h;
26077c478bd9Sstevel@tonic-gate 	ip6_hbh_t	*ip6hbh;
26087c478bd9Sstevel@tonic-gate 	struct ip6_opt_router	*ip6router;
26097c478bd9Sstevel@tonic-gate 	size_t		size = IPV6_HDR_LEN + sizeof (mld_hdr_t);
2610*e11c3f44Smeem 	ill_t		*ill = ilm->ilm_ill;
26117c478bd9Sstevel@tonic-gate 	ipif_t		*ipif;
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 	/*
26147c478bd9Sstevel@tonic-gate 	 * We need to place a router alert option in this packet.  The length
26157c478bd9Sstevel@tonic-gate 	 * of the options must be a multiple of 8.  The hbh option header is 2
26167c478bd9Sstevel@tonic-gate 	 * bytes followed by the 4 byte router alert option.  That leaves
26177c478bd9Sstevel@tonic-gate 	 * 2 bytes of pad for a total of 8 bytes.
26187c478bd9Sstevel@tonic-gate 	 */
26197c478bd9Sstevel@tonic-gate 	const int	router_alert_length = 8;
26207c478bd9Sstevel@tonic-gate 
26217c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_isv6);
26227c478bd9Sstevel@tonic-gate 
2623*e11c3f44Smeem 	size += router_alert_length;
26247c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
26257c478bd9Sstevel@tonic-gate 	if (mp == NULL)
26267c478bd9Sstevel@tonic-gate 		return;
26277c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, size);
26287c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + size;
26297c478bd9Sstevel@tonic-gate 
2630*e11c3f44Smeem 	ip6h = (ip6_t *)mp->b_rptr;
26317c478bd9Sstevel@tonic-gate 	ip6hbh = (struct ip6_hbh *)&ip6h[1];
26327c478bd9Sstevel@tonic-gate 	ip6router = (struct ip6_opt_router *)&ip6hbh[1];
26337c478bd9Sstevel@tonic-gate 	/*
26347c478bd9Sstevel@tonic-gate 	 * A zero is a pad option of length 1.  The bzero of the whole packet
26357c478bd9Sstevel@tonic-gate 	 * above will pad between ip6router and mld.
26367c478bd9Sstevel@tonic-gate 	 */
26377c478bd9Sstevel@tonic-gate 	mldh = (mld_hdr_t *)((uint8_t *)ip6hbh + router_alert_length);
26387c478bd9Sstevel@tonic-gate 
26397c478bd9Sstevel@tonic-gate 	mldh->mld_type = type;
26407c478bd9Sstevel@tonic-gate 	mldh->mld_addr = ilm->ilm_v6addr;
26417c478bd9Sstevel@tonic-gate 
26427c478bd9Sstevel@tonic-gate 	ip6router->ip6or_type = IP6OPT_ROUTER_ALERT;
26437c478bd9Sstevel@tonic-gate 	ip6router->ip6or_len = 2;
26447c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[0] = 0;
26457c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[1] = IP6_ALERT_MLD;
26467c478bd9Sstevel@tonic-gate 
26477c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_nxt = IPPROTO_ICMPV6;
26487c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_len = 0;
26497c478bd9Sstevel@tonic-gate 
26507c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
26517c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = htons(sizeof (*mldh) + router_alert_length);
26527c478bd9Sstevel@tonic-gate 	ip6h->ip6_nxt = IPPROTO_HOPOPTS;
26537c478bd9Sstevel@tonic-gate 	ip6h->ip6_hops = MLD_HOP_LIMIT;
26547c478bd9Sstevel@tonic-gate 	if (v6addr == NULL)
26557c478bd9Sstevel@tonic-gate 		ip6h->ip6_dst =  ilm->ilm_v6addr;
26567c478bd9Sstevel@tonic-gate 	else
26577c478bd9Sstevel@tonic-gate 		ip6h->ip6_dst = *v6addr;
26587c478bd9Sstevel@tonic-gate 
26597c478bd9Sstevel@tonic-gate 	/* ipif returned by ipif_lookup_zoneid is link-local (if present) */
26607c478bd9Sstevel@tonic-gate 	if (ipif_lookup_zoneid(ill, ilm->ilm_zoneid, IPIF_UP, &ipif)) {
26617c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = ipif->ipif_v6src_addr;
26627c478bd9Sstevel@tonic-gate 		ipif_refrele(ipif);
26637c478bd9Sstevel@tonic-gate 	} else {
26647c478bd9Sstevel@tonic-gate 		/* Otherwise, use IPv6 default address selection. */
26657c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = ipv6_all_zeros;
26667c478bd9Sstevel@tonic-gate 	}
26677c478bd9Sstevel@tonic-gate 
26687c478bd9Sstevel@tonic-gate 	/*
26697c478bd9Sstevel@tonic-gate 	 * Prepare for checksum by putting icmp length in the icmp
26707c478bd9Sstevel@tonic-gate 	 * checksum field. The checksum is calculated in ip_wput_v6.
26717c478bd9Sstevel@tonic-gate 	 */
26727c478bd9Sstevel@tonic-gate 	mldh->mld_cksum = htons(sizeof (*mldh));
26737c478bd9Sstevel@tonic-gate 
26747c478bd9Sstevel@tonic-gate 	/*
26757c478bd9Sstevel@tonic-gate 	 * ip_wput will automatically loopback the multicast packet to
26767c478bd9Sstevel@tonic-gate 	 * the conn if multicast loopback is enabled.
26777c478bd9Sstevel@tonic-gate 	 * The MIB stats corresponding to this outgoing MLD packet
26787c478bd9Sstevel@tonic-gate 	 * will be accounted for in ip_wput->ip_wput_v6->ip_wput_ire_v6
26797c478bd9Sstevel@tonic-gate 	 * ->icmp_update_out_mib_v6 function call.
26807c478bd9Sstevel@tonic-gate 	 */
26817c478bd9Sstevel@tonic-gate 	(void) ip_output_v6(NULL, mp, ill->ill_wq, IP_WPUT);
26827c478bd9Sstevel@tonic-gate }
26837c478bd9Sstevel@tonic-gate 
26847c478bd9Sstevel@tonic-gate /*
26857c478bd9Sstevel@tonic-gate  * Sends an MLD_V2_LISTENER_REPORT message out the passed-in ill.  The
26867c478bd9Sstevel@tonic-gate  * report will contain one multicast address record for each element of
26877c478bd9Sstevel@tonic-gate  * reclist.  If this causes packet length to exceed ill->ill_max_frag,
26887c478bd9Sstevel@tonic-gate  * multiple reports are sent.  reclist is assumed to be made up of
26897c478bd9Sstevel@tonic-gate  * buffers allocated by mcast_bldmrec(), and those buffers are freed here.
26907c478bd9Sstevel@tonic-gate  */
26917c478bd9Sstevel@tonic-gate static void
26927c478bd9Sstevel@tonic-gate mldv2_sendrpt(ill_t *ill, mrec_t *reclist)
26937c478bd9Sstevel@tonic-gate {
26947c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
26957c478bd9Sstevel@tonic-gate 	mld2r_t		*mld2r;
26967c478bd9Sstevel@tonic-gate 	mld2mar_t	*mld2mar;
26977c478bd9Sstevel@tonic-gate 	in6_addr_t	*srcarray;
26987c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h;
26997c478bd9Sstevel@tonic-gate 	ip6_hbh_t	*ip6hbh;
27007c478bd9Sstevel@tonic-gate 	struct ip6_opt_router	*ip6router;
27017c478bd9Sstevel@tonic-gate 	size_t		size, optlen, padlen, icmpsize, rsize;
27027c478bd9Sstevel@tonic-gate 	ipif_t		*ipif;
27037c478bd9Sstevel@tonic-gate 	int		i, numrec, more_src_cnt;
27047c478bd9Sstevel@tonic-gate 	mrec_t		*rp, *cur_reclist;
27057c478bd9Sstevel@tonic-gate 	mrec_t		*next_reclist = reclist;
27067c478bd9Sstevel@tonic-gate 	boolean_t	morepkts;
27077c478bd9Sstevel@tonic-gate 
2708*e11c3f44Smeem 	ASSERT(IAM_WRITER_ILL(ill));
2709*e11c3f44Smeem 
27107c478bd9Sstevel@tonic-gate 	/* If there aren't any records, there's nothing to send */
27117c478bd9Sstevel@tonic-gate 	if (reclist == NULL)
27127c478bd9Sstevel@tonic-gate 		return;
27137c478bd9Sstevel@tonic-gate 
27147c478bd9Sstevel@tonic-gate 	ASSERT(ill->ill_isv6);
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate 	/*
27177c478bd9Sstevel@tonic-gate 	 * Total option length (optlen + padlen) must be a multiple of
27187c478bd9Sstevel@tonic-gate 	 * 8 bytes.  We assume here that optlen <= 8, so the total option
27197c478bd9Sstevel@tonic-gate 	 * length will be 8.  Assert this in case anything ever changes.
27207c478bd9Sstevel@tonic-gate 	 */
27217c478bd9Sstevel@tonic-gate 	optlen = sizeof (ip6_hbh_t) + sizeof (struct ip6_opt_router);
27227c478bd9Sstevel@tonic-gate 	ASSERT(optlen <= 8);
27237c478bd9Sstevel@tonic-gate 	padlen = 8 - optlen;
27247c478bd9Sstevel@tonic-gate nextpkt:
27257c478bd9Sstevel@tonic-gate 	icmpsize = sizeof (mld2r_t);
27267c478bd9Sstevel@tonic-gate 	size = IPV6_HDR_LEN + optlen + padlen + icmpsize;
27277c478bd9Sstevel@tonic-gate 	morepkts = B_FALSE;
27287c478bd9Sstevel@tonic-gate 	more_src_cnt = 0;
27297c478bd9Sstevel@tonic-gate 	for (rp = cur_reclist = next_reclist, numrec = 0; rp != NULL;
27307c478bd9Sstevel@tonic-gate 	    rp = rp->mrec_next, numrec++) {
27317c478bd9Sstevel@tonic-gate 		rsize = sizeof (mld2mar_t) +
27327c478bd9Sstevel@tonic-gate 		    (rp->mrec_srcs.sl_numsrc * sizeof (in6_addr_t));
27337c478bd9Sstevel@tonic-gate 		if (size + rsize > ill->ill_max_frag) {
27347c478bd9Sstevel@tonic-gate 			if (rp == cur_reclist) {
27357c478bd9Sstevel@tonic-gate 				/*
27367c478bd9Sstevel@tonic-gate 				 * If the first mrec we looked at is too big
27377c478bd9Sstevel@tonic-gate 				 * to fit in a single packet (i.e the source
27387c478bd9Sstevel@tonic-gate 				 * list is too big), we must either truncate
27397c478bd9Sstevel@tonic-gate 				 * the list (if TO_EX or IS_EX), or send
27407c478bd9Sstevel@tonic-gate 				 * multiple reports for the same group (all
27417c478bd9Sstevel@tonic-gate 				 * other types).
27427c478bd9Sstevel@tonic-gate 				 */
27437c478bd9Sstevel@tonic-gate 				int srcspace, srcsperpkt;
27447c478bd9Sstevel@tonic-gate 				srcspace = ill->ill_max_frag -
27457c478bd9Sstevel@tonic-gate 				    (size + sizeof (mld2mar_t));
2746*e11c3f44Smeem 
2747*e11c3f44Smeem 				/*
2748*e11c3f44Smeem 				 * Skip if there's not even enough room in
2749*e11c3f44Smeem 				 * a single packet to send something useful.
2750*e11c3f44Smeem 				 */
2751*e11c3f44Smeem 				if (srcspace <= sizeof (in6_addr_t))
2752*e11c3f44Smeem 					continue;
2753*e11c3f44Smeem 
27547c478bd9Sstevel@tonic-gate 				srcsperpkt = srcspace / sizeof (in6_addr_t);
27557c478bd9Sstevel@tonic-gate 				/*
27567c478bd9Sstevel@tonic-gate 				 * Increment icmpsize and size, because we will
27577c478bd9Sstevel@tonic-gate 				 * be sending a record for the mrec we're
27587c478bd9Sstevel@tonic-gate 				 * looking at now.
27597c478bd9Sstevel@tonic-gate 				 */
27607c478bd9Sstevel@tonic-gate 				rsize = sizeof (mld2mar_t) +
27617c478bd9Sstevel@tonic-gate 				    (srcsperpkt * sizeof (in6_addr_t));
27627c478bd9Sstevel@tonic-gate 				icmpsize += rsize;
27637c478bd9Sstevel@tonic-gate 				size += rsize;
27647c478bd9Sstevel@tonic-gate 				if (rp->mrec_type == MODE_IS_EXCLUDE ||
27657c478bd9Sstevel@tonic-gate 				    rp->mrec_type == CHANGE_TO_EXCLUDE) {
27667c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
27677c478bd9Sstevel@tonic-gate 					if (rp->mrec_next == NULL) {
27687c478bd9Sstevel@tonic-gate 						/* no more packets to send */
27697c478bd9Sstevel@tonic-gate 						break;
27707c478bd9Sstevel@tonic-gate 					} else {
27717c478bd9Sstevel@tonic-gate 						/*
27727c478bd9Sstevel@tonic-gate 						 * more packets, but we're
27737c478bd9Sstevel@tonic-gate 						 * done with this mrec.
27747c478bd9Sstevel@tonic-gate 						 */
27757c478bd9Sstevel@tonic-gate 						next_reclist = rp->mrec_next;
27767c478bd9Sstevel@tonic-gate 					}
27777c478bd9Sstevel@tonic-gate 				} else {
27787c478bd9Sstevel@tonic-gate 					more_src_cnt = rp->mrec_srcs.sl_numsrc
27797c478bd9Sstevel@tonic-gate 					    - srcsperpkt;
27807c478bd9Sstevel@tonic-gate 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
27817c478bd9Sstevel@tonic-gate 					/*
27827c478bd9Sstevel@tonic-gate 					 * We'll fix up this mrec (remove the
27837c478bd9Sstevel@tonic-gate 					 * srcs we've already sent) before
27847c478bd9Sstevel@tonic-gate 					 * returning to nextpkt above.
27857c478bd9Sstevel@tonic-gate 					 */
27867c478bd9Sstevel@tonic-gate 					next_reclist = rp;
27877c478bd9Sstevel@tonic-gate 				}
27887c478bd9Sstevel@tonic-gate 			} else {
27897c478bd9Sstevel@tonic-gate 				next_reclist = rp;
27907c478bd9Sstevel@tonic-gate 			}
27917c478bd9Sstevel@tonic-gate 			morepkts = B_TRUE;
27927c478bd9Sstevel@tonic-gate 			break;
27937c478bd9Sstevel@tonic-gate 		}
27947c478bd9Sstevel@tonic-gate 		icmpsize += rsize;
27957c478bd9Sstevel@tonic-gate 		size += rsize;
27967c478bd9Sstevel@tonic-gate 	}
27977c478bd9Sstevel@tonic-gate 
27987c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
27997c478bd9Sstevel@tonic-gate 	if (mp == NULL)
28007c478bd9Sstevel@tonic-gate 		goto free_reclist;
28017c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, size);
28027c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + size;
28037c478bd9Sstevel@tonic-gate 
2804*e11c3f44Smeem 	ip6h = (ip6_t *)mp->b_rptr;
28057c478bd9Sstevel@tonic-gate 	ip6hbh = (ip6_hbh_t *)&(ip6h[1]);
28067c478bd9Sstevel@tonic-gate 	ip6router = (struct ip6_opt_router *)&(ip6hbh[1]);
28077c478bd9Sstevel@tonic-gate 	mld2r = (mld2r_t *)((uint8_t *)ip6hbh + optlen + padlen);
28087c478bd9Sstevel@tonic-gate 	mld2mar = (mld2mar_t *)&(mld2r[1]);
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
28117c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = htons(optlen + padlen + icmpsize);
28127c478bd9Sstevel@tonic-gate 	ip6h->ip6_nxt = IPPROTO_HOPOPTS;
28137c478bd9Sstevel@tonic-gate 	ip6h->ip6_hops = MLD_HOP_LIMIT;
28147c478bd9Sstevel@tonic-gate 	ip6h->ip6_dst = ipv6_all_v2rtrs_mcast;
28157c478bd9Sstevel@tonic-gate 	/* ipif returned by ipif_lookup_zoneid is link-local (if present) */
28167c478bd9Sstevel@tonic-gate 	if (ipif_lookup_zoneid(ill, ALL_ZONES, IPIF_UP, &ipif)) {
28177c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = ipif->ipif_v6src_addr;
28187c478bd9Sstevel@tonic-gate 		ipif_refrele(ipif);
28197c478bd9Sstevel@tonic-gate 	} else {
28207c478bd9Sstevel@tonic-gate 		/* otherwise, use IPv6 default address selection. */
28217c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = ipv6_all_zeros;
28227c478bd9Sstevel@tonic-gate 	}
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_nxt = IPPROTO_ICMPV6;
28257c478bd9Sstevel@tonic-gate 	/*
28267c478bd9Sstevel@tonic-gate 	 * ip6h_len is the number of 8-byte words, not including the first
28277c478bd9Sstevel@tonic-gate 	 * 8 bytes; we've assumed optlen + padlen == 8 bytes; hence len = 0.
28287c478bd9Sstevel@tonic-gate 	 */
28297c478bd9Sstevel@tonic-gate 	ip6hbh->ip6h_len = 0;
28307c478bd9Sstevel@tonic-gate 
28317c478bd9Sstevel@tonic-gate 	ip6router->ip6or_type = IP6OPT_ROUTER_ALERT;
28327c478bd9Sstevel@tonic-gate 	ip6router->ip6or_len = 2;
28337c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[0] = 0;
28347c478bd9Sstevel@tonic-gate 	ip6router->ip6or_value[1] = IP6_ALERT_MLD;
28357c478bd9Sstevel@tonic-gate 
28367c478bd9Sstevel@tonic-gate 	mld2r->mld2r_type = MLD_V2_LISTENER_REPORT;
28377c478bd9Sstevel@tonic-gate 	mld2r->mld2r_nummar = htons(numrec);
28387c478bd9Sstevel@tonic-gate 	/*
28397c478bd9Sstevel@tonic-gate 	 * Prepare for the checksum by putting icmp length in the icmp
28407c478bd9Sstevel@tonic-gate 	 * checksum field. The checksum is calculated in ip_wput_v6.
28417c478bd9Sstevel@tonic-gate 	 */
28427c478bd9Sstevel@tonic-gate 	mld2r->mld2r_cksum = htons(icmpsize);
28437c478bd9Sstevel@tonic-gate 
28447c478bd9Sstevel@tonic-gate 	for (rp = cur_reclist; rp != NULL; rp = rp->mrec_next) {
28457c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_type = rp->mrec_type;
28467c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_auxlen = 0;
28477c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_numsrc = htons(rp->mrec_srcs.sl_numsrc);
28487c478bd9Sstevel@tonic-gate 		mld2mar->mld2mar_group = rp->mrec_group;
28497c478bd9Sstevel@tonic-gate 		srcarray = (in6_addr_t *)&(mld2mar[1]);
28507c478bd9Sstevel@tonic-gate 
28517c478bd9Sstevel@tonic-gate 		for (i = 0; i < rp->mrec_srcs.sl_numsrc; i++)
28527c478bd9Sstevel@tonic-gate 			srcarray[i] = rp->mrec_srcs.sl_addr[i];
28537c478bd9Sstevel@tonic-gate 
28547c478bd9Sstevel@tonic-gate 		mld2mar = (mld2mar_t *)&(srcarray[i]);
28557c478bd9Sstevel@tonic-gate 	}
28567c478bd9Sstevel@tonic-gate 
28577c478bd9Sstevel@tonic-gate 	/*
28587c478bd9Sstevel@tonic-gate 	 * ip_wput will automatically loopback the multicast packet to
28597c478bd9Sstevel@tonic-gate 	 * the conn if multicast loopback is enabled.
28607c478bd9Sstevel@tonic-gate 	 * The MIB stats corresponding to this outgoing MLD packet
28617c478bd9Sstevel@tonic-gate 	 * will be accounted for in ip_wput->ip_wput_v6->ip_wput_ire_v6
28627c478bd9Sstevel@tonic-gate 	 * ->icmp_update_out_mib_v6 function call.
28637c478bd9Sstevel@tonic-gate 	 */
28647c478bd9Sstevel@tonic-gate 	(void) ip_output_v6(NULL, mp, ill->ill_wq, IP_WPUT);
28657c478bd9Sstevel@tonic-gate 
28667c478bd9Sstevel@tonic-gate 	if (morepkts) {
28677c478bd9Sstevel@tonic-gate 		if (more_src_cnt > 0) {
28687c478bd9Sstevel@tonic-gate 			int index, mvsize;
28697c478bd9Sstevel@tonic-gate 			slist_t *sl = &next_reclist->mrec_srcs;
28707c478bd9Sstevel@tonic-gate 			index = sl->sl_numsrc;
28717c478bd9Sstevel@tonic-gate 			mvsize = more_src_cnt * sizeof (in6_addr_t);
28727c478bd9Sstevel@tonic-gate 			(void) memmove(&sl->sl_addr[0], &sl->sl_addr[index],
28737c478bd9Sstevel@tonic-gate 			    mvsize);
28747c478bd9Sstevel@tonic-gate 			sl->sl_numsrc = more_src_cnt;
28757c478bd9Sstevel@tonic-gate 		}
28767c478bd9Sstevel@tonic-gate 		goto nextpkt;
28777c478bd9Sstevel@tonic-gate 	}
28787c478bd9Sstevel@tonic-gate 
28797c478bd9Sstevel@tonic-gate free_reclist:
28807c478bd9Sstevel@tonic-gate 	while (reclist != NULL) {
28817c478bd9Sstevel@tonic-gate 		rp = reclist->mrec_next;
28827c478bd9Sstevel@tonic-gate 		mi_free(reclist);
28837c478bd9Sstevel@tonic-gate 		reclist = rp;
28847c478bd9Sstevel@tonic-gate 	}
28857c478bd9Sstevel@tonic-gate }
28867c478bd9Sstevel@tonic-gate 
28877c478bd9Sstevel@tonic-gate static mrec_t *
28887c478bd9Sstevel@tonic-gate mcast_bldmrec(mcast_record_t type, in6_addr_t *grp, slist_t *srclist,
28897c478bd9Sstevel@tonic-gate     mrec_t *next)
28907c478bd9Sstevel@tonic-gate {
28917c478bd9Sstevel@tonic-gate 	mrec_t *rp;
28927c478bd9Sstevel@tonic-gate 	int i;
28937c478bd9Sstevel@tonic-gate 
28947c478bd9Sstevel@tonic-gate 	if ((type == ALLOW_NEW_SOURCES || type == BLOCK_OLD_SOURCES) &&
28957c478bd9Sstevel@tonic-gate 	    SLIST_IS_EMPTY(srclist))
28967c478bd9Sstevel@tonic-gate 		return (next);
28977c478bd9Sstevel@tonic-gate 
28987c478bd9Sstevel@tonic-gate 	rp = (mrec_t *)mi_alloc(sizeof (mrec_t), BPRI_HI);
28997c478bd9Sstevel@tonic-gate 	if (rp == NULL)
29007c478bd9Sstevel@tonic-gate 		return (next);
29017c478bd9Sstevel@tonic-gate 
29027c478bd9Sstevel@tonic-gate 	rp->mrec_next = next;
29037c478bd9Sstevel@tonic-gate 	rp->mrec_type = type;
29047c478bd9Sstevel@tonic-gate 	rp->mrec_auxlen = 0;
29057c478bd9Sstevel@tonic-gate 	rp->mrec_group = *grp;
29067c478bd9Sstevel@tonic-gate 	if (srclist == NULL) {
29077c478bd9Sstevel@tonic-gate 		rp->mrec_srcs.sl_numsrc = 0;
29087c478bd9Sstevel@tonic-gate 	} else {
29097c478bd9Sstevel@tonic-gate 		rp->mrec_srcs.sl_numsrc = srclist->sl_numsrc;
29107c478bd9Sstevel@tonic-gate 		for (i = 0; i < srclist->sl_numsrc; i++)
29117c478bd9Sstevel@tonic-gate 			rp->mrec_srcs.sl_addr[i] = srclist->sl_addr[i];
29127c478bd9Sstevel@tonic-gate 	}
29137c478bd9Sstevel@tonic-gate 
29147c478bd9Sstevel@tonic-gate 	return (rp);
29157c478bd9Sstevel@tonic-gate }
29167c478bd9Sstevel@tonic-gate 
29177c478bd9Sstevel@tonic-gate /*
29187c478bd9Sstevel@tonic-gate  * Set up initial retransmit state.  If memory cannot be allocated for
29197c478bd9Sstevel@tonic-gate  * the source lists, simply create as much state as is possible; memory
29207c478bd9Sstevel@tonic-gate  * allocation failures are considered one type of transient error that
29217c478bd9Sstevel@tonic-gate  * the retransmissions are designed to overcome (and if they aren't
29227c478bd9Sstevel@tonic-gate  * transient, there are bigger problems than failing to notify the
29237c478bd9Sstevel@tonic-gate  * router about multicast group membership state changes).
29247c478bd9Sstevel@tonic-gate  */
29257c478bd9Sstevel@tonic-gate static void
29267c478bd9Sstevel@tonic-gate mcast_init_rtx(ill_t *ill, rtx_state_t *rtxp, mcast_record_t rtype,
29277c478bd9Sstevel@tonic-gate     slist_t *flist)
29287c478bd9Sstevel@tonic-gate {
29297c478bd9Sstevel@tonic-gate 	/*
29307c478bd9Sstevel@tonic-gate 	 * There are only three possibilities for rtype:
29317c478bd9Sstevel@tonic-gate 	 *	New join, transition from INCLUDE {} to INCLUDE {flist}
29327c478bd9Sstevel@tonic-gate 	 *	  => rtype is ALLOW_NEW_SOURCES
29337c478bd9Sstevel@tonic-gate 	 *	New join, transition from INCLUDE {} to EXCLUDE {flist}
29347c478bd9Sstevel@tonic-gate 	 *	  => rtype is CHANGE_TO_EXCLUDE
29357c478bd9Sstevel@tonic-gate 	 *	State change that involves a filter mode change
29367c478bd9Sstevel@tonic-gate 	 *	  => rtype is either CHANGE_TO_INCLUDE or CHANGE_TO_EXCLUDE
29377c478bd9Sstevel@tonic-gate 	 */
29387c478bd9Sstevel@tonic-gate 	ASSERT(rtype == CHANGE_TO_EXCLUDE || rtype == CHANGE_TO_INCLUDE ||
29397c478bd9Sstevel@tonic-gate 	    rtype == ALLOW_NEW_SOURCES);
29407c478bd9Sstevel@tonic-gate 
29417c478bd9Sstevel@tonic-gate 	rtxp->rtx_cnt = ill->ill_mcast_rv;
29427c478bd9Sstevel@tonic-gate 
29437c478bd9Sstevel@tonic-gate 	switch (rtype) {
29447c478bd9Sstevel@tonic-gate 	case CHANGE_TO_EXCLUDE:
29457c478bd9Sstevel@tonic-gate 		rtxp->rtx_fmode_cnt = ill->ill_mcast_rv;
29467c478bd9Sstevel@tonic-gate 		CLEAR_SLIST(rtxp->rtx_allow);
29477c478bd9Sstevel@tonic-gate 		COPY_SLIST(flist, rtxp->rtx_block);
29487c478bd9Sstevel@tonic-gate 		break;
29497c478bd9Sstevel@tonic-gate 	case ALLOW_NEW_SOURCES:
29507c478bd9Sstevel@tonic-gate 	case CHANGE_TO_INCLUDE:
29517c478bd9Sstevel@tonic-gate 		rtxp->rtx_fmode_cnt =
29527c478bd9Sstevel@tonic-gate 		    rtype == ALLOW_NEW_SOURCES ? 0 : ill->ill_mcast_rv;
29537c478bd9Sstevel@tonic-gate 		CLEAR_SLIST(rtxp->rtx_block);
29547c478bd9Sstevel@tonic-gate 		COPY_SLIST(flist, rtxp->rtx_allow);
29557c478bd9Sstevel@tonic-gate 		break;
29567c478bd9Sstevel@tonic-gate 	}
29577c478bd9Sstevel@tonic-gate }
29587c478bd9Sstevel@tonic-gate 
29597c478bd9Sstevel@tonic-gate /*
29607c478bd9Sstevel@tonic-gate  * The basic strategy here, as extrapolated from RFC 3810 section 6.1 and
29617c478bd9Sstevel@tonic-gate  * RFC 3376 section 5.1, covers three cases:
29627c478bd9Sstevel@tonic-gate  *	* The current state change is a filter mode change
29637c478bd9Sstevel@tonic-gate  *		Set filter mode retransmit counter; set retransmit allow or
29647c478bd9Sstevel@tonic-gate  *		block list to new source list as appropriate, and clear the
29657c478bd9Sstevel@tonic-gate  *		retransmit list that was not set; send TO_IN or TO_EX with
29667c478bd9Sstevel@tonic-gate  *		new source list.
29677c478bd9Sstevel@tonic-gate  *	* The current state change is a source list change, but the filter
29687c478bd9Sstevel@tonic-gate  *	  mode retransmit counter is > 0
29697c478bd9Sstevel@tonic-gate  *		Decrement filter mode retransmit counter; set retransmit
29707c478bd9Sstevel@tonic-gate  *		allow or block list to  new source list as appropriate,
29717c478bd9Sstevel@tonic-gate  *		and clear the retransmit list that was not set; send TO_IN
29727c478bd9Sstevel@tonic-gate  *		or TO_EX with new source list.
29737c478bd9Sstevel@tonic-gate  *	* The current state change is a source list change, and the filter
29747c478bd9Sstevel@tonic-gate  *	  mode retransmit counter is 0.
29757c478bd9Sstevel@tonic-gate  *		Merge existing rtx allow and block lists with new state:
29767c478bd9Sstevel@tonic-gate  *		  rtx_allow = (new allow + rtx_allow) - new block
29777c478bd9Sstevel@tonic-gate  *		  rtx_block = (new block + rtx_block) - new allow
29787c478bd9Sstevel@tonic-gate  *		Send ALLOW and BLOCK records for new retransmit lists;
29797c478bd9Sstevel@tonic-gate  *		decrement retransmit counter.
29807c478bd9Sstevel@tonic-gate  *
29817c478bd9Sstevel@tonic-gate  * As is the case for mcast_init_rtx(), memory allocation failures are
29827c478bd9Sstevel@tonic-gate  * acceptable; we just create as much state as we can.
29837c478bd9Sstevel@tonic-gate  */
29847c478bd9Sstevel@tonic-gate static mrec_t *
29857c478bd9Sstevel@tonic-gate mcast_merge_rtx(ilm_t *ilm, mrec_t *mreclist, slist_t *flist)
29867c478bd9Sstevel@tonic-gate {
29877c478bd9Sstevel@tonic-gate 	ill_t *ill;
29887c478bd9Sstevel@tonic-gate 	rtx_state_t *rtxp = &ilm->ilm_rtx;
29897c478bd9Sstevel@tonic-gate 	mcast_record_t txtype;
29907c478bd9Sstevel@tonic-gate 	mrec_t *rp, *rpnext, *rtnmrec;
29917c478bd9Sstevel@tonic-gate 	boolean_t ovf;
29927c478bd9Sstevel@tonic-gate 
29937c478bd9Sstevel@tonic-gate 	ill = (ilm->ilm_ill == NULL ? ilm->ilm_ipif->ipif_ill : ilm->ilm_ill);
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 	if (mreclist == NULL)
29967c478bd9Sstevel@tonic-gate 		return (mreclist);
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	/*
29997c478bd9Sstevel@tonic-gate 	 * A filter mode change is indicated by a single mrec, which is
30007c478bd9Sstevel@tonic-gate 	 * either TO_IN or TO_EX.  In this case, we just need to set new
30017c478bd9Sstevel@tonic-gate 	 * retransmit state as if this were an initial join.  There is
30027c478bd9Sstevel@tonic-gate 	 * no change to the mrec list.
30037c478bd9Sstevel@tonic-gate 	 */
30047c478bd9Sstevel@tonic-gate 	if (mreclist->mrec_type == CHANGE_TO_INCLUDE ||
30057c478bd9Sstevel@tonic-gate 	    mreclist->mrec_type == CHANGE_TO_EXCLUDE) {
30067c478bd9Sstevel@tonic-gate 		mcast_init_rtx(ill, rtxp, mreclist->mrec_type,
30077c478bd9Sstevel@tonic-gate 		    &mreclist->mrec_srcs);
30087c478bd9Sstevel@tonic-gate 		return (mreclist);
30097c478bd9Sstevel@tonic-gate 	}
30107c478bd9Sstevel@tonic-gate 
30117c478bd9Sstevel@tonic-gate 	/*
30127c478bd9Sstevel@tonic-gate 	 * Only the source list has changed
30137c478bd9Sstevel@tonic-gate 	 */
30147c478bd9Sstevel@tonic-gate 	rtxp->rtx_cnt = ill->ill_mcast_rv;
30157c478bd9Sstevel@tonic-gate 	if (rtxp->rtx_fmode_cnt > 0) {
30167c478bd9Sstevel@tonic-gate 		/* but we're still sending filter mode change reports */
30177c478bd9Sstevel@tonic-gate 		rtxp->rtx_fmode_cnt--;
30187c478bd9Sstevel@tonic-gate 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
30197c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_block);
30207c478bd9Sstevel@tonic-gate 			COPY_SLIST(flist, rtxp->rtx_allow);
30217c478bd9Sstevel@tonic-gate 			txtype = CHANGE_TO_INCLUDE;
30227c478bd9Sstevel@tonic-gate 		} else {
30237c478bd9Sstevel@tonic-gate 			CLEAR_SLIST(rtxp->rtx_allow);
30247c478bd9Sstevel@tonic-gate 			COPY_SLIST(flist, rtxp->rtx_block);
30257c478bd9Sstevel@tonic-gate 			txtype = CHANGE_TO_EXCLUDE;
30267c478bd9Sstevel@tonic-gate 		}
30277c478bd9Sstevel@tonic-gate 		/* overwrite first mrec with new info */
30287c478bd9Sstevel@tonic-gate 		mreclist->mrec_type = txtype;
30297c478bd9Sstevel@tonic-gate 		l_copy(flist, &mreclist->mrec_srcs);
30307c478bd9Sstevel@tonic-gate 		/* then free any remaining mrecs */
30317c478bd9Sstevel@tonic-gate 		for (rp = mreclist->mrec_next; rp != NULL; rp = rpnext) {
30327c478bd9Sstevel@tonic-gate 			rpnext = rp->mrec_next;
30337c478bd9Sstevel@tonic-gate 			mi_free(rp);
30347c478bd9Sstevel@tonic-gate 		}
30357c478bd9Sstevel@tonic-gate 		mreclist->mrec_next = NULL;
30367c478bd9Sstevel@tonic-gate 		rtnmrec = mreclist;
30377c478bd9Sstevel@tonic-gate 	} else {
30387c478bd9Sstevel@tonic-gate 		mrec_t *allow_mrec, *block_mrec;
30397c478bd9Sstevel@tonic-gate 		/*
30407c478bd9Sstevel@tonic-gate 		 * Just send the source change reports; but we need to
30417c478bd9Sstevel@tonic-gate 		 * recalculate the ALLOW and BLOCK lists based on previous
30427c478bd9Sstevel@tonic-gate 		 * state and new changes.
30437c478bd9Sstevel@tonic-gate 		 */
30447c478bd9Sstevel@tonic-gate 		rtnmrec = mreclist;
30457c478bd9Sstevel@tonic-gate 		allow_mrec = block_mrec = NULL;
30467c478bd9Sstevel@tonic-gate 		for (rp = mreclist; rp != NULL; rp = rp->mrec_next) {
30477c478bd9Sstevel@tonic-gate 			ASSERT(rp->mrec_type == ALLOW_NEW_SOURCES ||
30487c478bd9Sstevel@tonic-gate 			    rp->mrec_type == BLOCK_OLD_SOURCES);
30497c478bd9Sstevel@tonic-gate 			if (rp->mrec_type == ALLOW_NEW_SOURCES)
30507c478bd9Sstevel@tonic-gate 				allow_mrec = rp;
30517c478bd9Sstevel@tonic-gate 			else
30527c478bd9Sstevel@tonic-gate 				block_mrec = rp;
30537c478bd9Sstevel@tonic-gate 		}
30547c478bd9Sstevel@tonic-gate 		/*
30557c478bd9Sstevel@tonic-gate 		 * Perform calculations:
30567c478bd9Sstevel@tonic-gate 		 *   new_allow = mrec_allow + (rtx_allow - mrec_block)
30577c478bd9Sstevel@tonic-gate 		 *   new_block = mrec_block + (rtx_block - mrec_allow)
30587c478bd9Sstevel@tonic-gate 		 *
30597c478bd9Sstevel@tonic-gate 		 * Each calc requires two steps, for example:
30607c478bd9Sstevel@tonic-gate 		 *   rtx_allow = rtx_allow - mrec_block;
30617c478bd9Sstevel@tonic-gate 		 *   new_allow = mrec_allow + rtx_allow;
30627c478bd9Sstevel@tonic-gate 		 *
30637c478bd9Sstevel@tonic-gate 		 * Store results in mrec lists, and then copy into rtx lists.
30647c478bd9Sstevel@tonic-gate 		 * We do it in this order in case the rtx list hasn't been
30657c478bd9Sstevel@tonic-gate 		 * alloc'd yet; if it hasn't and our alloc fails, that's okay,
30667c478bd9Sstevel@tonic-gate 		 * Overflows are also okay.
30677c478bd9Sstevel@tonic-gate 		 */
30687c478bd9Sstevel@tonic-gate 		if (block_mrec != NULL) {
30697c478bd9Sstevel@tonic-gate 			l_difference_in_a(rtxp->rtx_allow,
30707c478bd9Sstevel@tonic-gate 			    &block_mrec->mrec_srcs);
30717c478bd9Sstevel@tonic-gate 		}
30727c478bd9Sstevel@tonic-gate 		if (allow_mrec != NULL) {
30737c478bd9Sstevel@tonic-gate 			l_difference_in_a(rtxp->rtx_block,
30747c478bd9Sstevel@tonic-gate 			    &allow_mrec->mrec_srcs);
30757c478bd9Sstevel@tonic-gate 			l_union_in_a(&allow_mrec->mrec_srcs, rtxp->rtx_allow,
30767c478bd9Sstevel@tonic-gate 			    &ovf);
30777c478bd9Sstevel@tonic-gate 		}
30787c478bd9Sstevel@tonic-gate 		if (block_mrec != NULL) {
30797c478bd9Sstevel@tonic-gate 			l_union_in_a(&block_mrec->mrec_srcs, rtxp->rtx_block,
30807c478bd9Sstevel@tonic-gate 			    &ovf);
30817c478bd9Sstevel@tonic-gate 			COPY_SLIST(&block_mrec->mrec_srcs, rtxp->rtx_block);
30827c478bd9Sstevel@tonic-gate 		} else {
30837c478bd9Sstevel@tonic-gate 			rtnmrec = mcast_bldmrec(BLOCK_OLD_SOURCES,
30847c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_block, allow_mrec);
30857c478bd9Sstevel@tonic-gate 		}
30867c478bd9Sstevel@tonic-gate 		if (allow_mrec != NULL) {
30877c478bd9Sstevel@tonic-gate 			COPY_SLIST(&allow_mrec->mrec_srcs, rtxp->rtx_allow);
30887c478bd9Sstevel@tonic-gate 		} else {
30897c478bd9Sstevel@tonic-gate 			rtnmrec = mcast_bldmrec(ALLOW_NEW_SOURCES,
30907c478bd9Sstevel@tonic-gate 			    &ilm->ilm_v6addr, rtxp->rtx_allow, block_mrec);
30917c478bd9Sstevel@tonic-gate 		}
30927c478bd9Sstevel@tonic-gate 	}
30937c478bd9Sstevel@tonic-gate 
30947c478bd9Sstevel@tonic-gate 	return (rtnmrec);
30957c478bd9Sstevel@tonic-gate }
3096*e11c3f44Smeem 
3097*e11c3f44Smeem /*
3098*e11c3f44Smeem  * Convenience routine to signal the restart-timer thread.
3099*e11c3f44Smeem  */
3100*e11c3f44Smeem static void
3101*e11c3f44Smeem mcast_signal_restart_thread(ip_stack_t *ipst)
3102*e11c3f44Smeem {
3103*e11c3f44Smeem 	mutex_enter(&ipst->ips_mrt_lock);
3104*e11c3f44Smeem 	ipst->ips_mrt_flags |= IP_MRT_RUN;
3105*e11c3f44Smeem 	cv_signal(&ipst->ips_mrt_cv);
3106*e11c3f44Smeem 	mutex_exit(&ipst->ips_mrt_lock);
3107*e11c3f44Smeem }
3108*e11c3f44Smeem 
3109*e11c3f44Smeem /*
3110*e11c3f44Smeem  * Thread to restart IGMP/MLD timers.  See the comment in igmp_joingroup() for
3111*e11c3f44Smeem  * the story behind this unfortunate thread.
3112*e11c3f44Smeem  */
3113*e11c3f44Smeem void
3114*e11c3f44Smeem mcast_restart_timers_thread(ip_stack_t *ipst)
3115*e11c3f44Smeem {
3116*e11c3f44Smeem 	int next;
3117*e11c3f44Smeem 	char name[64];
3118*e11c3f44Smeem 	callb_cpr_t cprinfo;
3119*e11c3f44Smeem 
3120*e11c3f44Smeem 	(void) snprintf(name, sizeof (name), "mcast_restart_timers_thread_%d",
3121*e11c3f44Smeem 	    ipst->ips_netstack->netstack_stackid);
3122*e11c3f44Smeem 	CALLB_CPR_INIT(&cprinfo, &ipst->ips_mrt_lock, callb_generic_cpr, name);
3123*e11c3f44Smeem 
3124*e11c3f44Smeem 	for (;;) {
3125*e11c3f44Smeem 		mutex_enter(&ipst->ips_mrt_lock);
3126*e11c3f44Smeem 		while (!(ipst->ips_mrt_flags & (IP_MRT_STOP|IP_MRT_RUN))) {
3127*e11c3f44Smeem 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
3128*e11c3f44Smeem 			cv_wait(&ipst->ips_mrt_cv, &ipst->ips_mrt_lock);
3129*e11c3f44Smeem 			CALLB_CPR_SAFE_END(&cprinfo, &ipst->ips_mrt_lock);
3130*e11c3f44Smeem 		}
3131*e11c3f44Smeem 		if (ipst->ips_mrt_flags & IP_MRT_STOP)
3132*e11c3f44Smeem 			break;
3133*e11c3f44Smeem 		ipst->ips_mrt_flags &= ~IP_MRT_RUN;
3134*e11c3f44Smeem 		mutex_exit(&ipst->ips_mrt_lock);
3135*e11c3f44Smeem 
3136*e11c3f44Smeem 		mutex_enter(&ipst->ips_igmp_timer_lock);
3137*e11c3f44Smeem 		next = ipst->ips_igmp_deferred_next;
3138*e11c3f44Smeem 		ipst->ips_igmp_deferred_next = INFINITY;
3139*e11c3f44Smeem 		mutex_exit(&ipst->ips_igmp_timer_lock);
3140*e11c3f44Smeem 
3141*e11c3f44Smeem 		if (next != INFINITY)
3142*e11c3f44Smeem 			igmp_start_timers(next, ipst);
3143*e11c3f44Smeem 
3144*e11c3f44Smeem 		mutex_enter(&ipst->ips_mld_timer_lock);
3145*e11c3f44Smeem 		next = ipst->ips_mld_deferred_next;
3146*e11c3f44Smeem 		ipst->ips_mld_deferred_next = INFINITY;
3147*e11c3f44Smeem 		mutex_exit(&ipst->ips_mld_timer_lock);
3148*e11c3f44Smeem 		if (next != INFINITY)
3149*e11c3f44Smeem 			mld_start_timers(next, ipst);
3150*e11c3f44Smeem 	}
3151*e11c3f44Smeem 
3152*e11c3f44Smeem 	ipst->ips_mrt_flags |= IP_MRT_DONE;
3153*e11c3f44Smeem 	cv_signal(&ipst->ips_mrt_done_cv);
3154*e11c3f44Smeem 	CALLB_CPR_EXIT(&cprinfo);	/* drops ips_mrt_lock */
3155*e11c3f44Smeem 	thread_exit();
3156*e11c3f44Smeem }
3157