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