Home | History | Annotate | Download | only in ip
      1      0    stevel /*
      2      0    stevel  * CDDL HEADER START
      3      0    stevel  *
      4      0    stevel  * The contents of this file are subject to the terms of the
      5   1676       jpk  * Common Development and Distribution License (the "License").
      6   1676       jpk  * You may not use this file except in compliance with the License.
      7      0    stevel  *
      8      0    stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9      0    stevel  * or http://www.opensolaris.org/os/licensing.
     10      0    stevel  * See the License for the specific language governing permissions
     11      0    stevel  * and limitations under the License.
     12      0    stevel  *
     13      0    stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14      0    stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15      0    stevel  * If applicable, add the following below this CDDL HEADER, with the
     16      0    stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17      0    stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18      0    stevel  *
     19      0    stevel  * CDDL HEADER END
     20      0    stevel  */
     21      0    stevel /*
     22   8485     Peter  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23      0    stevel  * Use is subject to license terms.
     24      0    stevel  */
     25      0    stevel /* Copyright (c) 1990 Mentat Inc. */
     26      0    stevel 
     27      0    stevel /*
     28      0    stevel  * Internet Group Management Protocol (IGMP) routines.
     29      0    stevel  * Multicast Listener Discovery Protocol (MLD) routines.
     30      0    stevel  *
     31      0    stevel  * Written by Steve Deering, Stanford, May 1988.
     32      0    stevel  * Modified by Rosen Sharma, Stanford, Aug 1994.
     33      0    stevel  * Modified by Bill Fenner, Xerox PARC, Feb. 1995.
     34      0    stevel  *
     35      0    stevel  * MULTICAST 3.5.1.1
     36      0    stevel  */
     37      0    stevel 
     38      0    stevel #include <sys/types.h>
     39      0    stevel #include <sys/stream.h>
     40      0    stevel #include <sys/stropts.h>
     41      0    stevel #include <sys/strlog.h>
     42      0    stevel #include <sys/strsun.h>
     43      0    stevel #include <sys/systm.h>
     44      0    stevel #include <sys/ddi.h>
     45      0    stevel #include <sys/sunddi.h>
     46      0    stevel #include <sys/cmn_err.h>
     47      0    stevel #include <sys/atomic.h>
     48      0    stevel #include <sys/zone.h>
     49   8485     Peter #include <sys/callb.h>
     50      0    stevel #include <sys/param.h>
     51      0    stevel #include <sys/socket.h>
     52      0    stevel #include <inet/ipclassifier.h>
     53      0    stevel #include <net/if.h>
     54      0    stevel #include <net/route.h>
     55      0    stevel #include <netinet/in.h>
     56      0    stevel #include <netinet/igmp_var.h>
     57      0    stevel #include <netinet/ip6.h>
     58      0    stevel #include <netinet/icmp6.h>
     59  11042      Erik #include <inet/ipsec_impl.h>
     60      0    stevel 
     61      0    stevel #include <inet/common.h>
     62      0    stevel #include <inet/mi.h>
     63      0    stevel #include <inet/nd.h>
     64      0    stevel #include <inet/ip.h>
     65      0    stevel #include <inet/ip6.h>
     66      0    stevel #include <inet/ip_multi.h>
     67      0    stevel #include <inet/ip_listutils.h>
     68      0    stevel 
     69      0    stevel #include <netinet/igmp.h>
     70  11042      Erik #include <inet/ip_ndp.h>
     71      0    stevel #include <inet/ip_if.h>
     72      0    stevel 
     73      0    stevel static uint_t	igmp_query_in(ipha_t *ipha, igmpa_t *igmpa, ill_t *ill);
     74      0    stevel static uint_t	igmpv3_query_in(igmp3qa_t *igmp3qa, ill_t *ill, int igmplen);
     75      0    stevel static uint_t	mld_query_in(mld_hdr_t *mldh, ill_t *ill);
     76      0    stevel static uint_t	mldv2_query_in(mld2q_t *mld2q, ill_t *ill, int mldlen);
     77      0    stevel static void	igmp_sendpkt(ilm_t *ilm, uchar_t type, ipaddr_t addr);
     78      0    stevel static void	mld_sendpkt(ilm_t *ilm, uchar_t type, const in6_addr_t *v6addr);
     79  11042      Erik static void	igmpv3_sendrpt(ill_t *ill, mrec_t *reclist);
     80      0    stevel static void	mldv2_sendrpt(ill_t *ill, mrec_t *reclist);
     81      0    stevel static mrec_t	*mcast_bldmrec(mcast_record_t type, in6_addr_t *grp,
     82      0    stevel 		    slist_t *srclist, mrec_t *next);
     83      0    stevel static void	mcast_init_rtx(ill_t *ill, rtx_state_t *rtxp,
     84      0    stevel 		    mcast_record_t rtype, slist_t *flist);
     85      0    stevel static mrec_t	*mcast_merge_rtx(ilm_t *ilm, mrec_t *rp, slist_t *flist);
     86      0    stevel 
     87      0    stevel /*
     88      0    stevel  * Macros used to do timer len conversions.  Timer values are always
     89      0    stevel  * stored and passed to the timer functions as milliseconds; but the
     90      0    stevel  * default values and values from the wire may not be.
     91      0    stevel  *
     92      0    stevel  * And yes, it's obscure, but decisecond is easier to abbreviate than
     93      0    stevel  * "tenths of a second".
     94      0    stevel  */
     95      0    stevel #define	DSEC_TO_MSEC(dsec)	((dsec) * 100)
     96      0    stevel #define	SEC_TO_MSEC(sec)	((sec) * 1000)
     97      0    stevel 
     98      0    stevel /*
     99   4783      udpa  * A running timer (scheduled thru timeout) can be cancelled if another
    100   4783      udpa  * timer with a shorter timeout value is scheduled before it has timed
    101   4783      udpa  * out.  When the shorter timer expires, the original timer is updated
    102   4783      udpa  * to account for the time elapsed while the shorter timer ran; but this
    103   4783      udpa  * does not take into account the amount of time already spent in timeout
    104   4783      udpa  * state before being preempted by the shorter timer, that is the time
    105   4783      udpa  * interval between time scheduled to time cancelled.  This can cause
    106   4783      udpa  * delays in sending out multicast membership reports.  To resolve this
    107   4783      udpa  * problem, wallclock time (absolute time) is used instead of deltas
    108   4783      udpa  * (relative time) to track timers.
    109   4783      udpa  *
    110   4783      udpa  * The MACRO below gets the lbolt value, used for proper timer scheduling
    111   4783      udpa  * and firing. Therefore multicast membership reports are sent on time.
    112   4783      udpa  * The timer does not exactly fire at the time it was scehduled to fire,
    113   4783      udpa  * there is a difference of a few milliseconds observed. An offset is used
    114   4783      udpa  * to take care of the difference.
    115   4783      udpa  */
    116   4783      udpa 
    117   4783      udpa #define	CURRENT_MSTIME	((uint_t)TICK_TO_MSEC(ddi_get_lbolt()))
    118   4783      udpa #define	CURRENT_OFFSET	(999)
    119   4783      udpa 
    120   4783      udpa /*
    121      0    stevel  * The first multicast join will trigger the igmp timers / mld timers
    122      0    stevel  * The unit for next is milliseconds.
    123      0    stevel  */
    124  11042      Erik void
    125   3448  dh155122 igmp_start_timers(unsigned next, ip_stack_t *ipst)
    126      0    stevel {
    127      0    stevel 	int	time_left;
    128      0    stevel 	int	ret;
    129  11042      Erik 	timeout_id_t id;
    130      0    stevel 
    131      0    stevel 	ASSERT(next != 0 && next != INFINITY);
    132      0    stevel 
    133   3448  dh155122 	mutex_enter(&ipst->ips_igmp_timer_lock);
    134      0    stevel 
    135   3448  dh155122 	if (ipst->ips_igmp_timer_setter_active) {
    136      0    stevel 		/*
    137      0    stevel 		 * Serialize timer setters, one at a time. If the
    138      0    stevel 		 * timer is currently being set by someone,
    139      0    stevel 		 * just record the next time when it has to be
    140      0    stevel 		 * invoked and return. The current setter will
    141      0    stevel 		 * take care.
    142      0    stevel 		 */
    143   3448  dh155122 		ipst->ips_igmp_time_to_next =
    144   3448  dh155122 		    MIN(ipst->ips_igmp_time_to_next, next);
    145   3448  dh155122 		mutex_exit(&ipst->ips_igmp_timer_lock);
    146      0    stevel 		return;
    147      0    stevel 	} else {
    148   3448  dh155122 		ipst->ips_igmp_timer_setter_active = B_TRUE;
    149      0    stevel 	}
    150   3448  dh155122 	if (ipst->ips_igmp_timeout_id == 0) {
    151      0    stevel 		/*
    152      0    stevel 		 * The timer is inactive. We need to start a timer
    153      0    stevel 		 */
    154   3448  dh155122 		ipst->ips_igmp_time_to_next = next;
    155   3448  dh155122 		ipst->ips_igmp_timeout_id = timeout(igmp_timeout_handler,
    156   3448  dh155122 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
    157   4783      udpa 		ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
    158   3448  dh155122 		ipst->ips_igmp_timer_setter_active = B_FALSE;
    159   3448  dh155122 		mutex_exit(&ipst->ips_igmp_timer_lock);
    160      0    stevel 		return;
    161      0    stevel 	}
    162      0    stevel 
    163      0    stevel 	/*
    164      0    stevel 	 * The timer was scheduled sometime back for firing in
    165      0    stevel 	 * 'igmp_time_to_next' ms and is active. We need to
    166      0    stevel 	 * reschedule the timeout if the new 'next' will happen
    167      0    stevel 	 * earlier than the currently scheduled timeout
    168      0    stevel 	 */
    169   4783      udpa 	time_left = ipst->ips_igmp_timer_scheduled_last +
    170   3448  dh155122 	    MSEC_TO_TICK(ipst->ips_igmp_time_to_next) - ddi_get_lbolt();
    171      0    stevel 	if (time_left < MSEC_TO_TICK(next)) {
    172   3448  dh155122 		ipst->ips_igmp_timer_setter_active = B_FALSE;
    173   3448  dh155122 		mutex_exit(&ipst->ips_igmp_timer_lock);
    174      0    stevel 		return;
    175      0    stevel 	}
    176  11042      Erik 	id = ipst->ips_igmp_timeout_id;
    177      0    stevel 
    178   3448  dh155122 	mutex_exit(&ipst->ips_igmp_timer_lock);
    179  11042      Erik 	ret = untimeout(id);
    180   3448  dh155122 	mutex_enter(&ipst->ips_igmp_timer_lock);
    181      0    stevel 	/*
    182      0    stevel 	 * The timeout was cancelled, or the timeout handler
    183      0    stevel 	 * completed, while we were blocked in the untimeout.
    184      0    stevel 	 * No other thread could have set the timer meanwhile
    185      0    stevel 	 * since we serialized all the timer setters. Thus
    186      0    stevel 	 * no timer is currently active nor executing nor will
    187      0    stevel 	 * any timer fire in the future. We start the timer now
    188      0    stevel 	 * if needed.
    189      0    stevel 	 */
    190      0    stevel 	if (ret == -1) {
    191   3448  dh155122 		ASSERT(ipst->ips_igmp_timeout_id == 0);
    192      0    stevel 	} else {
    193   3448  dh155122 		ASSERT(ipst->ips_igmp_timeout_id != 0);
    194   3448  dh155122 		ipst->ips_igmp_timeout_id = 0;
    195      0    stevel 	}
    196   3448  dh155122 	if (ipst->ips_igmp_time_to_next != 0) {
    197   3448  dh155122 		ipst->ips_igmp_time_to_next =
    198   3448  dh155122 		    MIN(ipst->ips_igmp_time_to_next, next);
    199   3448  dh155122 		ipst->ips_igmp_timeout_id = timeout(igmp_timeout_handler,
    200   3448  dh155122 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
    201   4783      udpa 		ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
    202      0    stevel 	}
    203   3448  dh155122 	ipst->ips_igmp_timer_setter_active = B_FALSE;
    204   3448  dh155122 	mutex_exit(&ipst->ips_igmp_timer_lock);
    205      0    stevel }
    206      0    stevel 
    207      0    stevel /*
    208      0    stevel  * mld_start_timers:
    209      0    stevel  * The unit for next is milliseconds.
    210      0    stevel  */
    211  11042      Erik void
    212   3448  dh155122 mld_start_timers(unsigned next, ip_stack_t *ipst)
    213      0    stevel {
    214      0    stevel 	int	time_left;
    215      0    stevel 	int	ret;
    216  11042      Erik 	timeout_id_t id;
    217      0    stevel 
    218      0    stevel 	ASSERT(next != 0 && next != INFINITY);
    219      0    stevel 
    220   3448  dh155122 	mutex_enter(&ipst->ips_mld_timer_lock);
    221   3448  dh155122 	if (ipst->ips_mld_timer_setter_active) {
    222      0    stevel 		/*
    223      0    stevel 		 * Serialize timer setters, one at a time. If the
    224      0    stevel 		 * timer is currently being set by someone,
    225      0    stevel 		 * just record the next time when it has to be
    226      0    stevel 		 * invoked and return. The current setter will
    227      0    stevel 		 * take care.
    228      0    stevel 		 */
    229   3448  dh155122 		ipst->ips_mld_time_to_next =
    230   3448  dh155122 		    MIN(ipst->ips_mld_time_to_next, next);
    231   3448  dh155122 		mutex_exit(&ipst->ips_mld_timer_lock);
    232      0    stevel 		return;
    233      0    stevel 	} else {
    234   3448  dh155122 		ipst->ips_mld_timer_setter_active = B_TRUE;
    235      0    stevel 	}
    236   3448  dh155122 	if (ipst->ips_mld_timeout_id == 0) {
    237      0    stevel 		/*
    238      0    stevel 		 * The timer is inactive. We need to start a timer
    239      0    stevel 		 */
    240   3448  dh155122 		ipst->ips_mld_time_to_next = next;
    241   3448  dh155122 		ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
    242   3448  dh155122 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_mld_time_to_next));
    243   4783      udpa 		ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
    244   3448  dh155122 		ipst->ips_mld_timer_setter_active = B_FALSE;
    245   3448  dh155122 		mutex_exit(&ipst->ips_mld_timer_lock);
    246      0    stevel 		return;
    247      0    stevel 	}
    248      0    stevel 
    249      0    stevel 	/*
    250      0    stevel 	 * The timer was scheduled sometime back for firing in
    251      0    stevel 	 * 'igmp_time_to_next' ms and is active. We need to
    252      0    stevel 	 * reschedule the timeout if the new 'next' will happen
    253      0    stevel 	 * earlier than the currently scheduled timeout
    254      0    stevel 	 */
    255   4783      udpa 	time_left = ipst->ips_mld_timer_scheduled_last +
    256   3448  dh155122 	    MSEC_TO_TICK(ipst->ips_mld_time_to_next) - ddi_get_lbolt();
    257      0    stevel 	if (time_left < MSEC_TO_TICK(next)) {
    258   3448  dh155122 		ipst->ips_mld_timer_setter_active = B_FALSE;
    259   3448  dh155122 		mutex_exit(&ipst->ips_mld_timer_lock);
    260      0    stevel 		return;
    261      0    stevel 	}
    262  11042      Erik 	id = ipst->ips_mld_timeout_id;
    263      0    stevel 
    264   3448  dh155122 	mutex_exit(&ipst->ips_mld_timer_lock);
    265  11042      Erik 	ret = untimeout(id);
    266   3448  dh155122 	mutex_enter(&ipst->ips_mld_timer_lock);
    267      0    stevel 	/*
    268      0    stevel 	 * The timeout was cancelled, or the timeout handler
    269      0    stevel 	 * completed, while we were blocked in the untimeout.
    270      0    stevel 	 * No other thread could have set the timer meanwhile
    271      0    stevel 	 * since we serialized all the timer setters. Thus
    272      0    stevel 	 * no timer is currently active nor executing nor will
    273      0    stevel 	 * any timer fire in the future. We start the timer now
    274      0    stevel 	 * if needed.
    275      0    stevel 	 */
    276      0    stevel 	if (ret == -1) {
    277   3448  dh155122 		ASSERT(ipst->ips_mld_timeout_id == 0);
    278      0    stevel 	} else {
    279   3448  dh155122 		ASSERT(ipst->ips_mld_timeout_id != 0);
    280   3448  dh155122 		ipst->ips_mld_timeout_id = 0;
    281      0    stevel 	}
    282   3448  dh155122 	if (ipst->ips_mld_time_to_next != 0) {
    283   3448  dh155122 		ipst->ips_mld_time_to_next =
    284   3448  dh155122 		    MIN(ipst->ips_mld_time_to_next, next);
    285   3448  dh155122 		ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
    286   3448  dh155122 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_mld_time_to_next));
    287   4783      udpa 		ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
    288      0    stevel 	}
    289   3448  dh155122 	ipst->ips_mld_timer_setter_active = B_FALSE;
    290   3448  dh155122 	mutex_exit(&ipst->ips_mld_timer_lock);
    291      0    stevel }
    292      0    stevel 
    293      0    stevel /*
    294      0    stevel  * igmp_input:
    295   2843  ethindra  * Return NULL for a bad packet that is discarded here.
    296   2843  ethindra  * Return mp if the message is OK and should be handed to "raw" receivers.
    297      0    stevel  * Callers of igmp_input() may need to reinitialize variables that were copied
    298      0    stevel  * from the mblk as this calls pullupmsg().
    299      0    stevel  */
    300   2843  ethindra mblk_t *
    301  11042      Erik igmp_input(mblk_t *mp, ip_recv_attr_t *ira)
    302      0    stevel {
    303      0    stevel 	igmpa_t 	*igmpa;
    304      0    stevel 	ipha_t		*ipha = (ipha_t *)(mp->b_rptr);
    305      0    stevel 	int		iphlen, igmplen, mblklen;
    306      0    stevel 	ilm_t 		*ilm;
    307      0    stevel 	uint32_t	src, dst;
    308      0    stevel 	uint32_t 	group;
    309  11042      Erik 	in6_addr_t	v6group;
    310      0    stevel 	uint_t		next;
    311      0    stevel 	ipif_t 		*ipif;
    312  11042      Erik 	ill_t		*ill = ira->ira_ill;
    313  11042      Erik 	ip_stack_t	*ipst = ill->ill_ipst;
    314      0    stevel 
    315      0    stevel 	ASSERT(!ill->ill_isv6);
    316   3448  dh155122 	++ipst->ips_igmpstat.igps_rcv_total;
    317      0    stevel 
    318      0    stevel 	mblklen = MBLKL(mp);
    319  11042      Erik 	iphlen = ira->ira_ip_hdr_length;
    320  11042      Erik 	if (mblklen < 1 || mblklen < iphlen) {
    321   3448  dh155122 		++ipst->ips_igmpstat.igps_rcv_tooshort;
    322   2843  ethindra 		goto bad_pkt;
    323      0    stevel 	}
    324  11042      Erik 	igmplen = ira->ira_pktlen - iphlen;
    325      0    stevel 	/*
    326      0    stevel 	 * Since msg sizes are more variable with v3, just pullup the
    327      0    stevel 	 * whole thing now.
    328      0    stevel 	 */
    329      0    stevel 	if (MBLKL(mp) < (igmplen + iphlen)) {
    330      0    stevel 		mblk_t *mp1;
    331      0    stevel 		if ((mp1 = msgpullup(mp, -1)) == NULL) {
    332   3448  dh155122 			++ipst->ips_igmpstat.igps_rcv_tooshort;
    333   2843  ethindra 			goto bad_pkt;
    334      0    stevel 		}
    335      0    stevel 		freemsg(mp);
    336      0    stevel 		mp = mp1;
    337      0    stevel 		ipha = (ipha_t *)(mp->b_rptr);
    338      0    stevel 	}
    339      0    stevel 
    340      0    stevel 	/*
    341      0    stevel 	 * Validate lengths
    342      0    stevel 	 */
    343      0    stevel 	if (igmplen < IGMP_MINLEN) {
    344   3448  dh155122 		++ipst->ips_igmpstat.igps_rcv_tooshort;
    345   2843  ethindra 		goto bad_pkt;
    346      0    stevel 	}
    347      0    stevel 
    348      0    stevel 	igmpa = (igmpa_t *)(&mp->b_rptr[iphlen]);
    349      0    stevel 	src = ipha->ipha_src;
    350      0    stevel 	dst = ipha->ipha_dst;
    351      0    stevel 	if (ip_debug > 1)
    352      0    stevel 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
    353      0    stevel 		    "igmp_input: src 0x%x, dst 0x%x on %s\n",
    354      0    stevel 		    (int)ntohl(src), (int)ntohl(dst),
    355      0    stevel 		    ill->ill_name);
    356      0    stevel 
    357      0    stevel 	switch (igmpa->igmpa_type) {
    358      0    stevel 	case IGMP_MEMBERSHIP_QUERY:
    359      0    stevel 		/*
    360      0    stevel 		 * packet length differentiates between v1/v2 and v3
    361      0    stevel 		 * v1/v2 should be exactly 8 octets long; v3 is >= 12
    362      0    stevel 		 */
    363   4783      udpa 		if ((igmplen == IGMP_MINLEN) ||
    364   4783      udpa 		    (ipst->ips_igmp_max_version <= IGMP_V2_ROUTER)) {
    365      0    stevel 			next = igmp_query_in(ipha, igmpa, ill);
    366      0    stevel 		} else if (igmplen >= IGMP_V3_QUERY_MINLEN) {
    367      0    stevel 			next = igmpv3_query_in((igmp3qa_t *)igmpa, ill,
    368      0    stevel 			    igmplen);
    369      0    stevel 		} else {
    370   3448  dh155122 			++ipst->ips_igmpstat.igps_rcv_tooshort;
    371   2843  ethindra 			goto bad_pkt;
    372      0    stevel 		}
    373   2843  ethindra 		if (next == 0)
    374   2843  ethindra 			goto bad_pkt;
    375      0    stevel 
    376      0    stevel 		if (next != INFINITY)
    377   3448  dh155122 			igmp_start_timers(next, ipst);
    378      0    stevel 
    379      0    stevel 		break;
    380      0    stevel 
    381      0    stevel 	case IGMP_V1_MEMBERSHIP_REPORT:
    382      0    stevel 	case IGMP_V2_MEMBERSHIP_REPORT:
    383      0    stevel 		/*
    384      0    stevel 		 * For fast leave to work, we have to know that we are the
    385      0    stevel 		 * last person to send a report for this group. Reports
    386      0    stevel 		 * generated by us are looped back since we could potentially
    387      0    stevel 		 * be a multicast router, so discard reports sourced by me.
    388      0    stevel 		 */
    389      0    stevel 		mutex_enter(&ill->ill_lock);
    390      0    stevel 		for (ipif = ill->ill_ipif; ipif != NULL;
    391      0    stevel 		    ipif = ipif->ipif_next) {
    392      0    stevel 			if (ipif->ipif_lcl_addr == src) {
    393      0    stevel 				if (ip_debug > 1) {
    394      0    stevel 					(void) mi_strlog(ill->ill_rq,
    395      0    stevel 					    1,
    396      0    stevel 					    SL_TRACE,
    397      0    stevel 					    "igmp_input: we are only "
    398  11042      Erik 					    "member src 0x%x\n",
    399  11042      Erik 					    (int)ntohl(src));
    400      0    stevel 				}
    401      0    stevel 				mutex_exit(&ill->ill_lock);
    402   2843  ethindra 				return (mp);
    403      0    stevel 			}
    404      0    stevel 		}
    405      0    stevel 		mutex_exit(&ill->ill_lock);
    406      0    stevel 
    407   3448  dh155122 		++ipst->ips_igmpstat.igps_rcv_reports;
    408      0    stevel 		group = igmpa->igmpa_group;
    409      0    stevel 		if (!CLASSD(group)) {
    410   3448  dh155122 			++ipst->ips_igmpstat.igps_rcv_badreports;
    411   2843  ethindra 			goto bad_pkt;
    412      0    stevel 		}
    413      0    stevel 
    414      0    stevel 		/*
    415      0    stevel 		 * KLUDGE: if the IP source address of the report has an
    416      0    stevel 		 * unspecified (i.e., zero) subnet number, as is allowed for
    417      0    stevel 		 * a booting host, replace it with the correct subnet number
    418      0    stevel 		 * so that a process-level multicast routing demon can
    419      0    stevel 		 * determine which subnet it arrived from.  This is necessary
    420      0    stevel 		 * to compensate for the lack of any way for a process to
    421      0    stevel 		 * determine the arrival interface of an incoming packet.
    422      0    stevel 		 *
    423      0    stevel 		 * Requires that a copy of *this* message it passed up
    424      0    stevel 		 * to the raw interface which is done by our caller.
    425      0    stevel 		 */
    426      0    stevel 		if ((src & htonl(0xFF000000U)) == 0) {	/* Minimum net mask */
    427      0    stevel 			/* Pick the first ipif on this ill */
    428      0    stevel 			mutex_enter(&ill->ill_lock);
    429      0    stevel 			src = ill->ill_ipif->ipif_subnet;
    430      0    stevel 			mutex_exit(&ill->ill_lock);
    431      0    stevel 			ip1dbg(("igmp_input: changed src to 0x%x\n",
    432      0    stevel 			    (int)ntohl(src)));
    433      0    stevel 			ipha->ipha_src = src;
    434      0    stevel 		}
    435      0    stevel 
    436      0    stevel 		/*
    437   8485     Peter 		 * If our ill has ILMs that belong to the group being
    438   8485     Peter 		 * reported, and we are a 'Delaying Member' in the RFC
    439   8485     Peter 		 * terminology, stop our timer for that group and 'clear
    440   8485     Peter 		 * flag' i.e. mark as IGMP_OTHERMEMBER.
    441      0    stevel 		 */
    442  11042      Erik 		rw_enter(&ill->ill_mcast_lock, RW_WRITER);
    443  11042      Erik 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
    444  11042      Erik 		for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
    445  11042      Erik 			if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, &v6group))
    446  11042      Erik 				continue;
    447  11042      Erik 
    448  11042      Erik 			++ipst->ips_igmpstat.igps_rcv_ourreports;
    449  11042      Erik 			ilm->ilm_timer = INFINITY;
    450  11042      Erik 			ilm->ilm_state = IGMP_OTHERMEMBER;
    451  11042      Erik 		} /* for */
    452  11042      Erik 		rw_exit(&ill->ill_mcast_lock);
    453  11042      Erik 		ill_mcast_timer_start(ill->ill_ipst);
    454      0    stevel 		break;
    455      0    stevel 
    456      0    stevel 	case IGMP_V3_MEMBERSHIP_REPORT:
    457      0    stevel 		/*
    458      0    stevel 		 * Currently nothing to do here; IGMP router is not
    459      0    stevel 		 * implemented in ip, and v3 hosts don't pay attention
    460      0    stevel 		 * to membership reports.
    461      0    stevel 		 */
    462      0    stevel 		break;
    463      0    stevel 	}
    464      0    stevel 	/*
    465      0    stevel 	 * Pass all valid IGMP packets up to any process(es) listening
    466      0    stevel 	 * on a raw IGMP socket. Do not free the packet.
    467      0    stevel 	 */
    468   2843  ethindra 	return (mp);
    469   2843  ethindra 
    470   2843  ethindra bad_pkt:
    471   2843  ethindra 	freemsg(mp);
    472   2843  ethindra 	return (NULL);
    473      0    stevel }
    474      0    stevel 
    475      0    stevel static uint_t
    476      0    stevel igmp_query_in(ipha_t *ipha, igmpa_t *igmpa, ill_t *ill)
    477      0    stevel {
    478      0    stevel 	ilm_t	*ilm;
    479      0    stevel 	int	timer;
    480   4783      udpa 	uint_t	next, current;
    481   3448  dh155122 	ip_stack_t	 *ipst;
    482      0    stevel 
    483   3448  dh155122 	ipst = ill->ill_ipst;
    484   3448  dh155122 	++ipst->ips_igmpstat.igps_rcv_queries;
    485      0    stevel 
    486  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
    487      0    stevel 	/*
    488      0    stevel 	 * In the IGMPv2 specification, there are 3 states and a flag.
    489      0    stevel 	 *
    490      0    stevel 	 * In Non-Member state, we simply don't have a membership record.
    491      0    stevel 	 * In Delaying Member state, our timer is running (ilm->ilm_timer
    492      0    stevel 	 * < INFINITY).  In Idle Member state, our timer is not running
    493      0    stevel 	 * (ilm->ilm_timer == INFINITY).
    494      0    stevel 	 *
    495      0    stevel 	 * The flag is ilm->ilm_state, it is set to IGMP_OTHERMEMBER if
    496      0    stevel 	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
    497      0    stevel 	 * if I sent the last report.
    498      0    stevel 	 */
    499   4783      udpa 	if ((igmpa->igmpa_code == 0) ||
    500   4783      udpa 	    (ipst->ips_igmp_max_version == IGMP_V1_ROUTER)) {
    501      0    stevel 		/*
    502      0    stevel 		 * Query from an old router.
    503      0    stevel 		 * Remember that the querier on this interface is old,
    504      0    stevel 		 * and set the timer to the value in RFC 1112.
    505      0    stevel 		 */
    506      0    stevel 		ill->ill_mcast_v1_time = 0;
    507      0    stevel 		ill->ill_mcast_v1_tset = 1;
    508      0    stevel 		if (ill->ill_mcast_type != IGMP_V1_ROUTER) {
    509      0    stevel 			ip1dbg(("Received IGMPv1 Query on %s, switching mode "
    510      0    stevel 			    "to IGMP_V1_ROUTER\n", ill->ill_name));
    511      0    stevel 			atomic_add_16(&ill->ill_ifptr->illif_mcast_v1, 1);
    512      0    stevel 			ill->ill_mcast_type = IGMP_V1_ROUTER;
    513      0    stevel 		}
    514      0    stevel 
    515      0    stevel 		timer = SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY);
    516      0    stevel 
    517      0    stevel 		if (ipha->ipha_dst != htonl(INADDR_ALLHOSTS_GROUP) ||
    518      0    stevel 		    igmpa->igmpa_group != 0) {
    519   3448  dh155122 			++ipst->ips_igmpstat.igps_rcv_badqueries;
    520  11042      Erik 			rw_exit(&ill->ill_mcast_lock);
    521  11042      Erik 			ill_mcast_timer_start(ill->ill_ipst);
    522      0    stevel 			return (0);
    523      0    stevel 		}
    524      0    stevel 
    525      0    stevel 	} else {
    526      0    stevel 		in_addr_t group;
    527      0    stevel 
    528      0    stevel 		/*
    529      0    stevel 		 * Query from a new router
    530      0    stevel 		 * Simply do a validity check
    531      0    stevel 		 */
    532      0    stevel 		group = igmpa->igmpa_group;
    533      0    stevel 		if (group != 0 && (!CLASSD(group))) {
    534   3448  dh155122 			++ipst->ips_igmpstat.igps_rcv_badqueries;
    535  11042      Erik 			rw_exit(&ill->ill_mcast_lock);
    536  11042      Erik 			ill_mcast_timer_start(ill->ill_ipst);
    537      0    stevel 			return (0);
    538      0    stevel 		}
    539      0    stevel 
    540      0    stevel 		/*
    541      0    stevel 		 * Switch interface state to v2 on receipt of a v2 query
    542      0    stevel 		 * ONLY IF current state is v3.  Let things be if current
    543      0    stevel 		 * state if v1 but do reset the v2-querier-present timer.
    544      0    stevel 		 */
    545      0    stevel 		if (ill->ill_mcast_type == IGMP_V3_ROUTER) {
    546      0    stevel 			ip1dbg(("Received IGMPv2 Query on %s, switching mode "
    547      0    stevel 			    "to IGMP_V2_ROUTER", ill->ill_name));
    548      0    stevel 			atomic_add_16(&ill->ill_ifptr->illif_mcast_v2, 1);
    549      0    stevel 			ill->ill_mcast_type = IGMP_V2_ROUTER;
    550      0    stevel 		}
    551      0    stevel 		ill->ill_mcast_v2_time = 0;
    552      0    stevel 		ill->ill_mcast_v2_tset = 1;
    553      0    stevel 
    554      0    stevel 		timer = DSEC_TO_MSEC((int)igmpa->igmpa_code);
    555      0    stevel 	}
    556      0    stevel 
    557      0    stevel 	if (ip_debug > 1) {
    558      0    stevel 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
    559      0    stevel 		    "igmp_input: TIMER = igmp_code %d igmp_type 0x%x",
    560      0    stevel 		    (int)ntohs(igmpa->igmpa_code),
    561      0    stevel 		    (int)ntohs(igmpa->igmpa_type));
    562      0    stevel 	}
    563      0    stevel 
    564      0    stevel 	/*
    565      0    stevel 	 * -Start the timers in all of our membership records
    566      0    stevel 	 *  for the physical interface on which the query
    567      0    stevel 	 *  arrived, excluding those that belong to the "all
    568      0    stevel 	 *  hosts" group (224.0.0.1).
    569      0    stevel 	 *
    570      0    stevel 	 * -Restart any timer that is already running but has
    571      0    stevel 	 *  a value longer than the requested timeout.
    572      0    stevel 	 *
    573      0    stevel 	 * -Use the value specified in the query message as
    574      0    stevel 	 *  the maximum timeout.
    575      0    stevel 	 */
    576      0    stevel 	next = (unsigned)INFINITY;
    577   8485     Peter 
    578   8485     Peter 	current = CURRENT_MSTIME;
    579  11042      Erik 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
    580   4783      udpa 
    581      0    stevel 		/*
    582      0    stevel 		 * A multicast router joins INADDR_ANY address
    583      0    stevel 		 * to enable promiscuous reception of all
    584      0    stevel 		 * mcasts from the interface. This INADDR_ANY
    585      0    stevel 		 * is stored in the ilm_v6addr as V6 unspec addr
    586      0    stevel 		 */
    587      0    stevel 		if (!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr))
    588      0    stevel 			continue;
    589      0    stevel 		if (ilm->ilm_addr == htonl(INADDR_ANY))
    590      0    stevel 			continue;
    591      0    stevel 		if (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP) &&
    592      0    stevel 		    (igmpa->igmpa_group == 0) ||
    593      0    stevel 		    (igmpa->igmpa_group == ilm->ilm_addr)) {
    594      0    stevel 			if (ilm->ilm_timer > timer) {
    595      0    stevel 				MCAST_RANDOM_DELAY(ilm->ilm_timer, timer);
    596      0    stevel 				if (ilm->ilm_timer < next)
    597      0    stevel 					next = ilm->ilm_timer;
    598   4783      udpa 				ilm->ilm_timer += current;
    599      0    stevel 			}
    600      0    stevel 		}
    601      0    stevel 	}
    602  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
    603  11042      Erik 	/*
    604  11042      Erik 	 * No packets have been sent above - no
    605  11042      Erik 	 * ill_mcast_send_queued is needed.
    606  11042      Erik 	 */
    607  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
    608      0    stevel 
    609      0    stevel 	return (next);
    610      0    stevel }
    611      0    stevel 
    612      0    stevel static uint_t
    613      0    stevel igmpv3_query_in(igmp3qa_t *igmp3qa, ill_t *ill, int igmplen)
    614      0    stevel {
    615      0    stevel 	uint_t		i, next, mrd, qqi, timer, delay, numsrc;
    616   4783      udpa 	uint_t		current;
    617      0    stevel 	ilm_t		*ilm;
    618      0    stevel 	ipaddr_t	*src_array;
    619      0    stevel 	uint8_t		qrv;
    620   3448  dh155122 	ip_stack_t	 *ipst;
    621      0    stevel 
    622   3448  dh155122 	ipst = ill->ill_ipst;
    623      0    stevel 	/* make sure numsrc matches packet size */
    624      0    stevel 	numsrc = ntohs(igmp3qa->igmp3qa_numsrc);
    625      0    stevel 	if (igmplen < IGMP_V3_QUERY_MINLEN + (numsrc * sizeof (ipaddr_t))) {
    626   3448  dh155122 		++ipst->ips_igmpstat.igps_rcv_tooshort;
    627      0    stevel 		return (0);
    628      0    stevel 	}
    629      0    stevel 	src_array = (ipaddr_t *)&igmp3qa[1];
    630      0    stevel 
    631   3448  dh155122 	++ipst->ips_igmpstat.igps_rcv_queries;
    632  11042      Erik 
    633  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
    634      0    stevel 
    635      0    stevel 	if ((mrd = (uint_t)igmp3qa->igmp3qa_mxrc) >= IGMP_V3_MAXRT_FPMIN) {
    636      0    stevel 		uint_t hdrval, mant, exp;
    637      0    stevel 		hdrval = (uint_t)igmp3qa->igmp3qa_mxrc;
    638      0    stevel 		mant = hdrval & IGMP_V3_MAXRT_MANT_MASK;
    639      0    stevel 		exp = (hdrval & IGMP_V3_MAXRT_EXP_MASK) >> 4;
    640      0    stevel 		mrd = (mant | 0x10) << (exp + 3);
    641      0    stevel 	}
    642      0    stevel 	if (mrd == 0)
    643      0    stevel 		mrd = MCAST_DEF_QUERY_RESP_INTERVAL;
    644      0    stevel 	timer = DSEC_TO_MSEC(mrd);
    645      0    stevel 	MCAST_RANDOM_DELAY(delay, timer);
    646      0    stevel 	next = (unsigned)INFINITY;
    647   4783      udpa 	current = CURRENT_MSTIME;
    648      0    stevel 
    649      0    stevel 	if ((qrv = igmp3qa->igmp3qa_sqrv & IGMP_V3_RV_MASK) == 0)
    650      0    stevel 		ill->ill_mcast_rv = MCAST_DEF_ROBUSTNESS;
    651      0    stevel 	else
    652      0    stevel 		ill->ill_mcast_rv = qrv;
    653      0    stevel 
    654      0    stevel 	if ((qqi = (uint_t)igmp3qa->igmp3qa_qqic) >= IGMP_V3_QQI_FPMIN) {
    655      0    stevel 		uint_t hdrval, mant, exp;
    656      0    stevel 		hdrval = (uint_t)igmp3qa->igmp3qa_qqic;
    657      0    stevel 		mant = hdrval & IGMP_V3_QQI_MANT_MASK;
    658      0    stevel 		exp = (hdrval & IGMP_V3_QQI_EXP_MASK) >> 4;
    659      0    stevel 		qqi = (mant | 0x10) << (exp + 3);
    660      0    stevel 	}
    661      0    stevel 	ill->ill_mcast_qi = (qqi == 0) ? MCAST_DEF_QUERY_INTERVAL : qqi;
    662      0    stevel 
    663      0    stevel 	/*
    664      0    stevel 	 * If we have a pending general query response that's scheduled
    665      0    stevel 	 * sooner than the delay we calculated for this response, then
    666      0    stevel 	 * no action is required (RFC3376 section 5.2 rule 1)
    667      0    stevel 	 */
    668   4783      udpa 	if (ill->ill_global_timer < (current + delay)) {
    669  11042      Erik 		rw_exit(&ill->ill_mcast_lock);
    670  11042      Erik 		ill_mcast_timer_start(ill->ill_ipst);
    671      0    stevel 		return (next);
    672      0    stevel 	}
    673      0    stevel 
    674      0    stevel 	/*
    675      0    stevel 	 * Now take action depending upon query type:
    676      0    stevel 	 * general, group specific, or group/source specific.
    677      0    stevel 	 */
    678      0    stevel 	if ((numsrc == 0) && (igmp3qa->igmp3qa_group == INADDR_ANY)) {
    679      0    stevel 		/*
    680      0    stevel 		 * general query
    681      0    stevel 		 * We know global timer is either not running or is
    682      0    stevel 		 * greater than our calculated delay, so reset it to
    683      0    stevel 		 * our delay (random value in range [0, response time]).
    684      0    stevel 		 */
    685   4783      udpa 		ill->ill_global_timer =  current + delay;
    686   4783      udpa 		next = delay;
    687      0    stevel 	} else {
    688      0    stevel 		/* group or group/source specific query */
    689  11042      Erik 		for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
    690      0    stevel 			if (!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr) ||
    691      0    stevel 			    (ilm->ilm_addr == htonl(INADDR_ANY)) ||
    692      0    stevel 			    (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP)) ||
    693      0    stevel 			    (igmp3qa->igmp3qa_group != ilm->ilm_addr))
    694      0    stevel 				continue;
    695      0    stevel 			/*
    696      0    stevel 			 * If the query is group specific or we have a
    697      0    stevel 			 * pending group specific query, the response is
    698      0    stevel 			 * group specific (pending sources list should be
    699      0    stevel 			 * empty).  Otherwise, need to update the pending
    700      0    stevel 			 * sources list for the group and source specific
    701      0    stevel 			 * response.
    702      0    stevel 			 */
    703      0    stevel 			if (numsrc == 0 || (ilm->ilm_timer < INFINITY &&
    704      0    stevel 			    SLIST_IS_EMPTY(ilm->ilm_pendsrcs))) {
    705      0    stevel group_query:
    706      0    stevel 				FREE_SLIST(ilm->ilm_pendsrcs);
    707      0    stevel 				ilm->ilm_pendsrcs = NULL;
    708      0    stevel 			} else {
    709      0    stevel 				boolean_t overflow;
    710      0    stevel 				slist_t *pktl;
    711      0    stevel 				if (numsrc > MAX_FILTER_SIZE ||
    712      0    stevel 				    (ilm->ilm_pendsrcs == NULL &&
    713      0    stevel 				    (ilm->ilm_pendsrcs = l_alloc()) == NULL)) {
    714      0    stevel 					/*
    715      0    stevel 					 * We've been sent more sources than
    716      0    stevel 					 * we can deal with; or we can't deal
    717      0    stevel 					 * with a source list at all.  Revert
    718      0    stevel 					 * to a group specific query.
    719      0    stevel 					 */
    720      0    stevel 					goto group_query;
    721      0    stevel 				}
    722      0    stevel 				if ((pktl = l_alloc()) == NULL)
    723      0    stevel 					goto group_query;
    724      0    stevel 				pktl->sl_numsrc = numsrc;
    725      0    stevel 				for (i = 0; i < numsrc; i++)
    726      0    stevel 					IN6_IPADDR_TO_V4MAPPED(src_array[i],
    727      0    stevel 					    &(pktl->sl_addr[i]));
    728      0    stevel 				l_union_in_a(ilm->ilm_pendsrcs, pktl,
    729      0    stevel 				    &overflow);
    730      0    stevel 				l_free(pktl);
    731      0    stevel 				if (overflow)
    732      0    stevel 					goto group_query;
    733      0    stevel 			}
    734   4783      udpa 
    735   4783      udpa 			ilm->ilm_timer = (ilm->ilm_timer == INFINITY) ?
    736   4783      udpa 			    INFINITY : (ilm->ilm_timer - current);
    737      0    stevel 			/* choose soonest timer */
    738      0    stevel 			ilm->ilm_timer = MIN(ilm->ilm_timer, delay);
    739      0    stevel 			if (ilm->ilm_timer < next)
    740      0    stevel 				next = ilm->ilm_timer;
    741   4783      udpa 			ilm->ilm_timer += current;
    742      0    stevel 		}
    743      0    stevel 	}
    744  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
    745  11042      Erik 	/*
    746  11042      Erik 	 * No packets have been sent above - no
    747  11042      Erik 	 * ill_mcast_send_queued is needed.
    748  11042      Erik 	 */
    749  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
    750      0    stevel 
    751      0    stevel 	return (next);
    752      0    stevel }
    753      0    stevel 
    754  11042      Erik /*
    755  11042      Erik  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
    756  11042      Erik  * and it gets sent after the lock is dropped.
    757  11042      Erik  */
    758      0    stevel void
    759      0    stevel igmp_joingroup(ilm_t *ilm)
    760      0    stevel {
    761   4783      udpa 	uint_t	timer;
    762      0    stevel 	ill_t	*ill;
    763   3448  dh155122 	ip_stack_t	*ipst = ilm->ilm_ipst;
    764      0    stevel 
    765  11042      Erik 	ill = ilm->ilm_ill;
    766      0    stevel 
    767  11042      Erik 	ASSERT(!ill->ill_isv6);
    768  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    769      0    stevel 
    770      0    stevel 	if (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP)) {
    771      0    stevel 		ilm->ilm_rtx.rtx_timer = INFINITY;
    772      0    stevel 		ilm->ilm_state = IGMP_OTHERMEMBER;
    773      0    stevel 	} else {
    774      0    stevel 		ip1dbg(("Querier mode %d, sending report, group %x\n",
    775      0    stevel 		    ill->ill_mcast_type, htonl(ilm->ilm_addr)));
    776      0    stevel 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
    777      0    stevel 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
    778      0    stevel 		} else if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
    779      0    stevel 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
    780      0    stevel 		} else if (ill->ill_mcast_type == IGMP_V3_ROUTER) {
    781      0    stevel 			mrec_t *rp;
    782      0    stevel 			mcast_record_t rtype;
    783      0    stevel 			/*
    784      0    stevel 			 * The possible state changes we need to handle here:
    785      0    stevel 			 *   Old State	New State	Report
    786      0    stevel 			 *
    787      0    stevel 			 *   INCLUDE(0)	INCLUDE(X)	ALLOW(X),BLOCK(0)
    788      0    stevel 			 *   INCLUDE(0)	EXCLUDE(X)	TO_EX(X)
    789      0    stevel 			 *
    790      0    stevel 			 * No need to send the BLOCK(0) report; ALLOW(X)
    791      0    stevel 			 * is enough.
    792      0    stevel 			 */
    793      0    stevel 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
    794      0    stevel 			    ALLOW_NEW_SOURCES : CHANGE_TO_EXCLUDE;
    795      0    stevel 			rp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
    796      0    stevel 			    ilm->ilm_filter, NULL);
    797  11042      Erik 			igmpv3_sendrpt(ill, rp);
    798      0    stevel 			/*
    799      0    stevel 			 * Set up retransmission state.  Timer is set below,
    800      0    stevel 			 * for both v3 and older versions.
    801      0    stevel 			 */
    802      0    stevel 			mcast_init_rtx(ill, &ilm->ilm_rtx, rtype,
    803      0    stevel 			    ilm->ilm_filter);
    804      0    stevel 		}
    805      0    stevel 
    806      0    stevel 		/* Set the ilm timer value */
    807   9472      Erik 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
    808      0    stevel 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
    809      0    stevel 		    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
    810   4783      udpa 		timer = ilm->ilm_rtx.rtx_timer;
    811   4783      udpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
    812      0    stevel 		ilm->ilm_state = IGMP_IREPORTEDLAST;
    813      0    stevel 
    814      0    stevel 		/*
    815  11042      Erik 		 * We are holding ill_mcast_lock here and the timeout
    816  11042      Erik 		 * handler (igmp_timeout_handler_per_ill) acquires that
    817  11077      Erik 		 * lock. Hence we can't call igmp_start_timers since it could
    818  11042      Erik 		 * deadlock in untimeout().
    819  11042      Erik 		 * Instead the thread which drops ill_mcast_lock will have
    820  11042      Erik 		 * to call ill_mcast_timer_start().
    821      0    stevel 		 */
    822   3448  dh155122 		mutex_enter(&ipst->ips_igmp_timer_lock);
    823   4783      udpa 		ipst->ips_igmp_deferred_next = MIN(timer,
    824   3448  dh155122 		    ipst->ips_igmp_deferred_next);
    825   3448  dh155122 		mutex_exit(&ipst->ips_igmp_timer_lock);
    826      0    stevel 	}
    827      0    stevel 
    828      0    stevel 	if (ip_debug > 1) {
    829  11042      Erik 		(void) mi_strlog(ilm->ilm_ill->ill_rq, 1, SL_TRACE,
    830      0    stevel 		    "igmp_joingroup: multicast_type %d timer %d",
    831  11042      Erik 		    (ilm->ilm_ill->ill_mcast_type),
    832   4783      udpa 		    (int)ntohl(timer));
    833      0    stevel 	}
    834      0    stevel }
    835      0    stevel 
    836  11042      Erik /*
    837  11042      Erik  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
    838  11042      Erik  * and it gets sent after the lock is dropped.
    839  11042      Erik  */
    840      0    stevel void
    841      0    stevel mld_joingroup(ilm_t *ilm)
    842      0    stevel {
    843   4783      udpa 	uint_t	timer;
    844      0    stevel 	ill_t	*ill;
    845   3448  dh155122 	ip_stack_t	*ipst = ilm->ilm_ipst;
    846      0    stevel 
    847      0    stevel 	ill = ilm->ilm_ill;
    848      0    stevel 
    849  11042      Erik 	ASSERT(ill->ill_isv6);
    850      0    stevel 
    851  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    852  11042      Erik 
    853      0    stevel 	if (IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr)) {
    854      0    stevel 		ilm->ilm_rtx.rtx_timer = INFINITY;
    855      0    stevel 		ilm->ilm_state = IGMP_OTHERMEMBER;
    856      0    stevel 	} else {
    857      0    stevel 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
    858      0    stevel 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
    859      0    stevel 		} else {
    860      0    stevel 			mrec_t *rp;
    861      0    stevel 			mcast_record_t rtype;
    862      0    stevel 			/*
    863      0    stevel 			 * The possible state changes we need to handle here:
    864      0    stevel 			 *	Old State   New State	Report
    865      0    stevel 			 *
    866      0    stevel 			 *	INCLUDE(0)  INCLUDE(X)	ALLOW(X),BLOCK(0)
    867      0    stevel 			 *	INCLUDE(0)  EXCLUDE(X)	TO_EX(X)
    868      0    stevel 			 *
    869      0    stevel 			 * No need to send the BLOCK(0) report; ALLOW(X)
    870      0    stevel 			 * is enough
    871      0    stevel 			 */
    872      0    stevel 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
    873      0    stevel 			    ALLOW_NEW_SOURCES : CHANGE_TO_EXCLUDE;
    874      0    stevel 			rp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
    875      0    stevel 			    ilm->ilm_filter, NULL);
    876      0    stevel 			mldv2_sendrpt(ill, rp);
    877      0    stevel 			/*
    878      0    stevel 			 * Set up retransmission state.  Timer is set below,
    879      0    stevel 			 * for both v2 and v1.
    880      0    stevel 			 */
    881      0    stevel 			mcast_init_rtx(ill, &ilm->ilm_rtx, rtype,
    882      0    stevel 			    ilm->ilm_filter);
    883      0    stevel 		}
    884      0    stevel 
    885      0    stevel 		/* Set the ilm timer value */
    886      0    stevel 		ASSERT(ill->ill_mcast_type != MLD_V2_ROUTER ||
    887      0    stevel 		    ilm->ilm_rtx.rtx_cnt > 0);
    888   9472      Erik 
    889   9472      Erik 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
    890      0    stevel 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
    891      0    stevel 		    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
    892   4783      udpa 		timer = ilm->ilm_rtx.rtx_timer;
    893   4783      udpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
    894      0    stevel 		ilm->ilm_state = IGMP_IREPORTEDLAST;
    895      0    stevel 
    896      0    stevel 		/*
    897  11042      Erik 		 * We are holding ill_mcast_lock here and the timeout
    898  11042      Erik 		 * handler (mld_timeout_handler_per_ill) acquires that
    899  11077      Erik 		 * lock. Hence we can't call mld_start_timers since it could
    900  11042      Erik 		 * deadlock in untimeout().
    901  11042      Erik 		 * Instead the thread which drops ill_mcast_lock will have
    902  11042      Erik 		 * to call ill_mcast_timer_start().
    903      0    stevel 		 */
    904   3448  dh155122 		mutex_enter(&ipst->ips_mld_timer_lock);
    905   4783      udpa 		ipst->ips_mld_deferred_next = MIN(timer,
    906   3448  dh155122 		    ipst->ips_mld_deferred_next);
    907   3448  dh155122 		mutex_exit(&ipst->ips_mld_timer_lock);
    908      0    stevel 	}
    909      0    stevel 
    910      0    stevel 	if (ip_debug > 1) {
    911      0    stevel 		(void) mi_strlog(ilm->ilm_ill->ill_rq, 1, SL_TRACE,
    912      0    stevel 		    "mld_joingroup: multicast_type %d timer %d",
    913      0    stevel 		    (ilm->ilm_ill->ill_mcast_type),
    914   4783      udpa 		    (int)ntohl(timer));
    915      0    stevel 	}
    916      0    stevel }
    917      0    stevel 
    918  11042      Erik /*
    919  11042      Erik  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
    920  11042      Erik  * and it gets sent after the lock is dropped.
    921  11042      Erik  */
    922      0    stevel void
    923      0    stevel igmp_leavegroup(ilm_t *ilm)
    924      0    stevel {
    925  11042      Erik 	ill_t *ill = ilm->ilm_ill;
    926      0    stevel 
    927      0    stevel 	ASSERT(!ill->ill_isv6);
    928      0    stevel 
    929  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    930      0    stevel 	if (ilm->ilm_state == IGMP_IREPORTEDLAST &&
    931      0    stevel 	    ill->ill_mcast_type == IGMP_V2_ROUTER &&
    932      0    stevel 	    (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP))) {
    933      0    stevel 		igmp_sendpkt(ilm, IGMP_V2_LEAVE_GROUP,
    934      0    stevel 		    (htonl(INADDR_ALLRTRS_GROUP)));
    935      0    stevel 		return;
    936  11042      Erik 	}
    937  11042      Erik 	if ((ill->ill_mcast_type == IGMP_V3_ROUTER) &&
    938      0    stevel 	    (ilm->ilm_addr != htonl(INADDR_ALLHOSTS_GROUP))) {
    939      0    stevel 		mrec_t *rp;
    940      0    stevel 		/*
    941      0    stevel 		 * The possible state changes we need to handle here:
    942      0    stevel 		 *	Old State	New State	Report
    943      0    stevel 		 *
    944      0    stevel 		 *	INCLUDE(X)	INCLUDE(0)	ALLOW(0),BLOCK(X)
    945      0    stevel 		 *	EXCLUDE(X)	INCLUDE(0)	TO_IN(0)
    946      0    stevel 		 *
    947      0    stevel 		 * No need to send the ALLOW(0) report; BLOCK(X) is enough
    948      0    stevel 		 */
    949      0    stevel 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
    950      0    stevel 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
    951      0    stevel 			    ilm->ilm_filter, NULL);
    952      0    stevel 		} else {
    953      0    stevel 			rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr,
    954      0    stevel 			    NULL, NULL);
    955      0    stevel 		}
    956  11042      Erik 		igmpv3_sendrpt(ill, rp);
    957      0    stevel 		return;
    958      0    stevel 	}
    959      0    stevel }
    960      0    stevel 
    961  11042      Erik /*
    962  11042      Erik  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
    963  11042      Erik  * and it gets sent after the lock is dropped.
    964  11042      Erik  */
    965      0    stevel void
    966      0    stevel mld_leavegroup(ilm_t *ilm)
    967      0    stevel {
    968      0    stevel 	ill_t *ill = ilm->ilm_ill;
    969      0    stevel 
    970      0    stevel 	ASSERT(ill->ill_isv6);
    971      0    stevel 
    972  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    973      0    stevel 	if (ilm->ilm_state == IGMP_IREPORTEDLAST &&
    974      0    stevel 	    ill->ill_mcast_type == MLD_V1_ROUTER &&
    975      0    stevel 	    (!IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr))) {
    976      0    stevel 		mld_sendpkt(ilm, MLD_LISTENER_REDUCTION, &ipv6_all_rtrs_mcast);
    977      0    stevel 		return;
    978  11042      Erik 	}
    979  11042      Erik 	if ((ill->ill_mcast_type == MLD_V2_ROUTER) &&
    980      0    stevel 	    (!IN6_ARE_ADDR_EQUAL(&ipv6_all_hosts_mcast, &ilm->ilm_v6addr))) {
    981      0    stevel 		mrec_t *rp;
    982      0    stevel 		/*
    983      0    stevel 		 * The possible state changes we need to handle here:
    984      0    stevel 		 *	Old State	New State	Report
    985      0    stevel 		 *
    986      0    stevel 		 *	INCLUDE(X)	INCLUDE(0)	ALLOW(0),BLOCK(X)
    987      0    stevel 		 *	EXCLUDE(X)	INCLUDE(0)	TO_IN(0)
    988      0    stevel 		 *
    989      0    stevel 		 * No need to send the ALLOW(0) report; BLOCK(X) is enough
    990      0    stevel 		 */
    991      0    stevel 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
    992      0    stevel 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
    993      0    stevel 			    ilm->ilm_filter, NULL);
    994      0    stevel 		} else {
    995      0    stevel 			rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr,
    996      0    stevel 			    NULL, NULL);
    997      0    stevel 		}
    998      0    stevel 		mldv2_sendrpt(ill, rp);
    999      0    stevel 		return;
   1000      0    stevel 	}
   1001      0    stevel }
   1002      0    stevel 
   1003  11042      Erik /*
   1004  11042      Erik  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
   1005  11042      Erik  * and it gets sent after the lock is dropped.
   1006  11042      Erik  */
   1007      0    stevel void
   1008      0    stevel igmp_statechange(ilm_t *ilm, mcast_record_t fmode, slist_t *flist)
   1009      0    stevel {
   1010      0    stevel 	ill_t *ill;
   1011      0    stevel 	mrec_t *rp;
   1012   3448  dh155122 	ip_stack_t	*ipst = ilm->ilm_ipst;
   1013      0    stevel 
   1014      0    stevel 	ASSERT(ilm != NULL);
   1015      0    stevel 
   1016      0    stevel 	/* state change reports should only be sent if the router is v3 */
   1017  11042      Erik 	if (ilm->ilm_ill->ill_mcast_type != IGMP_V3_ROUTER)
   1018      0    stevel 		return;
   1019      0    stevel 
   1020  11042      Erik 	ill = ilm->ilm_ill;
   1021  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
   1022      0    stevel 
   1023      0    stevel 	/*
   1024      0    stevel 	 * Compare existing(old) state with the new state and prepare
   1025      0    stevel 	 * State Change Report, according to the rules in RFC 3376:
   1026      0    stevel 	 *
   1027      0    stevel 	 *	Old State	New State	State Change Report
   1028      0    stevel 	 *
   1029      0    stevel 	 *	INCLUDE(A)	INCLUDE(B)	ALLOW(B-A),BLOCK(A-B)
   1030      0    stevel 	 *	EXCLUDE(A)	EXCLUDE(B)	ALLOW(A-B),BLOCK(B-A)
   1031      0    stevel 	 *	INCLUDE(A)	EXCLUDE(B)	TO_EX(B)
   1032      0    stevel 	 *	EXCLUDE(A)	INCLUDE(B)	TO_IN(B)
   1033      0    stevel 	 */
   1034      0    stevel 
   1035      0    stevel 	if (ilm->ilm_fmode == fmode) {
   1036      0    stevel 		slist_t	*a_minus_b = NULL, *b_minus_a = NULL;
   1037      0    stevel 		slist_t *allow, *block;
   1038      0    stevel 		if (((a_minus_b = l_alloc()) == NULL) ||
   1039      0    stevel 		    ((b_minus_a = l_alloc()) == NULL)) {
   1040      0    stevel 			l_free(a_minus_b);
   1041      0    stevel 			if (ilm->ilm_fmode == MODE_IS_INCLUDE)
   1042      0    stevel 				goto send_to_ex;
   1043      0    stevel 			else
   1044      0    stevel 				goto send_to_in;
   1045      0    stevel 		}
   1046      0    stevel 		l_difference(ilm->ilm_filter, flist, a_minus_b);
   1047      0    stevel 		l_difference(flist, ilm->ilm_filter, b_minus_a);
   1048      0    stevel 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
   1049      0    stevel 			allow = b_minus_a;
   1050      0    stevel 			block = a_minus_b;
   1051      0    stevel 		} else {
   1052      0    stevel 			allow = a_minus_b;
   1053      0    stevel 			block = b_minus_a;
   1054      0    stevel 		}
   1055      0    stevel 		rp = NULL;
   1056      0    stevel 		if (!SLIST_IS_EMPTY(allow))
   1057      0    stevel 			rp = mcast_bldmrec(ALLOW_NEW_SOURCES, &ilm->ilm_v6addr,
   1058      0    stevel 			    allow, rp);
   1059      0    stevel 		if (!SLIST_IS_EMPTY(block))
   1060      0    stevel 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
   1061      0    stevel 			    block, rp);
   1062      0    stevel 		l_free(a_minus_b);
   1063      0    stevel 		l_free(b_minus_a);
   1064      0    stevel 	} else if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
   1065      0    stevel send_to_ex:
   1066      0    stevel 		rp = mcast_bldmrec(CHANGE_TO_EXCLUDE, &ilm->ilm_v6addr, flist,
   1067      0    stevel 		    NULL);
   1068      0    stevel 	} else {
   1069      0    stevel send_to_in:
   1070      0    stevel 		rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr, flist,
   1071      0    stevel 		    NULL);
   1072      0    stevel 	}
   1073      0    stevel 
   1074      0    stevel 	/*
   1075      0    stevel 	 * Need to set up retransmission state; merge the new info with the
   1076      0    stevel 	 * current state (which may be null).  If the timer is not currently
   1077  11042      Erik 	 * running, the caller will start it when dropping ill_mcast_lock.
   1078      0    stevel 	 */
   1079      0    stevel 	rp = mcast_merge_rtx(ilm, rp, flist);
   1080      0    stevel 	if (ilm->ilm_rtx.rtx_timer == INFINITY) {
   1081   9472      Erik 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
   1082      0    stevel 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
   1083      0    stevel 		    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
   1084   3448  dh155122 		mutex_enter(&ipst->ips_igmp_timer_lock);
   1085   3448  dh155122 		ipst->ips_igmp_deferred_next = MIN(ipst->ips_igmp_deferred_next,
   1086      0    stevel 		    ilm->ilm_rtx.rtx_timer);
   1087   4783      udpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
   1088   3448  dh155122 		mutex_exit(&ipst->ips_igmp_timer_lock);
   1089      0    stevel 	}
   1090      0    stevel 
   1091  11042      Erik 	igmpv3_sendrpt(ill, rp);
   1092      0    stevel }
   1093      0    stevel 
   1094  11042      Erik /*
   1095  11042      Erik  * Caller holds ill_mcast_lock. We queue the packet using ill_mcast_queue
   1096  11042      Erik  * and it gets sent after the lock is dropped.
   1097  11042      Erik  */
   1098      0    stevel void
   1099      0    stevel mld_statechange(ilm_t *ilm, mcast_record_t fmode, slist_t *flist)
   1100      0    stevel {
   1101      0    stevel 	ill_t *ill;
   1102      0    stevel 	mrec_t *rp = NULL;
   1103   3448  dh155122 	ip_stack_t	*ipst = ilm->ilm_ipst;
   1104      0    stevel 
   1105      0    stevel 	ASSERT(ilm != NULL);
   1106      0    stevel 
   1107      0    stevel 	ill = ilm->ilm_ill;
   1108  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
   1109      0    stevel 
   1110      0    stevel 	/* only need to send if we have an mldv2-capable router */
   1111      0    stevel 	if (ill->ill_mcast_type != MLD_V2_ROUTER) {
   1112      0    stevel 		return;
   1113      0    stevel 	}
   1114      0    stevel 
   1115      0    stevel 	/*
   1116      0    stevel 	 * Compare existing (old) state with the new state passed in
   1117      0    stevel 	 * and send appropriate MLDv2 State Change Report.
   1118      0    stevel 	 *
   1119      0    stevel 	 *	Old State	New State	State Change Report
   1120      0    stevel 	 *
   1121      0    stevel 	 *	INCLUDE(A)	INCLUDE(B)	ALLOW(B-A),BLOCK(A-B)
   1122      0    stevel 	 *	EXCLUDE(A)	EXCLUDE(B)	ALLOW(A-B),BLOCK(B-A)
   1123      0    stevel 	 *	INCLUDE(A)	EXCLUDE(B)	TO_EX(B)
   1124      0    stevel 	 *	EXCLUDE(A)	INCLUDE(B)	TO_IN(B)
   1125      0    stevel 	 */
   1126      0    stevel 	if (ilm->ilm_fmode == fmode) {
   1127      0    stevel 		slist_t	*a_minus_b = NULL, *b_minus_a = NULL;
   1128      0    stevel 		slist_t *allow, *block;
   1129      0    stevel 		if (((a_minus_b = l_alloc()) == NULL) ||
   1130      0    stevel 		    ((b_minus_a = l_alloc()) == NULL)) {
   1131      0    stevel 			l_free(a_minus_b);
   1132      0    stevel 			if (ilm->ilm_fmode == MODE_IS_INCLUDE)
   1133      0    stevel 				goto send_to_ex;
   1134      0    stevel 			else
   1135      0    stevel 				goto send_to_in;
   1136      0    stevel 		}
   1137      0    stevel 		l_difference(ilm->ilm_filter, flist, a_minus_b);
   1138      0    stevel 		l_difference(flist, ilm->ilm_filter, b_minus_a);
   1139      0    stevel 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
   1140      0    stevel 			allow = b_minus_a;
   1141      0    stevel 			block = a_minus_b;
   1142      0    stevel 		} else {
   1143      0    stevel 			allow = a_minus_b;
   1144      0    stevel 			block = b_minus_a;
   1145      0    stevel 		}
   1146      0    stevel 		if (!SLIST_IS_EMPTY(allow))
   1147      0    stevel 			rp = mcast_bldmrec(ALLOW_NEW_SOURCES, &ilm->ilm_v6addr,
   1148      0    stevel 			    allow, rp);
   1149      0    stevel 		if (!SLIST_IS_EMPTY(block))
   1150      0    stevel 			rp = mcast_bldmrec(BLOCK_OLD_SOURCES, &ilm->ilm_v6addr,
   1151      0    stevel 			    block, rp);
   1152      0    stevel 		l_free(a_minus_b);
   1153      0    stevel 		l_free(b_minus_a);
   1154      0    stevel 	} else if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
   1155      0    stevel send_to_ex:
   1156      0    stevel 		rp = mcast_bldmrec(CHANGE_TO_EXCLUDE, &ilm->ilm_v6addr, flist,
   1157      0    stevel 		    NULL);
   1158      0    stevel 	} else {
   1159      0    stevel send_to_in:
   1160      0    stevel 		rp = mcast_bldmrec(CHANGE_TO_INCLUDE, &ilm->ilm_v6addr, flist,
   1161      0    stevel 		    NULL);
   1162      0    stevel 	}
   1163      0    stevel 
   1164      0    stevel 	/*
   1165      0    stevel 	 * Need to set up retransmission state; merge the new info with the
   1166      0    stevel 	 * current state (which may be null).  If the timer is not currently
   1167  11042      Erik 	 * running, the caller will start it when dropping ill_mcast_lock.
   1168      0    stevel 	 */
   1169      0    stevel 	rp = mcast_merge_rtx(ilm, rp, flist);
   1170      0    stevel 	ASSERT(ilm->ilm_rtx.rtx_cnt > 0);
   1171      0    stevel 	if (ilm->ilm_rtx.rtx_timer == INFINITY) {
   1172   9472      Erik 		ilm->ilm_rtx.rtx_cnt = ill->ill_mcast_rv;
   1173      0    stevel 		MCAST_RANDOM_DELAY(ilm->ilm_rtx.rtx_timer,
   1174      0    stevel 		    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
   1175   3448  dh155122 		mutex_enter(&ipst->ips_mld_timer_lock);
   1176   3448  dh155122 		ipst->ips_mld_deferred_next =
   1177   3448  dh155122 		    MIN(ipst->ips_mld_deferred_next, ilm->ilm_rtx.rtx_timer);
   1178   4783      udpa 		ilm->ilm_rtx.rtx_timer += CURRENT_MSTIME;
   1179   3448  dh155122 		mutex_exit(&ipst->ips_mld_timer_lock);
   1180      0    stevel 	}
   1181      0    stevel 
   1182      0    stevel 	mldv2_sendrpt(ill, rp);
   1183      0    stevel }
   1184      0    stevel 
   1185      0    stevel uint_t
   1186   4783      udpa igmp_timeout_handler_per_ill(ill_t *ill)
   1187      0    stevel {
   1188   4783      udpa 	uint_t	next = INFINITY, current;
   1189      0    stevel 	ilm_t	*ilm;
   1190      0    stevel 	mrec_t	*rp = NULL;
   1191      0    stevel 	mrec_t	*rtxrp = NULL;
   1192      0    stevel 	rtx_state_t *rtxp;
   1193      0    stevel 	mcast_record_t	rtype;
   1194      0    stevel 
   1195  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   1196      0    stevel 
   1197   4783      udpa 	current = CURRENT_MSTIME;
   1198      0    stevel 	/* First check the global timer on this interface */
   1199      0    stevel 	if (ill->ill_global_timer == INFINITY)
   1200      0    stevel 		goto per_ilm_timer;
   1201   4783      udpa 	if (ill->ill_global_timer <= (current + CURRENT_OFFSET)) {
   1202      0    stevel 		ill->ill_global_timer = INFINITY;
   1203      0    stevel 		/*
   1204      0    stevel 		 * Send report for each group on this interface.
   1205      0    stevel 		 * Since we just set the global timer (received a v3 general
   1206      0    stevel 		 * query), need to skip the all hosts addr (224.0.0.1), per
   1207      0    stevel 		 * RFC 3376 section 5.
   1208      0    stevel 		 */
   1209      0    stevel 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
   1210      0    stevel 			if (ilm->ilm_addr == htonl(INADDR_ALLHOSTS_GROUP))
   1211      0    stevel 				continue;
   1212  11042      Erik 			rp = mcast_bldmrec(ilm->ilm_fmode, &ilm->ilm_v6addr,
   1213  11042      Erik 			    ilm->ilm_filter, rp);
   1214      0    stevel 			/*
   1215      0    stevel 			 * Since we're sending a report on this group, okay
   1216      0    stevel 			 * to delete pending group-specific timers.  Note
   1217      0    stevel 			 * that group-specific retransmit timers still need
   1218      0    stevel 			 * to be checked in the per_ilm_timer for-loop.
   1219      0    stevel 			 */
   1220      0    stevel 			ilm->ilm_timer = INFINITY;
   1221      0    stevel 			ilm->ilm_state = IGMP_IREPORTEDLAST;
   1222      0    stevel 			FREE_SLIST(ilm->ilm_pendsrcs);
   1223      0    stevel 			ilm->ilm_pendsrcs = NULL;
   1224      0    stevel 		}
   1225  11042      Erik 		igmpv3_sendrpt(ill, rp);
   1226  11042      Erik 		rp = NULL;
   1227      0    stevel 	} else {
   1228   4783      udpa 		if ((ill->ill_global_timer - current) < next)
   1229   4783      udpa 			next = ill->ill_global_timer - current;
   1230      0    stevel 	}
   1231      0    stevel 
   1232      0    stevel per_ilm_timer:
   1233      0    stevel 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
   1234      0    stevel 		if (ilm->ilm_timer == INFINITY)
   1235      0    stevel 			goto per_ilm_rtxtimer;
   1236      0    stevel 
   1237   4783      udpa 		if (ilm->ilm_timer > (current + CURRENT_OFFSET)) {
   1238   4783      udpa 			if ((ilm->ilm_timer - current) < next)
   1239   4783      udpa 				next = ilm->ilm_timer - current;
   1240      0    stevel 
   1241      0    stevel 			if (ip_debug > 1) {
   1242      0    stevel 				(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
   1243   4783      udpa 				    "igmp_timo_hlr 2: ilm_timr %d "
   1244      0    stevel 				    "typ %d nxt %d",
   1245   4783      udpa 				    (int)ntohl(ilm->ilm_timer - current),
   1246      0    stevel 				    (ill->ill_mcast_type), next);
   1247      0    stevel 			}
   1248      0    stevel 
   1249      0    stevel 			goto per_ilm_rtxtimer;
   1250      0    stevel 		}
   1251      0    stevel 
   1252      0    stevel 		/* the timer has expired, need to take action */
   1253      0    stevel 		ilm->ilm_timer = INFINITY;
   1254      0    stevel 		ilm->ilm_state = IGMP_IREPORTEDLAST;
   1255      0    stevel 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
   1256      0    stevel 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
   1257      0    stevel 		} else if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
   1258      0    stevel 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
   1259      0    stevel 		} else {
   1260      0    stevel 			slist_t *rsp;
   1261      0    stevel 			if (!SLIST_IS_EMPTY(ilm->ilm_pendsrcs) &&
   1262      0    stevel 			    (rsp = l_alloc()) != NULL) {
   1263      0    stevel 				/*
   1264      0    stevel 				 * Contents of reply depend on pending
   1265      0    stevel 				 * requested source list.
   1266      0    stevel 				 */
   1267      0    stevel 				if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
   1268      0    stevel 					l_intersection(ilm->ilm_filter,
   1269      0    stevel 					    ilm->ilm_pendsrcs, rsp);
   1270      0    stevel 				} else {
   1271      0    stevel 					l_difference(ilm->ilm_pendsrcs,
   1272      0    stevel 					    ilm->ilm_filter, rsp);
   1273      0    stevel 				}
   1274      0    stevel 				FREE_SLIST(ilm->ilm_pendsrcs);
   1275      0    stevel 				ilm->ilm_pendsrcs = NULL;
   1276      0    stevel 				if (!SLIST_IS_EMPTY(rsp))
   1277      0    stevel 					rp = mcast_bldmrec(MODE_IS_INCLUDE,
   1278      0    stevel 					    &ilm->ilm_v6addr, rsp, rp);
   1279      0    stevel 				FREE_SLIST(rsp);
   1280      0    stevel 			} else {
   1281      0    stevel 				/*
   1282      0    stevel 				 * Either the pending request is just group-
   1283      0    stevel 				 * specific, or we couldn't get the resources
   1284      0    stevel 				 * (rsp) to build a source-specific reply.
   1285      0    stevel 				 */
   1286      0    stevel 				rp = mcast_bldmrec(ilm->ilm_fmode,
   1287      0    stevel 				    &ilm->ilm_v6addr, ilm->ilm_filter, rp);
   1288      0    stevel 			}
   1289  11042      Erik 			igmpv3_sendrpt(ill, rp);
   1290      0    stevel 			rp = NULL;
   1291      0    stevel 		}
   1292      0    stevel 
   1293      0    stevel per_ilm_rtxtimer:
   1294      0    stevel 		rtxp = &ilm->ilm_rtx;
   1295      0    stevel 
   1296      0    stevel 		if (rtxp->rtx_timer == INFINITY)
   1297      0    stevel 			continue;
   1298   4783      udpa 		if (rtxp->rtx_timer > (current + CURRENT_OFFSET)) {
   1299   4783      udpa 			if ((rtxp->rtx_timer - current) < next)
   1300   4783      udpa 				next = rtxp->rtx_timer - current;
   1301      0    stevel 			continue;
   1302      0    stevel 		}
   1303      0    stevel 
   1304      0    stevel 		rtxp->rtx_timer = INFINITY;
   1305      0    stevel 		ilm->ilm_state = IGMP_IREPORTEDLAST;
   1306      0    stevel 		if (ill->ill_mcast_type == IGMP_V1_ROUTER) {
   1307      0    stevel 			igmp_sendpkt(ilm, IGMP_V1_MEMBERSHIP_REPORT, 0);
   1308      0    stevel 			continue;
   1309  11042      Erik 		}
   1310  11042      Erik 		if (ill->ill_mcast_type == IGMP_V2_ROUTER) {
   1311      0    stevel 			igmp_sendpkt(ilm, IGMP_V2_MEMBERSHIP_REPORT, 0);
   1312      0    stevel 			continue;
   1313      0    stevel 		}
   1314      0    stevel 
   1315      0    stevel 		/*
   1316      0    stevel 		 * The retransmit timer has popped, and our router is
   1317      0    stevel 		 * IGMPv3.  We have to delve into the retransmit state
   1318      0    stevel 		 * stored in the ilm.
   1319      0    stevel 		 *
   1320      0    stevel 		 * Decrement the retransmit count.  If the fmode rtx
   1321      0    stevel 		 * count is active, decrement it, and send a filter
   1322      0    stevel 		 * mode change report with the ilm's source list.
   1323      0    stevel 		 * Otherwise, send a source list change report with
   1324      0    stevel 		 * the current retransmit lists.
   1325      0    stevel 		 */
   1326      0    stevel 		ASSERT(rtxp->rtx_cnt > 0);
   1327      0    stevel 		ASSERT(rtxp->rtx_cnt >= rtxp->rtx_fmode_cnt);
   1328      0    stevel 		rtxp->rtx_cnt--;
   1329      0    stevel 		if (rtxp->rtx_fmode_cnt > 0) {
   1330      0    stevel 			rtxp->rtx_fmode_cnt--;
   1331      0    stevel 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
   1332      0    stevel 			    CHANGE_TO_INCLUDE : CHANGE_TO_EXCLUDE;
   1333      0    stevel 			rtxrp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
   1334      0    stevel 			    ilm->ilm_filter, rtxrp);
   1335      0    stevel 		} else {
   1336      0    stevel 			rtxrp = mcast_bldmrec(ALLOW_NEW_SOURCES,
   1337      0    stevel 			    &ilm->ilm_v6addr, rtxp->rtx_allow, rtxrp);
   1338      0    stevel 			rtxrp = mcast_bldmrec(BLOCK_OLD_SOURCES,
   1339      0    stevel 			    &ilm->ilm_v6addr, rtxp->rtx_block, rtxrp);
   1340      0    stevel 		}
   1341      0    stevel 		if (rtxp->rtx_cnt > 0) {
   1342      0    stevel 			MCAST_RANDOM_DELAY(rtxp->rtx_timer,
   1343      0    stevel 			    SEC_TO_MSEC(IGMP_MAX_HOST_REPORT_DELAY));
   1344      0    stevel 			if (rtxp->rtx_timer < next)
   1345      0    stevel 				next = rtxp->rtx_timer;
   1346   4783      udpa 			rtxp->rtx_timer += current;
   1347      0    stevel 		} else {
   1348   9472      Erik 			ASSERT(rtxp->rtx_timer == INFINITY);
   1349      0    stevel 			CLEAR_SLIST(rtxp->rtx_allow);
   1350      0    stevel 			CLEAR_SLIST(rtxp->rtx_block);
   1351      0    stevel 		}
   1352  11042      Erik 		igmpv3_sendrpt(ill, rtxrp);
   1353      0    stevel 		rtxrp = NULL;
   1354      0    stevel 	}
   1355      0    stevel 
   1356  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   1357  11042      Erik 	/* Send any deferred/queued IP packets */
   1358  11042      Erik 	ill_mcast_send_queued(ill);
   1359  11042      Erik 	/* Defer ill_mcast_timer_start() until the caller is done */
   1360      0    stevel 
   1361      0    stevel 	return (next);
   1362      0    stevel }
   1363      0    stevel 
   1364      0    stevel /*
   1365      0    stevel  * igmp_timeout_handler:
   1366      0    stevel  * Called when there are timeout events, every next * TMEOUT_INTERVAL (tick).
   1367      0    stevel  * Returns number of ticks to next event (or 0 if none).
   1368      0    stevel  *
   1369      0    stevel  * As part of multicast join and leave igmp we may need to send out an
   1370      0    stevel  * igmp request. The igmp related state variables in the ilm are protected
   1371  11042      Erik  * by ill_mcast_lock. A single global igmp timer is used to track igmp timeouts.
   1372      0    stevel  * igmp_timer_lock protects the global igmp_timeout_id. igmp_start_timers
   1373      0    stevel  * starts the igmp timer if needed. It serializes multiple threads trying to
   1374      0    stevel  * simultaneously start the timer using the igmp_timer_setter_active flag.
   1375      0    stevel  *
   1376      0    stevel  * igmp_input() receives igmp queries and responds to the queries
   1377      0    stevel  * in a delayed fashion by posting a timer i.e. it calls igmp_start_timers().
   1378  11042      Erik  * Later the igmp_timer fires, the timeout handler igmp_timerout_handler()
   1379  11042      Erik  * performs the action exclusively after acquiring ill_mcast_lock.
   1380      0    stevel  *
   1381      0    stevel  * The igmp_slowtimeo() function is called thru another timer.
   1382      0    stevel  * igmp_slowtimeout_lock protects the igmp_slowtimeout_id
   1383      0    stevel  */
   1384      0    stevel void
   1385      0    stevel igmp_timeout_handler(void *arg)
   1386      0    stevel {
   1387      0    stevel 	ill_t	*ill;
   1388      0    stevel 	uint_t  global_next = INFINITY;
   1389      0    stevel 	uint_t  next;
   1390      0    stevel 	ill_walk_context_t ctx;
   1391   7098      meem 	ip_stack_t *ipst = arg;
   1392      0    stevel 
   1393   3448  dh155122 	ASSERT(arg != NULL);
   1394   3448  dh155122 	mutex_enter(&ipst->ips_igmp_timer_lock);
   1395   3448  dh155122 	ASSERT(ipst->ips_igmp_timeout_id != 0);
   1396  11042      Erik 	ipst->ips_igmp_timeout_id = 0;
   1397   4783      udpa 	ipst->ips_igmp_timer_scheduled_last = 0;
   1398   3448  dh155122 	ipst->ips_igmp_time_to_next = 0;
   1399   3448  dh155122 	mutex_exit(&ipst->ips_igmp_timer_lock);
   1400      0    stevel 
   1401   3448  dh155122 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1402   3448  dh155122 	ill = ILL_START_WALK_V4(&ctx, ipst);
   1403      0    stevel 	for (; ill != NULL; ill = ill_next(&ctx, ill)) {
   1404      0    stevel 		ASSERT(!ill->ill_isv6);
   1405  11042      Erik 		/* Make sure the ill isn't going away. */
   1406  11042      Erik 		if (!ill_check_and_refhold(ill))
   1407      0    stevel 			continue;
   1408   3448  dh155122 		rw_exit(&ipst->ips_ill_g_lock);
   1409  11042      Erik 		next = igmp_timeout_handler_per_ill(ill);
   1410  11042      Erik 		if (next < global_next)
   1411  11042      Erik 			global_next = next;
   1412  11042      Erik 		ill_refrele(ill);
   1413   3448  dh155122 		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1414      0    stevel 	}
   1415   3448  dh155122 	rw_exit(&ipst->ips_ill_g_lock);
   1416      0    stevel 	if (global_next != INFINITY)
   1417   3448  dh155122 		igmp_start_timers(global_next, ipst);
   1418      0    stevel }
   1419      0    stevel 
   1420      0    stevel /*
   1421      0    stevel  * mld_timeout_handler:
   1422      0    stevel  * Called when there are timeout events, every next (tick).
   1423      0    stevel  * Returns number of ticks to next event (or 0 if none).
   1424      0    stevel  */
   1425      0    stevel uint_t
   1426   4783      udpa mld_timeout_handler_per_ill(ill_t *ill)
   1427      0    stevel {
   1428      0    stevel 	ilm_t 	*ilm;
   1429   4783      udpa 	uint_t	next = INFINITY, current;
   1430      0    stevel 	mrec_t	*rp, *rtxrp;
   1431      0    stevel 	rtx_state_t *rtxp;
   1432      0    stevel 	mcast_record_t	rtype;
   1433      0    stevel 
   1434  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   1435      0    stevel 
   1436   4783      udpa 	current = CURRENT_MSTIME;
   1437      0    stevel 	/*
   1438      0    stevel 	 * First check the global timer on this interface; the global timer
   1439      0    stevel 	 * is not used for MLDv1, so if it's set we can assume we're v2.
   1440      0    stevel 	 */
   1441      0    stevel 	if (ill->ill_global_timer == INFINITY)
   1442      0    stevel 		goto per_ilm_timer;
   1443   4783      udpa 	if (ill->ill_global_timer <= (current + CURRENT_OFFSET)) {
   1444      0    stevel 		ill->ill_global_timer = INFINITY;
   1445      0    stevel 		/*
   1446      0    stevel 		 * Send report for each group on this interface.
   1447      0    stevel 		 * Since we just set the global timer (received a v2 general
   1448      0    stevel 		 * query), need to skip the all hosts addr (ff02::1), per
   1449      0    stevel 		 * RFC 3810 section 6.
   1450      0    stevel 		 */
   1451      0    stevel 		rp = NULL;
   1452      0    stevel 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
   1453      0    stevel 			if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr,
   1454      0    stevel 			    &ipv6_all_hosts_mcast))
   1455      0    stevel 				continue;
   1456      0    stevel 			rp = mcast_bldmrec(ilm->ilm_fmode, &ilm->ilm_v6addr,
   1457      0    stevel 			    ilm->ilm_filter, rp);
   1458      0    stevel 			/*
   1459      0    stevel 			 * Since we're sending a report on this group, okay
   1460      0    stevel 			 * to delete pending group-specific timers.  Note
   1461      0    stevel 			 * that group-specific retransmit timers still need
   1462      0    stevel 			 * to be checked in the per_ilm_timer for-loop.
   1463      0    stevel 			 */
   1464      0    stevel 			ilm->ilm_timer = INFINITY;
   1465      0    stevel 			ilm->ilm_state = IGMP_IREPORTEDLAST;
   1466      0    stevel 			FREE_SLIST(ilm->ilm_pendsrcs);
   1467      0    stevel 			ilm->ilm_pendsrcs = NULL;
   1468      0    stevel 		}
   1469      0    stevel 		mldv2_sendrpt(ill, rp);
   1470      0    stevel 	} else {
   1471   4783      udpa 		if ((ill->ill_global_timer - current) < next)
   1472   4783      udpa 			next = ill->ill_global_timer - current;
   1473      0    stevel 	}
   1474      0    stevel 
   1475      0    stevel per_ilm_timer:
   1476      0    stevel 	rp = rtxrp = NULL;
   1477      0    stevel 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
   1478      0    stevel 		if (ilm->ilm_timer == INFINITY)
   1479      0    stevel 			goto per_ilm_rtxtimer;
   1480      0    stevel 
   1481   4783      udpa 		if (ilm->ilm_timer > (current + CURRENT_OFFSET)) {
   1482   4783      udpa 			if ((ilm->ilm_timer - current) < next)
   1483   4783      udpa 				next = ilm->ilm_timer - current;
   1484      0    stevel 
   1485      0    stevel 			if (ip_debug > 1) {
   1486      0    stevel 				(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
   1487      0    stevel 				    "igmp_timo_hlr 2: ilm_timr"
   1488   4783      udpa 				    " %d typ %d nxt %d",
   1489   4783      udpa 				    (int)ntohl(ilm->ilm_timer - current),
   1490      0    stevel 				    (ill->ill_mcast_type), next);
   1491      0    stevel 			}
   1492      0    stevel 
   1493      0    stevel 			goto per_ilm_rtxtimer;
   1494      0    stevel 		}
   1495      0    stevel 
   1496      0    stevel 		/* the timer has expired, need to take action */
   1497      0    stevel 		ilm->ilm_timer = INFINITY;
   1498      0    stevel 		ilm->ilm_state = IGMP_IREPORTEDLAST;
   1499      0    stevel 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
   1500      0    stevel 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
   1501      0    stevel 		} else {
   1502      0    stevel 			slist_t *rsp;
   1503      0    stevel 			if (!SLIST_IS_EMPTY(ilm->ilm_pendsrcs) &&
   1504      0    stevel 			    (rsp = l_alloc()) != NULL) {
   1505      0    stevel 				/*
   1506      0    stevel 				 * Contents of reply depend on pending
   1507      0    stevel 				 * requested source list.
   1508      0    stevel 				 */
   1509      0    stevel 				if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
   1510      0    stevel 					l_intersection(ilm->ilm_filter,
   1511      0    stevel 					    ilm->ilm_pendsrcs, rsp);
   1512      0    stevel 				} else {
   1513      0    stevel 					l_difference(ilm->ilm_pendsrcs,
   1514      0    stevel 					    ilm->ilm_filter, rsp);
   1515      0    stevel 				}
   1516      0    stevel 				FREE_SLIST(ilm->ilm_pendsrcs);
   1517      0    stevel 				ilm->ilm_pendsrcs = NULL;
   1518      0    stevel 				if (!SLIST_IS_EMPTY(rsp))
   1519      0    stevel 					rp = mcast_bldmrec(MODE_IS_INCLUDE,
   1520      0    stevel 					    &ilm->ilm_v6addr, rsp, rp);
   1521      0    stevel 				FREE_SLIST(rsp);
   1522      0    stevel 			} else {
   1523      0    stevel 				rp = mcast_bldmrec(ilm->ilm_fmode,
   1524      0    stevel 				    &ilm->ilm_v6addr, ilm->ilm_filter, rp);
   1525      0    stevel 			}
   1526      0    stevel 		}
   1527      0    stevel 
   1528      0    stevel per_ilm_rtxtimer:
   1529      0    stevel 		rtxp = &ilm->ilm_rtx;
   1530      0    stevel 
   1531      0    stevel 		if (rtxp->rtx_timer == INFINITY)
   1532      0    stevel 			continue;
   1533   4783      udpa 		if (rtxp->rtx_timer > (current + CURRENT_OFFSET)) {
   1534   4783      udpa 			if ((rtxp->rtx_timer - current) < next)
   1535   4783      udpa 				next = rtxp->rtx_timer - current;
   1536      0    stevel 			continue;
   1537      0    stevel 		}
   1538      0    stevel 
   1539      0    stevel 		rtxp->rtx_timer = INFINITY;
   1540      0    stevel 		ilm->ilm_state = IGMP_IREPORTEDLAST;
   1541      0    stevel 		if (ill->ill_mcast_type == MLD_V1_ROUTER) {
   1542      0    stevel 			mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
   1543      0    stevel 			continue;
   1544      0    stevel 		}
   1545      0    stevel 
   1546      0    stevel 		/*
   1547      0    stevel 		 * The retransmit timer has popped, and our router is
   1548      0    stevel 		 * MLDv2.  We have to delve into the retransmit state
   1549      0    stevel 		 * stored in the ilm.
   1550      0    stevel 		 *
   1551      0    stevel 		 * Decrement the retransmit count.  If the fmode rtx
   1552      0    stevel 		 * count is active, decrement it, and send a filter
   1553      0    stevel 		 * mode change report with the ilm's source list.
   1554      0    stevel 		 * Otherwise, send a source list change report with
   1555      0    stevel 		 * the current retransmit lists.
   1556      0    stevel 		 */
   1557      0    stevel 		ASSERT(rtxp->rtx_cnt > 0);
   1558      0    stevel 		ASSERT(rtxp->rtx_cnt >= rtxp->rtx_fmode_cnt);
   1559      0    stevel 		rtxp->rtx_cnt--;
   1560      0    stevel 		if (rtxp->rtx_fmode_cnt > 0) {
   1561      0    stevel 			rtxp->rtx_fmode_cnt--;
   1562      0    stevel 			rtype = (ilm->ilm_fmode == MODE_IS_INCLUDE) ?
   1563      0    stevel 			    CHANGE_TO_INCLUDE : CHANGE_TO_EXCLUDE;
   1564      0    stevel 			rtxrp = mcast_bldmrec(rtype, &ilm->ilm_v6addr,
   1565      0    stevel 			    ilm->ilm_filter, rtxrp);
   1566      0    stevel 		} else {
   1567      0    stevel 			rtxrp = mcast_bldmrec(ALLOW_NEW_SOURCES,
   1568      0    stevel 			    &ilm->ilm_v6addr, rtxp->rtx_allow, rtxrp);
   1569      0    stevel 			rtxrp = mcast_bldmrec(BLOCK_OLD_SOURCES,
   1570      0    stevel 			    &ilm->ilm_v6addr, rtxp->rtx_block, rtxrp);
   1571      0    stevel 		}
   1572      0    stevel 		if (rtxp->rtx_cnt > 0) {
   1573      0    stevel 			MCAST_RANDOM_DELAY(rtxp->rtx_timer,
   1574      0    stevel 			    SEC_TO_MSEC(ICMP6_MAX_HOST_REPORT_DELAY));
   1575      0    stevel 			if (rtxp->rtx_timer < next)
   1576      0    stevel 				next = rtxp->rtx_timer;
   1577   4783      udpa 			rtxp->rtx_timer += current;
   1578      0    stevel 		} else {
   1579   9472      Erik 			ASSERT(rtxp->rtx_timer == INFINITY);
   1580      0    stevel 			CLEAR_SLIST(rtxp->rtx_allow);
   1581      0    stevel 			CLEAR_SLIST(rtxp->rtx_block);
   1582      0    stevel 		}
   1583      0    stevel 	}
   1584      0    stevel 
   1585      0    stevel 	if (ill->ill_mcast_type == MLD_V2_ROUTER) {
   1586      0    stevel 		mldv2_sendrpt(ill, rp);
   1587      0    stevel 		mldv2_sendrpt(ill, rtxrp);
   1588      0    stevel 	}
   1589  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   1590  11042      Erik 	/* Send any deferred/queued IP packets */
   1591  11042      Erik 	ill_mcast_send_queued(ill);
   1592  11042      Erik 	/* Defer ill_mcast_timer_start() until the caller is done */
   1593      0    stevel 
   1594      0    stevel 	return (next);
   1595      0    stevel }
   1596      0    stevel 
   1597      0    stevel /*
   1598      0    stevel  * mld_timeout_handler:
   1599      0    stevel  * Called when there are timeout events, every next * TMEOUT_INTERVAL (tick).
   1600      0    stevel  * Returns number of ticks to next event (or 0 if none).
   1601      0    stevel  * MT issues are same as igmp_timeout_handler
   1602      0    stevel  */
   1603      0    stevel void
   1604      0    stevel mld_timeout_handler(void *arg)
   1605      0    stevel {
   1606      0    stevel 	ill_t	*ill;
   1607      0    stevel 	uint_t  global_next = INFINITY;
   1608      0    stevel 	uint_t  next;
   1609      0    stevel 	ill_walk_context_t ctx;
   1610   7098      meem 	ip_stack_t *ipst = arg;
   1611      0    stevel 
   1612   3448  dh155122 	ASSERT(arg != NULL);
   1613   3448  dh155122 	mutex_enter(&ipst->ips_mld_timer_lock);
   1614   3448  dh155122 	ASSERT(ipst->ips_mld_timeout_id != 0);
   1615  11042      Erik 	ipst->ips_mld_timeout_id = 0;
   1616   4783      udpa 	ipst->ips_mld_timer_scheduled_last = 0;
   1617   3448  dh155122 	ipst->ips_mld_time_to_next = 0;
   1618   3448  dh155122 	mutex_exit(&ipst->ips_mld_timer_lock);
   1619      0    stevel 
   1620   3448  dh155122 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1621   3448  dh155122 	ill = ILL_START_WALK_V6(&ctx, ipst);
   1622      0    stevel 	for (; ill != NULL; ill = ill_next(&ctx, ill)) {
   1623      0    stevel 		ASSERT(ill->ill_isv6);
   1624  11042      Erik 		/* Make sure the ill isn't going away. */
   1625  11042      Erik 		if (!ill_check_and_refhold(ill))
   1626      0    stevel 			continue;
   1627   3448  dh155122 		rw_exit(&ipst->ips_ill_g_lock);
   1628  11042      Erik 		next = mld_timeout_handler_per_ill(ill);
   1629  11042      Erik 		if (next < global_next)
   1630  11042      Erik 			global_next = next;
   1631  11042      Erik 		ill_refrele(ill);
   1632   3448  dh155122 		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1633      0    stevel 	}
   1634   3448  dh155122 	rw_exit(&ipst->ips_ill_g_lock);
   1635      0    stevel 	if (global_next != INFINITY)
   1636   3448  dh155122 		mld_start_timers(global_next, ipst);
   1637      0    stevel }
   1638      0    stevel 
   1639      0    stevel /*
   1640      0    stevel  * Calculate the Older Version Querier Present timeout value, in number
   1641      0    stevel  * of slowtimo intervals, for the given ill.
   1642      0    stevel  */
   1643      0    stevel #define	OVQP(ill) \
   1644      0    stevel 	((1000 * (((ill)->ill_mcast_rv * (ill)->ill_mcast_qi) \
   1645      0    stevel 	+ MCAST_QUERY_RESP_INTERVAL)) / MCAST_SLOWTIMO_INTERVAL)
   1646      0    stevel 
   1647      0    stevel /*
   1648      0    stevel  * igmp_slowtimo:
   1649      0    stevel  * - Resets to new router if we didnt we hear from the router
   1650      0    stevel  *   in IGMP_AGE_THRESHOLD seconds.
   1651      0    stevel  * - Resets slowtimeout.
   1652   4783      udpa  * Check for ips_igmp_max_version ensures that we don't revert to a higher
   1653   4783      udpa  * IGMP version than configured.
   1654      0    stevel  */
   1655      0    stevel void
   1656      0    stevel igmp_slowtimo(void *arg)
   1657      0    stevel {
   1658      0    stevel 	ill_t	*ill;
   1659      0    stevel 	ill_if_t *ifp;
   1660      0    stevel 	avl_tree_t *avl_tree;
   1661   3448  dh155122 	ip_stack_t *ipst = (ip_stack_t *)arg;
   1662      0    stevel 
   1663   3448  dh155122 	ASSERT(arg != NULL);
   1664      0    stevel 
   1665      0    stevel 	/*
   1666      0    stevel 	 * The ill_if_t list is circular, hence the odd loop parameters.
   1667      0    stevel 	 *
   1668      0    stevel 	 * We can't use the ILL_START_WALK and ill_next() wrappers for this
   1669      0    stevel 	 * walk, as we need to check the illif_mcast_* fields in the ill_if_t
   1670      0    stevel 	 * structure (allowing us to skip if none of the instances have timers
   1671      0    stevel 	 * running).
   1672      0    stevel 	 */
   1673  11042      Erik 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1674   3448  dh155122 	for (ifp = IP_V4_ILL_G_LIST(ipst);
   1675   3448  dh155122 	    ifp != (ill_if_t *)&IP_V4_ILL_G_LIST(ipst);
   1676      0    stevel 	    ifp = ifp->illif_next) {
   1677      0    stevel 		/*
   1678      0    stevel 		 * illif_mcast_v[12] are set using atomics. If an ill hears
   1679      0    stevel 		 * a V1 or V2 query now and we miss seeing the count now,
   1680      0    stevel 		 * we will see it the next time igmp_slowtimo is called.
   1681      0    stevel 		 */
   1682      0    stevel 		if (ifp->illif_mcast_v1 == 0 && ifp->illif_mcast_v2 == 0)
   1683      0    stevel 			continue;
   1684      0    stevel 
   1685      0    stevel 		avl_tree = &ifp->illif_avl_by_ppa;
   1686      0    stevel 		for (ill = avl_first(avl_tree); ill != NULL;
   1687      0    stevel 		    ill = avl_walk(avl_tree, ill, AVL_AFTER)) {
   1688  11042      Erik 			/* Make sure the ill isn't going away. */
   1689  11042      Erik 			if (!ill_check_and_refhold(ill))
   1690  11042      Erik 				continue;
   1691  11042      Erik 			rw_exit(&ipst->ips_ill_g_lock);
   1692  11042      Erik 			rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   1693      0    stevel 			if (ill->ill_mcast_v1_tset == 1)
   1694      0    stevel 				ill->ill_mcast_v1_time++;
   1695      0    stevel 			if (ill->ill_mcast_v2_tset == 1)
   1696      0    stevel 				ill->ill_mcast_v2_time++;
   1697   4783      udpa 			if ((ill->ill_mcast_type == IGMP_V1_ROUTER) &&
   1698   4783      udpa 			    (ipst->ips_igmp_max_version >= IGMP_V2_ROUTER) &&
   1699   4783      udpa 			    (ill->ill_mcast_v1_time >= OVQP(ill))) {
   1700   4783      udpa 				if ((ill->ill_mcast_v2_tset > 0) ||
   1701   4783      udpa 				    (ipst->ips_igmp_max_version ==
   1702   4783      udpa 				    IGMP_V2_ROUTER)) {
   1703   4783      udpa 					ip1dbg(("V1 query timer "
   1704   4783      udpa 					    "expired on %s; switching "
   1705   4783      udpa 					    "mode to IGMP_V2\n",
   1706   4783      udpa 					    ill->ill_name));
   1707   4783      udpa 					ill->ill_mcast_type =
   1708   4783      udpa 					    IGMP_V2_ROUTER;
   1709   4783      udpa 				} else {
   1710   4783      udpa 					ip1dbg(("V1 query timer "
   1711   4783      udpa 					    "expired on %s; switching "
   1712   4783      udpa 					    "mode to IGMP_V3\n",
   1713   4783      udpa 					    ill->ill_name));
   1714   4783      udpa 					ill->ill_mcast_type =
   1715   4783      udpa 					    IGMP_V3_ROUTER;
   1716      0    stevel 				}
   1717   4783      udpa 				ill->ill_mcast_v1_time = 0;
   1718   4783      udpa 				ill->ill_mcast_v1_tset = 0;
   1719   4783      udpa 				atomic_add_16(&ifp->illif_mcast_v1, -1);
   1720      0    stevel 			}
   1721   4783      udpa 			if ((ill->ill_mcast_type == IGMP_V2_ROUTER) &&
   1722   4783      udpa 			    (ipst->ips_igmp_max_version >= IGMP_V3_ROUTER) &&
   1723   4783      udpa 			    (ill->ill_mcast_v2_time >= OVQP(ill))) {
   1724   4783      udpa 				ip1dbg(("V2 query timer expired on "
   1725   4783      udpa 				    "%s; switching mode to IGMP_V3\n",
   1726   4783      udpa 				    ill->ill_name));
   1727   4783      udpa 				ill->ill_mcast_type = IGMP_V3_ROUTER;
   1728   4783      udpa 				ill->ill_mcast_v2_time = 0;
   1729   4783      udpa 				ill->ill_mcast_v2_tset = 0;
   1730   4783      udpa 				atomic_add_16(&ifp->illif_mcast_v2, -1);
   1731      0    stevel 			}
   1732  11042      Erik 			rw_exit(&ill->ill_mcast_lock);
   1733  11042      Erik 			ill_refrele(ill);
   1734  11042      Erik 			rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1735      0    stevel 		}
   1736      0    stevel 	}
   1737   3448  dh155122 	rw_exit(&ipst->ips_ill_g_lock);
   1738  11042      Erik 	ill_mcast_timer_start(ipst);
   1739   3448  dh155122 	mutex_enter(&ipst->ips_igmp_slowtimeout_lock);
   1740   3448  dh155122 	ipst->ips_igmp_slowtimeout_id = timeout(igmp_slowtimo, (void *)ipst,
   1741   7098      meem 	    MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
   1742   3448  dh155122 	mutex_exit(&ipst->ips_igmp_slowtimeout_lock);
   1743      0    stevel }
   1744      0    stevel 
   1745      0    stevel /*
   1746      0    stevel  * mld_slowtimo:
   1747      0    stevel  * - Resets to newer version if we didn't hear from the older version router
   1748      0    stevel  *   in MLD_AGE_THRESHOLD seconds.
   1749      0    stevel  * - Restarts slowtimeout.
   1750   4783      udpa  * Check for ips_mld_max_version ensures that we don't revert to a higher
   1751   4783      udpa  * IGMP version than configured.
   1752      0    stevel  */
   1753      0    stevel void
   1754      0    stevel mld_slowtimo(void *arg)
   1755      0    stevel {
   1756      0    stevel 	ill_t *ill;
   1757      0    stevel 	ill_if_t *ifp;
   1758      0    stevel 	avl_tree_t *avl_tree;
   1759   3448  dh155122 	ip_stack_t *ipst = (ip_stack_t *)arg;
   1760      0    stevel 
   1761   3448  dh155122 	ASSERT(arg != NULL);
   1762      0    stevel 	/* See comments in igmp_slowtimo() above... */
   1763   3448  dh155122 	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1764   3448  dh155122 	for (ifp = IP_V6_ILL_G_LIST(ipst);
   1765   3448  dh155122 	    ifp != (ill_if_t *)&IP_V6_ILL_G_LIST(ipst);
   1766      0    stevel 	    ifp = ifp->illif_next) {
   1767      0    stevel 		if (ifp->illif_mcast_v1 == 0)
   1768      0    stevel 			continue;
   1769      0    stevel 
   1770      0    stevel 		avl_tree = &ifp->illif_avl_by_ppa;
   1771      0    stevel 		for (ill = avl_first(avl_tree); ill != NULL;
   1772      0    stevel 		    ill = avl_walk(avl_tree, ill, AVL_AFTER)) {
   1773  11042      Erik 			/* Make sure the ill isn't going away. */
   1774  11042      Erik 			if (!ill_check_and_refhold(ill))
   1775  11042      Erik 				continue;
   1776  11042      Erik 			rw_exit(&ipst->ips_ill_g_lock);
   1777  11042      Erik 			rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   1778      0    stevel 			if (ill->ill_mcast_v1_tset == 1)
   1779      0    stevel 				ill->ill_mcast_v1_time++;
   1780   4783      udpa 			if ((ill->ill_mcast_type == MLD_V1_ROUTER) &&
   1781   4783      udpa 			    (ipst->ips_mld_max_version >= MLD_V2_ROUTER) &&
   1782   4783      udpa 			    (ill->ill_mcast_v1_time >= OVQP(ill))) {
   1783   4783      udpa 				ip1dbg(("MLD query timer expired on"
   1784   4783      udpa 				    " %s; switching mode to MLD_V2\n",
   1785   4783      udpa 				    ill->ill_name));
   1786   4783      udpa 				ill->ill_mcast_type = MLD_V2_ROUTER;
   1787   4783      udpa 				ill->ill_mcast_v1_time = 0;
   1788   4783      udpa 				ill->ill_mcast_v1_tset = 0;
   1789   4783      udpa 				atomic_add_16(&ifp->illif_mcast_v1, -1);
   1790      0    stevel 			}
   1791  11042      Erik 			rw_exit(&ill->ill_mcast_lock);
   1792  11042      Erik 			ill_refrele(ill);
   1793  11042      Erik 			rw_enter(&ipst->ips_ill_g_lock, RW_READER);
   1794      0    stevel 		}
   1795      0    stevel 	}
   1796   3448  dh155122 	rw_exit(&ipst->ips_ill_g_lock);
   1797  11042      Erik 	ill_mcast_timer_start(ipst);
   1798   3448  dh155122 	mutex_enter(&ipst->ips_mld_slowtimeout_lock);
   1799   3448  dh155122 	ipst->ips_mld_slowtimeout_id = timeout(mld_slowtimo, (void *)ipst,
   1800      0    stevel 	    MSEC_TO_TICK(MCAST_SLOWTIMO_INTERVAL));
   1801   3448  dh155122 	mutex_exit(&ipst->ips_mld_slowtimeout_lock);
   1802      0    stevel }
   1803      0    stevel 
   1804      0    stevel /*
   1805      0    stevel  * igmp_sendpkt:
   1806  11042      Erik  * This will send to ip_output_simple just like icmp_inbound.
   1807      0    stevel  */
   1808      0    stevel static void
   1809      0    stevel igmp_sendpkt(ilm_t *ilm, uchar_t type, ipaddr_t addr)
   1810      0    stevel {
   1811      0    stevel 	mblk_t	*mp;
   1812      0    stevel 	igmpa_t	*igmpa;
   1813      0    stevel 	uint8_t *rtralert;
   1814      0    stevel 	ipha_t	*ipha;
   1815      0    stevel 	int	hdrlen = sizeof (ipha_t) + RTRALERT_LEN;
   1816      0    stevel 	size_t	size  = hdrlen + sizeof (igmpa_t);
   1817  11042      Erik 	ill_t 	*ill  = ilm->ilm_ill;
   1818   3448  dh155122 	ip_stack_t *ipst = ill->ill_ipst;
   1819      0    stevel 
   1820  11042      Erik 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
   1821      0    stevel 
   1822      0    stevel 	mp = allocb(size, BPRI_HI);
   1823      0    stevel 	if (mp == NULL) {
   1824      0    stevel 		return;
   1825      0    stevel 	}
   1826      0    stevel 	mp->b_wptr = mp->b_rptr + size;
   1827      0    stevel 
   1828      0    stevel 	ipha = (ipha_t *)mp->b_rptr;
   1829      0    stevel 	rtralert = (uint8_t *)&(ipha[1]);
   1830      0    stevel 	igmpa = (igmpa_t *)&(rtralert[RTRALERT_LEN]);
   1831      0    stevel 	igmpa->igmpa_type   = type;
   1832      0    stevel 	igmpa->igmpa_code   = 0;
   1833      0    stevel 	igmpa->igmpa_group  = ilm->ilm_addr;
   1834      0    stevel 	igmpa->igmpa_cksum  = 0;
   1835      0    stevel 	igmpa->igmpa_cksum  = IP_CSUM(mp, hdrlen, 0);
   1836      0    stevel 
   1837   2603  rk129064 	rtralert[0] = IPOPT_COPY | IPOPT_RTRALERT;
   1838      0    stevel 	rtralert[1] = RTRALERT_LEN;
   1839      0    stevel 	rtralert[2] = 0;
   1840      0    stevel 	rtralert[3] = 0;
   1841      0    stevel 
   1842      0    stevel 	ipha->ipha_version_and_hdr_length = (IP_VERSION << 4)
   1843      0    stevel 	    | (IP_SIMPLE_HDR_LENGTH_IN_WORDS + RTRALERT_LEN_IN_WORDS);
   1844      0    stevel 	ipha->ipha_type_of_service 	= 0;
   1845      0    stevel 	ipha->ipha_length = htons(size);
   1846      0    stevel 	ipha->ipha_ident = 0;
   1847      0    stevel 	ipha->ipha_fragment_offset_and_flags = 0;
   1848      0    stevel 	ipha->ipha_ttl 		= IGMP_TTL;
   1849      0    stevel 	ipha->ipha_protocol 	= IPPROTO_IGMP;
   1850      0    stevel 	ipha->ipha_hdr_checksum 	= 0;
   1851      0    stevel 	ipha->ipha_dst 		= addr ? addr : igmpa->igmpa_group;
   1852  11042      Erik 	ipha->ipha_src 		= INADDR_ANY;
   1853      0    stevel 
   1854  11042      Erik 	ill_mcast_queue(ill, mp);
   1855      0    stevel 
   1856   3448  dh155122 	++ipst->ips_igmpstat.igps_snd_reports;
   1857      0    stevel }
   1858      0    stevel 
   1859      0    stevel /*
   1860  11042      Erik  * Sends an IGMP_V3_MEMBERSHIP_REPORT message out the ill.
   1861  11042      Erik  * The report will contain one group record
   1862      0    stevel  * for each element of reclist.  If this causes packet length to
   1863  11042      Erik  * exceed ill->ill_mtu, multiple reports are sent.
   1864      0    stevel  * reclist is assumed to be made up of buffers allocated by mcast_bldmrec(),
   1865      0    stevel  * and those buffers are freed here.
   1866      0    stevel  */
   1867      0    stevel static void
   1868  11042      Erik igmpv3_sendrpt(ill_t *ill, mrec_t *reclist)
   1869      0    stevel {
   1870      0    stevel 	igmp3ra_t *igmp3ra;
   1871      0    stevel 	grphdra_t *grphdr;
   1872  11042      Erik 	mblk_t *mp;
   1873      0    stevel 	ipha_t *ipha;
   1874      0    stevel 	uint8_t *rtralert;
   1875      0    stevel 	ipaddr_t *src_array;
   1876      0    stevel 	int i, j, numrec, more_src_cnt;
   1877      0    stevel 	size_t hdrsize, size, rsize;
   1878      0    stevel 	mrec_t *rp, *cur_reclist;
   1879      0    stevel 	mrec_t *next_reclist = reclist;
   1880      0    stevel 	boolean_t morepkts;
   1881   3448  dh155122 	ip_stack_t	 *ipst = ill->ill_ipst;
   1882      0    stevel 
   1883  11042      Erik 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
   1884   8485     Peter 
   1885      0    stevel 	/* if there aren't any records, there's nothing to send */
   1886      0    stevel 	if (reclist == NULL)
   1887      0    stevel 		return;
   1888      0    stevel 
   1889      0    stevel 	hdrsize = sizeof (ipha_t) + RTRALERT_LEN;
   1890      0    stevel nextpkt:
   1891      0    stevel 	size = hdrsize + sizeof (igmp3ra_t);
   1892      0    stevel 	morepkts = B_FALSE;
   1893      0    stevel 	more_src_cnt = 0;
   1894      0    stevel 	cur_reclist = next_reclist;
   1895      0    stevel 	numrec = 0;
   1896      0    stevel 	for (rp = cur_reclist; rp != NULL; rp = rp->mrec_next) {
   1897      0    stevel 		rsize = sizeof (grphdra_t) +
   1898      0    stevel 		    (rp->mrec_srcs.sl_numsrc * sizeof (ipaddr_t));
   1899  11042      Erik 		if (size + rsize > ill->ill_mtu) {
   1900      0    stevel 			if (rp == cur_reclist) {
   1901      0    stevel 				/*
   1902      0    stevel 				 * If the first mrec we looked at is too big
   1903      0    stevel 				 * to fit in a single packet (i.e the source
   1904      0    stevel 				 * list is too big), we must either truncate
   1905      0    stevel 				 * the list (if TO_EX or IS_EX), or send
   1906      0    stevel 				 * multiple reports for the same group (all
   1907      0    stevel 				 * other types).
   1908      0    stevel 				 */
   1909      0    stevel 				int srcspace, srcsperpkt;
   1910  11042      Erik 				srcspace = ill->ill_mtu - (size +
   1911      0    stevel 				    sizeof (grphdra_t));
   1912   8485     Peter 
   1913   8485     Peter 				/*
   1914   8485     Peter 				 * Skip if there's not even enough room in
   1915   8485     Peter 				 * a single packet to send something useful.
   1916   8485     Peter 				 */
   1917   8485     Peter 				if (srcspace <= sizeof (ipaddr_t))
   1918   8485     Peter 					continue;
   1919   8485     Peter 
   1920      0    stevel 				srcsperpkt = srcspace / sizeof (ipaddr_t);
   1921      0    stevel 				/*
   1922      0    stevel 				 * Increment size and numrec, because we will
   1923      0    stevel 				 * be sending a record for the mrec we're
   1924      0    stevel 				 * looking at now.
   1925      0    stevel 				 */
   1926      0    stevel 				size += sizeof (grphdra_t) +
   1927      0    stevel 				    (srcsperpkt * sizeof (ipaddr_t));
   1928      0    stevel 				numrec++;
   1929      0    stevel 				if (rp->mrec_type == MODE_IS_EXCLUDE ||
   1930      0    stevel 				    rp->mrec_type == CHANGE_TO_EXCLUDE) {
   1931      0    stevel 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
   1932      0    stevel 					if (rp->mrec_next == NULL) {
   1933      0    stevel 						/* no more packets to send */
   1934      0    stevel 						break;
   1935      0    stevel 					} else {
   1936      0    stevel 						/*
   1937      0    stevel 						 * more packets, but we're
   1938      0    stevel 						 * done with this mrec.
   1939      0    stevel 						 */
   1940      0    stevel 						next_reclist = rp->mrec_next;
   1941      0    stevel 					}
   1942      0    stevel 				} else {
   1943      0    stevel 					more_src_cnt = rp->mrec_srcs.sl_numsrc
   1944      0    stevel 					    - srcsperpkt;
   1945      0    stevel 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
   1946      0    stevel 					/*
   1947      0    stevel 					 * We'll fix up this mrec (remove the
   1948      0    stevel 					 * srcs we've already sent) before
   1949      0    stevel 					 * returning to nextpkt above.
   1950      0    stevel 					 */
   1951      0    stevel 					next_reclist = rp;
   1952      0    stevel 				}
   1953      0    stevel 			} else {
   1954      0    stevel 				next_reclist = rp;
   1955      0    stevel 			}
   1956      0    stevel 			morepkts = B_TRUE;
   1957      0    stevel 			break;
   1958      0    stevel 		}
   1959      0    stevel 		size += rsize;
   1960      0    stevel 		numrec++;
   1961      0    stevel 	}
   1962      0    stevel 
   1963      0    stevel 	mp = allocb(size, BPRI_HI);
   1964      0    stevel 	if (mp == NULL) {
   1965      0    stevel 		goto free_reclist;
   1966      0    stevel 	}
   1967      0    stevel 	bzero((char *)mp->b_rptr, size);
   1968      0    stevel 	mp->b_wptr = (uchar_t *)(mp->b_rptr + size);
   1969      0    stevel 
   1970      0    stevel 	ipha = (ipha_t *)mp->b_rptr;
   1971      0    stevel 	rtralert = (uint8_t *)&(ipha[1]);
   1972      0    stevel 	igmp3ra = (igmp3ra_t *)&(rtralert[RTRALERT_LEN]);
   1973      0    stevel 	grphdr = (grphdra_t *)&(igmp3ra[1]);
   1974      0    stevel 
   1975      0    stevel 	rp = cur_reclist;
   1976      0    stevel 	for (i = 0; i < numrec; i++) {
   1977      0    stevel 		grphdr->grphdra_type = rp->mrec_type;
   1978      0    stevel 		grphdr->grphdra_numsrc = htons(rp->mrec_srcs.sl_numsrc);
   1979      0    stevel 		grphdr->grphdra_group = V4_PART_OF_V6(rp->mrec_group);
   1980      0    stevel 		src_array = (ipaddr_t *)&(grphdr[1]);
   1981      0    stevel 
   1982      0    stevel 		for (j = 0; j < rp->mrec_srcs.sl_numsrc; j++)
   1983      0    stevel 			src_array[j] = V4_PART_OF_V6(rp->mrec_srcs.sl_addr[j]);
   1984      0    stevel 
   1985      0    stevel 		grphdr = (grphdra_t *)&(src_array[j]);
   1986      0    stevel 		rp = rp->mrec_next;
   1987      0    stevel 	}
   1988      0    stevel 
   1989      0    stevel 	igmp3ra->igmp3ra_type = IGMP_V3_MEMBERSHIP_REPORT;
   1990      0    stevel 	igmp3ra->igmp3ra_numrec = htons(numrec);
   1991      0    stevel 	igmp3ra->igmp3ra_cksum = IP_CSUM(mp, hdrsize, 0);
   1992      0    stevel 
   1993   2603  rk129064 	rtralert[0] = IPOPT_COPY | IPOPT_RTRALERT;
   1994      0    stevel 	rtralert[1] = RTRALERT_LEN;
   1995      0    stevel 	rtralert[2] = 0;
   1996      0    stevel 	rtralert[3] = 0;
   1997      0    stevel 
   1998      0    stevel 	ipha->ipha_version_and_hdr_length = IP_VERSION << 4
   1999      0    stevel 	    | (IP_SIMPLE_HDR_LENGTH_IN_WORDS + RTRALERT_LEN_IN_WORDS);
   2000      0    stevel 	ipha->ipha_type_of_service = IPTOS_PREC_INTERNETCONTROL;
   2001      0    stevel 	ipha->ipha_length = htons(size);
   2002      0    stevel 	ipha->ipha_ttl = IGMP_TTL;
   2003      0    stevel 	ipha->ipha_protocol = IPPROTO_IGMP;
   2004      0    stevel 	ipha->ipha_dst = htonl(INADDR_ALLRPTS_GROUP);
   2005  11042      Erik 	ipha->ipha_src = INADDR_ANY;
   2006      0    stevel 
   2007  11042      Erik 	ill_mcast_queue(ill, mp);
   2008      0    stevel 
   2009   3448  dh155122 	++ipst->ips_igmpstat.igps_snd_reports;
   2010      0    stevel 
   2011      0    stevel 	if (morepkts) {
   2012      0    stevel 		if (more_src_cnt > 0) {
   2013      0    stevel 			int index, mvsize;
   2014      0    stevel 			slist_t *sl = &next_reclist->mrec_srcs;
   2015      0    stevel 			index = sl->sl_numsrc;
   2016      0    stevel 			mvsize = more_src_cnt * sizeof (in6_addr_t);
   2017      0    stevel 			(void) memmove(&sl->sl_addr[0], &sl->sl_addr[index],
   2018      0    stevel 			    mvsize);
   2019      0    stevel 			sl->sl_numsrc = more_src_cnt;
   2020      0    stevel 		}
   2021      0    stevel 		goto nextpkt;
   2022      0    stevel 	}
   2023      0    stevel 
   2024      0    stevel free_reclist:
   2025      0    stevel 	while (reclist != NULL) {
   2026      0    stevel 		rp = reclist->mrec_next;
   2027      0    stevel 		mi_free(reclist);
   2028      0    stevel 		reclist = rp;
   2029      0    stevel 	}
   2030      0    stevel }
   2031      0    stevel 
   2032      0    stevel /*
   2033      0    stevel  * mld_input:
   2034  11042      Erik  * Return NULL for a bad packet that is discarded here.
   2035  11042      Erik  * Return mp if the message is OK and should be handed to "raw" receivers.
   2036  11042      Erik  * Callers of mld_input() may need to reinitialize variables that were copied
   2037  11042      Erik  * from the mblk as this calls pullupmsg().
   2038      0    stevel  */
   2039  11042      Erik mblk_t *
   2040  11042      Erik mld_input(mblk_t *mp, ip_recv_attr_t *ira)
   2041      0    stevel {
   2042      0    stevel 	ip6_t		*ip6h = (ip6_t *)(mp->b_rptr);
   2043      0    stevel 	mld_hdr_t	*mldh;
   2044      0    stevel 	ilm_t		*ilm;
   2045      0    stevel 	ipif_t		*ipif;
   2046      0    stevel 	uint16_t	hdr_length, exthdr_length;
   2047  11042      Erik 	in6_addr_t	*v6group_ptr;
   2048      0    stevel 	uint_t		next;
   2049      0    stevel 	int		mldlen;
   2050  11042      Erik 	ill_t		*ill = ira->ira_ill;
   2051   3448  dh155122 	ip_stack_t	*ipst = ill->ill_ipst;
   2052      0    stevel 
   2053      0    stevel 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembTotal);
   2054      0    stevel 
   2055      0    stevel 	/* Make sure the src address of the packet is link-local */
   2056      0    stevel 	if (!(IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src))) {
   2057      0    stevel 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
   2058      0    stevel 		freemsg(mp);
   2059  11042      Erik 		return (NULL);
   2060      0    stevel 	}
   2061      0    stevel 
   2062      0    stevel 	if (ip6h->ip6_hlim != 1) {
   2063      0    stevel 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpBadHoplimit);
   2064      0    stevel 		freemsg(mp);
   2065  11042      Erik 		return (NULL);
   2066      0    stevel 	}
   2067      0    stevel 
   2068      0    stevel 	/* Get to the icmp header part */
   2069  11042      Erik 	hdr_length = ira->ira_ip_hdr_length;
   2070  11042      Erik 	exthdr_length = hdr_length - IPV6_HDR_LEN;
   2071  11042      Erik 
   2072      0    stevel 	mldlen = ntohs(ip6h->ip6_plen) - exthdr_length;
   2073      0    stevel 
   2074      0    stevel 	/* An MLD packet must at least be 24 octets to be valid */
   2075      0    stevel 	if (mldlen < MLD_MINLEN) {
   2076      0    stevel 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
   2077      0    stevel 		freemsg(mp);
   2078  11042      Erik 		return (NULL);
   2079      0    stevel 	}
   2080      0    stevel 
   2081      0    stevel 	mldh = (mld_hdr_t *)(&mp->b_rptr[hdr_length]);
   2082      0    stevel 
   2083      0    stevel 	switch (mldh->mld_type) {
   2084      0    stevel 	case MLD_LISTENER_QUERY:
   2085      0    stevel 		/*
   2086      0    stevel 		 * packet length differentiates between v1 and v2.  v1
   2087      0    stevel 		 * query should be exactly 24 octets long; v2 is >= 28.
   2088      0    stevel 		 */
   2089   4783      udpa 		if ((mldlen == MLD_MINLEN) ||
   2090   4783      udpa 		    (ipst->ips_mld_max_version < MLD_V2_ROUTER)) {
   2091      0    stevel 			next = mld_query_in(mldh, ill);
   2092      0    stevel 		} else if (mldlen >= MLD_V2_QUERY_MINLEN) {
   2093      0    stevel 			next = mldv2_query_in((mld2q_t *)mldh, ill, mldlen);
   2094      0    stevel 		} else {
   2095      0    stevel 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
   2096      0    stevel 			freemsg(mp);
   2097  11042      Erik 			return (NULL);
   2098      0    stevel 		}
   2099      0    stevel 		if (next == 0) {
   2100  11042      Erik 			return (mp);
   2101      0    stevel 		}
   2102      0    stevel 
   2103      0    stevel 		if (next != INFINITY)
   2104   3448  dh155122 			mld_start_timers(next, ipst);
   2105      0    stevel 		break;
   2106      0    stevel 
   2107  11042      Erik 	case MLD_LISTENER_REPORT:
   2108      0    stevel 		/*
   2109      0    stevel 		 * For fast leave to work, we have to know that we are the
   2110      0    stevel 		 * last person to send a report for this group.  Reports
   2111      0    stevel 		 * generated by us are looped back since we could potentially
   2112      0    stevel 		 * be a multicast router, so discard reports sourced by me.
   2113      0    stevel 		 */
   2114      0    stevel 		mutex_enter(&ill->ill_lock);
   2115      0    stevel 		for (ipif = ill->ill_ipif; ipif != NULL;
   2116      0    stevel 		    ipif = ipif->ipif_next) {
   2117      0    stevel 			if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr,
   2118  11042      Erik 			    &ip6h->ip6_src)) {
   2119      0    stevel 				if (ip_debug > 1) {
   2120      0    stevel 					char    buf1[INET6_ADDRSTRLEN];
   2121      0    stevel 
   2122      0    stevel 					(void) mi_strlog(ill->ill_rq,
   2123      0    stevel 					    1,
   2124      0    stevel 					    SL_TRACE,
   2125      0    stevel 					    "mld_input: we are only "
   2126  11042      Erik 					    "member src %s\n",
   2127  11042      Erik 					    inet_ntop(AF_INET6, &ip6h->ip6_src,
   2128  11042      Erik 					    buf1, sizeof (buf1)));
   2129      0    stevel 				}
   2130      0    stevel 				mutex_exit(&ill->ill_lock);
   2131  11042      Erik 				return (mp);
   2132      0    stevel 			}
   2133      0    stevel 		}
   2134      0    stevel 		mutex_exit(&ill->ill_lock);
   2135      0    stevel 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembResponses);
   2136      0    stevel 
   2137      0    stevel 		v6group_ptr = &mldh->mld_addr;
   2138      0    stevel 		if (!IN6_IS_ADDR_MULTICAST(v6group_ptr)) {
   2139      0    stevel 			BUMP_MIB(ill->ill_icmp6_mib,
   2140      0    stevel 			    ipv6IfIcmpInGroupMembBadReports);
   2141      0    stevel 			freemsg(mp);
   2142  11042      Erik 			return (NULL);
   2143      0    stevel 		}
   2144  11042      Erik 
   2145      0    stevel 
   2146      0    stevel 		/*
   2147      0    stevel 		 * If we belong to the group being reported, and we are a
   2148      0    stevel 		 * 'Delaying member' per the RFC terminology, stop our timer
   2149      0    stevel 		 * for that group and 'clear flag' i.e. mark ilm_state as
   2150      0    stevel 		 * IGMP_OTHERMEMBER. With zones, there can be multiple group
   2151      0    stevel 		 * membership entries for the same group address (one per zone)
   2152      0    stevel 		 * so we need to walk the ill_ilm list.
   2153      0    stevel 		 */
   2154  11042      Erik 		rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   2155  11042      Erik 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
   2156      0    stevel 			if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group_ptr))
   2157   7098      meem 				continue;
   2158      0    stevel 			BUMP_MIB(ill->ill_icmp6_mib,
   2159      0    stevel 			    ipv6IfIcmpInGroupMembOurReports);
   2160      0    stevel 
   2161      0    stevel 			ilm->ilm_timer = INFINITY;
   2162      0    stevel 			ilm->ilm_state = IGMP_OTHERMEMBER;
   2163      0    stevel 		}
   2164  11042      Erik 		rw_exit(&ill->ill_mcast_lock);
   2165  11042      Erik 		/*
   2166  11042      Erik 		 * No packets have been sent above - no
   2167  11042      Erik 		 * ill_mcast_send_queued is needed.
   2168  11042      Erik 		 */
   2169  11042      Erik 		ill_mcast_timer_start(ill->ill_ipst);
   2170      0    stevel 		break;
   2171  11042      Erik 
   2172      0    stevel 	case MLD_LISTENER_REDUCTION:
   2173      0    stevel 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembReductions);
   2174      0    stevel 		break;
   2175      0    stevel 	}
   2176  11042      Erik 	return (mp);
   2177      0    stevel }
   2178      0    stevel 
   2179      0    stevel /*
   2180      0    stevel  * Handles an MLDv1 Listener Query.  Returns 0 on error, or the appropriate
   2181      0    stevel  * (non-zero, unsigned) timer value to be set on success.
   2182      0    stevel  */
   2183      0    stevel static uint_t
   2184      0    stevel mld_query_in(mld_hdr_t *mldh, ill_t *ill)
   2185      0    stevel {
   2186      0    stevel 	ilm_t	*ilm;
   2187      0    stevel 	int	timer;
   2188   4783      udpa 	uint_t	next, current;
   2189      0    stevel 	in6_addr_t *v6group;
   2190      0    stevel 
   2191      0    stevel 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembQueries);
   2192      0    stevel 
   2193      0    stevel 	/*
   2194      0    stevel 	 * In the MLD specification, there are 3 states and a flag.
   2195      0    stevel 	 *
   2196      0    stevel 	 * In Non-Listener state, we simply don't have a membership record.
   2197      0    stevel 	 * In Delaying state, our timer is running (ilm->ilm_timer < INFINITY)
   2198      0    stevel 	 * In Idle Member state, our timer is not running (ilm->ilm_timer ==
   2199      0    stevel 	 * INFINITY)
   2200      0    stevel 	 *
   2201      0    stevel 	 * The flag is ilm->ilm_state, it is set to IGMP_OTHERMEMBER if
   2202      0    stevel 	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
   2203      0    stevel 	 * if I sent the last report.
   2204      0    stevel 	 */
   2205      0    stevel 	v6group = &mldh->mld_addr;
   2206      0    stevel 	if (!(IN6_IS_ADDR_UNSPECIFIED(v6group)) &&
   2207      0    stevel 	    ((!IN6_IS_ADDR_MULTICAST(v6group)))) {
   2208      0    stevel 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembBadQueries);
   2209      0    stevel 		return (0);
   2210      0    stevel 	}
   2211      0    stevel 
   2212      0    stevel 	/* Need to do compatibility mode checking */
   2213  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   2214      0    stevel 	ill->ill_mcast_v1_time = 0;
   2215      0    stevel 	ill->ill_mcast_v1_tset = 1;
   2216      0    stevel 	if (ill->ill_mcast_type == MLD_V2_ROUTER) {
   2217      0    stevel 		ip1dbg(("Received MLDv1 Query on %s, switching mode to "
   2218      0    stevel 		    "MLD_V1_ROUTER\n", ill->ill_name));
   2219      0    stevel 		atomic_add_16(&ill->ill_ifptr->illif_mcast_v1, 1);
   2220      0    stevel 		ill->ill_mcast_type = MLD_V1_ROUTER;
   2221      0    stevel 	}
   2222      0    stevel 
   2223      0    stevel 	timer = (int)ntohs(mldh->mld_maxdelay);
   2224      0    stevel 	if (ip_debug > 1) {
   2225      0    stevel 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
   2226      0    stevel 		    "mld_input: TIMER = mld_maxdelay %d mld_type 0x%x",
   2227      0    stevel 		    timer, (int)mldh->mld_type);
   2228      0    stevel 	}
   2229      0    stevel 
   2230      0    stevel 	/*
   2231      0    stevel 	 * -Start the timers in all of our membership records for
   2232      0    stevel 	 * the physical interface on which the query arrived,
   2233      0    stevel 	 * excl:
   2234      0    stevel 	 *	1.  those that belong to the "all hosts" group,
   2235      0    stevel 	 *	2.  those with 0 scope, or 1 node-local scope.
   2236      0    stevel 	 *
   2237      0    stevel 	 * -Restart any timer that is already running but has a value
   2238      0    stevel 	 * longer that the requested timeout.
   2239      0    stevel 	 * -Use the value specified in the query message as the
   2240      0    stevel 	 * maximum timeout.
   2241      0    stevel 	 */
   2242      0    stevel 	next = INFINITY;
   2243   8485     Peter 
   2244   8485     Peter 	current = CURRENT_MSTIME;
   2245  11042      Erik 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
   2246      0    stevel 		ASSERT(!IN6_IS_ADDR_V4MAPPED(&ilm->ilm_v6addr));
   2247      0    stevel 
   2248      0    stevel 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr) ||
   2249      0    stevel 		    IN6_IS_ADDR_MC_NODELOCAL(&ilm->ilm_v6addr) ||
   2250      0    stevel 		    IN6_IS_ADDR_MC_RESERVED(&ilm->ilm_v6addr))
   2251      0    stevel 			continue;
   2252      0    stevel 		if ((!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr,
   2253      0    stevel 		    &ipv6_all_hosts_mcast)) &&
   2254      0    stevel 		    (IN6_IS_ADDR_UNSPECIFIED(v6group)) ||
   2255      0    stevel 		    (IN6_ARE_ADDR_EQUAL(v6group, &ilm->ilm_v6addr))) {
   2256      0    stevel 			if (timer == 0) {
   2257      0    stevel 				/* Respond immediately */
   2258      0    stevel 				ilm->ilm_timer = INFINITY;
   2259      0    stevel 				ilm->ilm_state = IGMP_IREPORTEDLAST;
   2260      0    stevel 				mld_sendpkt(ilm, MLD_LISTENER_REPORT, NULL);
   2261      0    stevel 				break;
   2262      0    stevel 			}
   2263      0    stevel 			if (ilm->ilm_timer > timer) {
   2264      0    stevel 				MCAST_RANDOM_DELAY(ilm->ilm_timer, timer);
   2265      0    stevel 				if (ilm->ilm_timer < next)
   2266      0    stevel 					next = ilm->ilm_timer;
   2267   4783      udpa 				ilm->ilm_timer += current;
   2268      0    stevel 			}
   2269      0    stevel 			break;
   2270      0    stevel 		}
   2271      0    stevel 	}
   2272  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   2273  11042      Erik 	/* Send any deferred/queued IP packets */
   2274  11042      Erik 	ill_mcast_send_queued(ill);
   2275  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
   2276      0    stevel 
   2277      0    stevel 	return (next);
   2278      0    stevel }
   2279      0    stevel 
   2280      0    stevel /*
   2281      0    stevel  * Handles an MLDv2 Listener Query.  On error, returns 0; on success,
   2282      0    stevel  * returns the appropriate (non-zero, unsigned) timer value (which may
   2283      0    stevel  * be INFINITY) to be set.
   2284      0    stevel  */
   2285      0    stevel static uint_t
   2286      0    stevel mldv2_query_in(mld2q_t *mld2q, ill_t *ill, int mldlen)
   2287      0    stevel {
   2288      0    stevel 	ilm_t	*ilm;
   2289      0    stevel 	in6_addr_t *v6group, *src_array;
   2290   4783      udpa 	uint_t	next, numsrc, i, mrd, delay, qqi, current;
   2291      0    stevel 	uint8_t	qrv;
   2292      0    stevel 
   2293      0    stevel 	v6group = &mld2q->mld2q_addr;
   2294      0    stevel 	numsrc = ntohs(mld2q->mld2q_numsrc);
   2295      0    stevel 
   2296      0    stevel 	/* make sure numsrc matches packet size */
   2297      0    stevel 	if (mldlen < MLD_V2_QUERY_MINLEN + (numsrc * sizeof (in6_addr_t))) {
   2298      0    stevel 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
   2299      0    stevel 		return (0);
   2300      0    stevel 	}
   2301      0    stevel 	src_array = (in6_addr_t *)&mld2q[1];
   2302      0    stevel 
   2303      0    stevel 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInGroupMembQueries);
   2304      0    stevel 
   2305      0    stevel 	/* extract Maximum Response Delay from code in header */
   2306      0    stevel 	mrd = ntohs(mld2q->mld2q_mxrc);
   2307      0    stevel 	if (mrd >= MLD_V2_MAXRT_FPMIN) {
   2308      0    stevel 		uint_t hdrval, mant, exp;
   2309      0    stevel 		hdrval = mrd;
   2310      0    stevel 		mant = hdrval & MLD_V2_MAXRT_MANT_MASK;
   2311      0    stevel 		exp = (hdrval & MLD_V2_MAXRT_EXP_MASK) >> 12;
   2312      0    stevel 		mrd = (mant | 0x1000) << (exp + 3);
   2313      0    stevel 	}
   2314   4783      udpa 	if (mrd == 0)
   2315   4783      udpa 		mrd = DSEC_TO_MSEC(MCAST_DEF_QUERY_RESP_INTERVAL);
   2316   4783      udpa 
   2317      0    stevel 	MCAST_RANDOM_DELAY(delay, mrd);
   2318      0    stevel 	next = (unsigned)INFINITY;
   2319   4783      udpa 	current = CURRENT_MSTIME;
   2320      0    stevel 
   2321      0    stevel 	if ((qrv = mld2q->mld2q_sqrv & MLD_V2_RV_MASK) == 0)
   2322      0    stevel 		ill->ill_mcast_rv = MCAST_DEF_ROBUSTNESS;
   2323      0    stevel 	else
   2324      0    stevel 		ill->ill_mcast_rv = qrv;
   2325      0    stevel 
   2326      0    stevel 	if ((qqi = (uint_t)mld2q->mld2q_qqic) >= MLD_V2_QQI_FPMIN) {
   2327      0    stevel 		uint_t mant, exp;
   2328      0    stevel 		mant = qqi & MLD_V2_QQI_MANT_MASK;
   2329      0    stevel 		exp = (qqi & MLD_V2_QQI_EXP_MASK) >> 12;
   2330      0    stevel 		qqi = (mant | 0x10) << (exp + 3);
   2331      0    stevel 	}
   2332      0    stevel 	ill->ill_mcast_qi = (qqi == 0) ? MCAST_DEF_QUERY_INTERVAL : qqi;
   2333      0    stevel 
   2334      0    stevel 	/*
   2335      0    stevel 	 * If we have a pending general query response that's scheduled
   2336      0    stevel 	 * sooner than the delay we calculated for this response, then
   2337      0    stevel 	 * no action is required (MLDv2 draft section 6.2 rule 1)
   2338      0    stevel 	 */
   2339  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   2340   4783      udpa 	if (ill->ill_global_timer < (current + delay)) {
   2341  11042      Erik 		rw_exit(&ill->ill_mcast_lock);
   2342      0    stevel 		return (next);
   2343      0    stevel 	}
   2344      0    stevel 
   2345      0    stevel 	/*
   2346      0    stevel 	 * Now take action depending on query type: general,
   2347      0    stevel 	 * group specific, or group/source specific.
   2348      0    stevel 	 */
   2349      0    stevel 	if ((numsrc == 0) && IN6_IS_ADDR_UNSPECIFIED(v6group)) {
   2350      0    stevel 		/*
   2351      0    stevel 		 * general query
   2352      0    stevel 		 * We know global timer is either not running or is
   2353      0    stevel 		 * greater than our calculated delay, so reset it to
   2354      0    stevel 		 * our delay (random value in range [0, response time])
   2355      0    stevel 		 */
   2356   4783      udpa 		ill->ill_global_timer = current + delay;
   2357   4783      udpa 		next = delay;
   2358      0    stevel 	} else {
   2359      0    stevel 		/* group or group/source specific query */
   2360  11042      Erik 		for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
   2361      0    stevel 			if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr) ||
   2362      0    stevel 			    IN6_IS_ADDR_MC_NODELOCAL(&ilm->ilm_v6addr) ||
   2363      0    stevel 			    IN6_IS_ADDR_MC_RESERVED(&ilm->ilm_v6addr) ||
   2364      0    stevel 			    !IN6_ARE_ADDR_EQUAL(v6group, &ilm->ilm_v6addr))
   2365      0    stevel 				continue;
   2366      0    stevel 
   2367      0    stevel 			/*
   2368      0    stevel 			 * If the query is group specific or we have a
   2369      0    stevel 			 * pending group specific query, the response is
   2370      0    stevel 			 * group specific (pending sources list should be
   2371      0    stevel 			 * empty).  Otherwise, need to update the pending
   2372      0    stevel 			 * sources list for the group and source specific
   2373      0    stevel 			 * response.
   2374      0    stevel 			 */
   2375      0    stevel 			if (numsrc == 0 || (ilm->ilm_timer < INFINITY &&
   2376      0    stevel 			    SLIST_IS_EMPTY(ilm->ilm_pendsrcs))) {
   2377      0    stevel group_query:
   2378      0    stevel 				FREE_SLIST(ilm->ilm_pendsrcs);
   2379      0    stevel 				ilm->ilm_pendsrcs = NULL;
   2380      0    stevel 			} else {
   2381      0    stevel 				boolean_t overflow;
   2382      0    stevel 				slist_t *pktl;
   2383      0    stevel 				if (numsrc > MAX_FILTER_SIZE ||
   2384      0    stevel 				    (ilm->ilm_pendsrcs == NULL &&
   2385      0    stevel 				    (ilm->ilm_pendsrcs = l_alloc()) == NULL)) {
   2386      0    stevel 					/*
   2387      0    stevel 					 * We've been sent more sources than
   2388      0    stevel 					 * we can deal with; or we can't deal
   2389      0    stevel 					 * with a source list at all. Revert
   2390      0    stevel 					 * to a group specific query.
   2391      0    stevel 					 */
   2392      0    stevel 					goto group_query;
   2393      0    stevel 				}
   2394      0    stevel 				if ((pktl = l_alloc()) == NULL)
   2395      0    stevel 					goto group_query;
   2396      0    stevel 				pktl->sl_numsrc = numsrc;
   2397      0    stevel 				for (i = 0; i < numsrc; i++)
   2398      0    stevel 					pktl->sl_addr[i] = src_array[i];
   2399      0    stevel 				l_union_in_a(ilm->ilm_pendsrcs, pktl,
   2400      0    stevel 				    &overflow);
   2401      0    stevel 				l_free(pktl);
   2402      0    stevel 				if (overflow)
   2403      0    stevel 					goto group_query;
   2404      0    stevel 			}
   2405   4783      udpa 			ilm->ilm_timer = (ilm->ilm_timer == INFINITY) ?
   2406   4783      udpa 			    INFINITY : (ilm->ilm_timer - current);
   2407      0    stevel 			/* set timer to soonest value */
   2408      0    stevel 			ilm->ilm_timer = MIN(ilm->ilm_timer, delay);
   2409      0    stevel 			if (ilm->ilm_timer < next)
   2410      0    stevel 				next = ilm->ilm_timer;
   2411   4783      udpa 			ilm->ilm_timer += current;
   2412      0    stevel 			break;
   2413      0    stevel 		}
   2414      0    stevel 	}
   2415  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   2416  11042      Erik 	/*
   2417  11042      Erik 	 * No packets have been sent above - no
   2418  11042      Erik 	 * ill_mcast_send_queued is needed.
   2419  11042      Erik 	 */
   2420  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
   2421      0    stevel 
   2422      0    stevel 	return (next);
   2423      0    stevel }
   2424      0    stevel 
   2425      0    stevel /*
   2426      0    stevel  * Send MLDv1 response packet with hoplimit 1
   2427      0    stevel  */
   2428      0    stevel static void
   2429      0    stevel mld_sendpkt(ilm_t *ilm, uchar_t type, const in6_addr_t *v6addr)
   2430      0    stevel {
   2431      0    stevel 	mblk_t		*mp;
   2432      0    stevel 	mld_hdr_t	*mldh;
   2433      0    stevel 	ip6_t 		*ip6h;
   2434      0    stevel 	ip6_hbh_t	*ip6hbh;
   2435      0    stevel 	struct ip6_opt_router	*ip6router;
   2436      0    stevel 	size_t		size = IPV6_HDR_LEN + sizeof (mld_hdr_t);
   2437   8485     Peter 	ill_t		*ill = ilm->ilm_ill;
   2438  11042      Erik 
   2439  11042      Erik 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
   2440      0    stevel 
   2441      0    stevel 	/*
   2442      0    stevel 	 * We need to place a router alert option in this packet.  The length
   2443      0    stevel 	 * of the options must be a multiple of 8.  The hbh option header is 2
   2444      0    stevel 	 * bytes followed by the 4 byte router alert option.  That leaves
   2445      0    stevel 	 * 2 bytes of pad for a total of 8 bytes.
   2446      0    stevel 	 */
   2447      0    stevel 	const int	router_alert_length = 8;
   2448      0    stevel 
   2449      0    stevel 	ASSERT(ill->ill_isv6);
   2450      0    stevel 
   2451   8485     Peter 	size += router_alert_length;
   2452      0    stevel 	mp = allocb(size, BPRI_HI);
   2453      0    stevel 	if (mp == NULL)
   2454      0    stevel 		return;
   2455      0    stevel 	bzero(mp->b_rptr, size);
   2456      0    stevel 	mp->b_wptr = mp->b_rptr + size;
   2457      0    stevel 
   2458   8485     Peter 	ip6h = (ip6_t *)mp->b_rptr;
   2459      0    stevel 	ip6hbh = (struct ip6_hbh *)&ip6h[1];
   2460      0    stevel 	ip6router = (struct ip6_opt_router *)&ip6hbh[1];
   2461      0    stevel 	/*
   2462      0    stevel 	 * A zero is a pad option of length 1.  The bzero of the whole packet
   2463      0    stevel 	 * above will pad between ip6router and mld.
   2464      0    stevel 	 */
   2465      0    stevel 	mldh = (mld_hdr_t *)((uint8_t *)ip6hbh + router_alert_length);
   2466      0    stevel 
   2467      0    stevel 	mldh->mld_type = type;
   2468      0    stevel 	mldh->mld_addr = ilm->ilm_v6addr;
   2469      0    stevel 
   2470      0    stevel 	ip6router->ip6or_type = IP6OPT_ROUTER_ALERT;
   2471      0    stevel 	ip6router->ip6or_len = 2;
   2472      0    stevel 	ip6router->ip6or_value[0] = 0;
   2473      0    stevel 	ip6router->ip6or_value[1] = IP6_ALERT_MLD;
   2474      0    stevel 
   2475      0    stevel 	ip6hbh->ip6h_nxt = IPPROTO_ICMPV6;
   2476      0    stevel 	ip6hbh->ip6h_len = 0;
   2477      0    stevel 
   2478      0    stevel 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
   2479      0    stevel 	ip6h->ip6_plen = htons(sizeof (*mldh) + router_alert_length);
   2480      0    stevel 	ip6h->ip6_nxt = IPPROTO_HOPOPTS;
   2481      0    stevel 	ip6h->ip6_hops = MLD_HOP_LIMIT;
   2482      0    stevel 	if (v6addr == NULL)
   2483      0    stevel 		ip6h->ip6_dst =  ilm->ilm_v6addr;
   2484      0    stevel 	else
   2485      0    stevel 		ip6h->ip6_dst = *v6addr;
   2486      0    stevel 
   2487  11042      Erik 	ip6h->ip6_src = ipv6_all_zeros;
   2488      0    stevel 	/*
   2489      0    stevel 	 * Prepare for checksum by putting icmp length in the icmp
   2490  11042      Erik 	 * checksum field. The checksum is calculated in ip_output.
   2491      0    stevel 	 */
   2492      0    stevel 	mldh->mld_cksum = htons(sizeof (*mldh));
   2493      0    stevel 
   2494  11042      Erik 	ill_mcast_queue(ill, mp);
   2495      0    stevel }
   2496      0    stevel 
   2497      0    stevel /*
   2498      0    stevel  * Sends an MLD_V2_LISTENER_REPORT message out the passed-in ill.  The
   2499      0    stevel  * report will contain one multicast address record for each element of
   2500  11042      Erik  * reclist.  If this causes packet length to exceed ill->ill_mtu,
   2501      0    stevel  * multiple reports are sent.  reclist is assumed to be made up of
   2502      0    stevel  * buffers allocated by mcast_bldmrec(), and those buffers are freed here.
   2503      0    stevel  */
   2504      0    stevel static void
   2505      0    stevel mldv2_sendrpt(ill_t *ill, mrec_t *reclist)
   2506      0    stevel {
   2507      0    stevel 	mblk_t		*mp;
   2508      0    stevel 	mld2r_t		*mld2r;
   2509      0    stevel 	mld2mar_t	*mld2mar;
   2510      0    stevel 	in6_addr_t	*srcarray;
   2511      0    stevel 	ip6_t		*ip6h;
   2512      0    stevel 	ip6_hbh_t	*ip6hbh;
   2513      0    stevel 	struct ip6_opt_router	*ip6router;
   2514      0    stevel 	size_t		size, optlen, padlen, icmpsize, rsize;
   2515      0    stevel 	int		i, numrec, more_src_cnt;
   2516      0    stevel 	mrec_t		*rp, *cur_reclist;
   2517      0    stevel 	mrec_t		*next_reclist = reclist;
   2518      0    stevel 	boolean_t	morepkts;
   2519      0    stevel 
   2520      0    stevel 	/* If there aren't any records, there's nothing to send */
   2521      0    stevel 	if (reclist == NULL)
   2522      0    stevel 		return;
   2523      0    stevel 
   2524      0    stevel 	ASSERT(ill->ill_isv6);
   2525  11042      Erik 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
   2526      0    stevel 
   2527      0    stevel 	/*
   2528      0    stevel 	 * Total option length (optlen + padlen) must be a multiple of
   2529      0    stevel 	 * 8 bytes.  We assume here that optlen <= 8, so the total option
   2530      0    stevel 	 * length will be 8.  Assert this in case anything ever changes.
   2531      0    stevel 	 */
   2532      0    stevel 	optlen = sizeof (ip6_hbh_t) + sizeof (struct ip6_opt_router);
   2533      0    stevel 	ASSERT(optlen <= 8);
   2534      0    stevel 	padlen = 8 - optlen;
   2535      0    stevel nextpkt:
   2536      0    stevel 	icmpsize = sizeof (mld2r_t);
   2537      0    stevel 	size = IPV6_HDR_LEN + optlen + padlen + icmpsize;
   2538      0    stevel 	morepkts = B_FALSE;
   2539      0    stevel 	more_src_cnt = 0;
   2540      0    stevel 	for (rp = cur_reclist = next_reclist, numrec = 0; rp != NULL;
   2541      0    stevel 	    rp = rp->mrec_next, numrec++) {
   2542      0    stevel 		rsize = sizeof (mld2mar_t) +
   2543      0    stevel 		    (rp->mrec_srcs.sl_numsrc * sizeof (in6_addr_t));
   2544  11042      Erik 		if (size + rsize > ill->ill_mtu) {
   2545      0    stevel 			if (rp == cur_reclist) {
   2546      0    stevel 				/*
   2547      0    stevel 				 * If the first mrec we looked at is too big
   2548      0    stevel 				 * to fit in a single packet (i.e the source
   2549      0    stevel 				 * list is too big), we must either truncate
   2550      0    stevel 				 * the list (if TO_EX or IS_EX), or send
   2551      0    stevel 				 * multiple reports for the same group (all
   2552      0    stevel 				 * other types).
   2553      0    stevel 				 */
   2554      0    stevel 				int srcspace, srcsperpkt;
   2555  11042      Erik 				srcspace = ill->ill_mtu -
   2556      0    stevel 				    (size + sizeof (mld2mar_t));
   2557   8485     Peter 
   2558   8485     Peter 				/*
   2559   8485     Peter 				 * Skip if there's not even enough room in
   2560   8485     Peter 				 * a single packet to send something useful.
   2561   8485     Peter 				 */
   2562   8485     Peter 				if (srcspace <= sizeof (in6_addr_t))
   2563   8485     Peter 					continue;
   2564   8485     Peter 
   2565      0    stevel 				srcsperpkt = srcspace / sizeof (in6_addr_t);
   2566      0    stevel 				/*
   2567      0    stevel 				 * Increment icmpsize and size, because we will
   2568      0    stevel 				 * be sending a record for the mrec we're
   2569      0    stevel 				 * looking at now.
   2570      0    stevel 				 */
   2571      0    stevel 				rsize = sizeof (mld2mar_t) +
   2572      0    stevel 				    (srcsperpkt * sizeof (in6_addr_t));
   2573      0    stevel 				icmpsize += rsize;
   2574      0    stevel 				size += rsize;
   2575      0    stevel 				if (rp->mrec_type == MODE_IS_EXCLUDE ||
   2576      0    stevel 				    rp->mrec_type == CHANGE_TO_EXCLUDE) {
   2577      0    stevel 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
   2578      0    stevel 					if (rp->mrec_next == NULL) {
   2579      0    stevel 						/* no more packets to send */
   2580      0    stevel 						break;
   2581      0    stevel 					} else {
   2582      0    stevel 						/*
   2583      0    stevel 						 * more packets, but we're
   2584      0    stevel 						 * done with this mrec.
   2585      0    stevel 						 */
   2586      0    stevel 						next_reclist = rp->mrec_next;
   2587      0    stevel 					}
   2588      0    stevel 				} else {
   2589      0    stevel 					more_src_cnt = rp->mrec_srcs.sl_numsrc
   2590      0    stevel 					    - srcsperpkt;
   2591      0    stevel 					rp->mrec_srcs.sl_numsrc = srcsperpkt;
   2592      0    stevel 					/*
   2593      0    stevel 					 * We'll fix up this mrec (remove the
   2594      0    stevel 					 * srcs we've already sent) before
   2595      0    stevel 					 * returning to nextpkt above.
   2596      0    stevel 					 */
   2597      0    stevel 					next_reclist = rp;
   2598      0    stevel 				}
   2599      0    stevel 			} else {
   2600      0    stevel 				next_reclist = rp;
   2601      0    stevel 			}
   2602      0    stevel 			morepkts = B_TRUE;
   2603      0    stevel 			break;
   2604      0    stevel 		}
   2605      0    stevel 		icmpsize += rsize;
   2606      0    stevel 		size += rsize;
   2607      0    stevel 	}
   2608      0    stevel 
   2609      0    stevel 	mp = allocb(size, BPRI_HI);
   2610      0    stevel 	if (mp == NULL)
   2611      0    stevel 		goto free_reclist;
   2612      0    stevel 	bzero(mp->b_rptr, size);
   2613      0    stevel 	mp->b_wptr = mp->b_rptr + size;
   2614      0    stevel 
   2615   8485     Peter 	ip6h = (ip6_t *)mp->b_rptr;
   2616      0    stevel 	ip6hbh = (ip6_hbh_t *)&(ip6h[1]);
   2617      0    stevel 	ip6router = (struct ip6_opt_router *)&(ip6hbh[1]);
   2618      0    stevel 	mld2r = (mld2r_t *)((uint8_t *)ip6hbh + optlen + padlen);
   2619      0    stevel 	mld2mar = (mld2mar_t *)&(mld2r[1]);
   2620      0    stevel 
   2621      0    stevel 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
   2622      0    stevel 	ip6h->ip6_plen = htons(optlen + padlen + icmpsize);
   2623      0    stevel 	ip6h->ip6_nxt = IPPROTO_HOPOPTS;
   2624      0    stevel 	ip6h->ip6_hops = MLD_HOP_LIMIT;
   2625      0    stevel 	ip6h->ip6_dst = ipv6_all_v2rtrs_mcast;
   2626  11042      Erik 	ip6h->ip6_src = ipv6_all_zeros;
   2627      0    stevel 
   2628      0    stevel 	ip6hbh->ip6h_nxt = IPPROTO_ICMPV6;
   2629      0    stevel 	/*
   2630      0    stevel 	 * ip6h_len is the number of 8-byte words, not including the first
   2631      0    stevel 	 * 8 bytes; we've assumed optlen + padlen == 8 bytes; hence len = 0.
   2632      0    stevel 	 */
   2633      0    stevel 	ip6hbh->ip6h_len = 0;
   2634      0    stevel 
   2635      0    stevel 	ip6router->ip6or_type = IP6OPT_ROUTER_ALERT;
   2636      0    stevel 	ip6router->ip6or_len = 2;
   2637      0    stevel 	ip6router->ip6or_value[0] = 0;
   2638      0    stevel 	ip6router->ip6or_value[1] = IP6_ALERT_MLD;
   2639      0    stevel 
   2640      0    stevel 	mld2r->mld2r_type = MLD_V2_LISTENER_REPORT;
   2641      0    stevel 	mld2r->mld2r_nummar = htons(numrec);
   2642      0    stevel 	/*
   2643      0    stevel 	 * Prepare for the checksum by putting icmp length in the icmp
   2644  11042      Erik 	 * checksum field. The checksum is calculated in ip_output_simple.
   2645      0    stevel 	 */
   2646      0    stevel 	mld2r->mld2r_cksum = htons(icmpsize);
   2647      0    stevel 
   2648      0    stevel 	for (rp = cur_reclist; rp != NULL; rp = rp->mrec_next) {
   2649      0    stevel 		mld2mar->mld2mar_type = rp->mrec_type;
   2650      0    stevel 		mld2mar->mld2mar_auxlen = 0;
   2651      0    stevel 		mld2mar->mld2mar_numsrc = htons(rp->mrec_srcs.sl_numsrc);
   2652      0    stevel 		mld2mar->mld2mar_group = rp->mrec_group;
   2653      0    stevel 		srcarray = (in6_addr_t *)&(mld2mar[1]);
   2654      0    stevel 
   2655      0    stevel 		for (i = 0; i < rp->mrec_srcs.sl_numsrc; i++)
   2656      0    stevel 			srcarray[i] = rp->mrec_srcs.sl_addr[i];
   2657      0    stevel 
   2658      0    stevel 		mld2mar = (mld2mar_t *)&(srcarray[i]);
   2659      0    stevel 	}
   2660      0    stevel 
   2661  11042      Erik 	ill_mcast_queue(ill, mp);
   2662      0    stevel 
   2663      0    stevel 	if (morepkts) {
   2664      0    stevel 		if (more_src_cnt > 0) {
   2665      0    stevel 			int index, mvsize;
   2666      0    stevel 			slist_t *sl = &next_reclist->mrec_srcs;
   2667      0    stevel 			index = sl->sl_numsrc;
   2668      0    stevel 			mvsize = more_src_cnt * sizeof (in6_addr_t);
   2669      0    stevel 			(void) memmove(&sl->sl_addr[0], &sl->sl_addr[index],
   2670      0    stevel 			    mvsize);
   2671      0    stevel 			sl->sl_numsrc = more_src_cnt;
   2672      0    stevel 		}
   2673      0    stevel 		goto nextpkt;
   2674      0    stevel 	}
   2675      0    stevel 
   2676      0    stevel free_reclist:
   2677      0    stevel 	while (reclist != NULL) {
   2678      0    stevel 		rp = reclist->mrec_next;
   2679      0    stevel 		mi_free(reclist);
   2680      0    stevel 		reclist = rp;
   2681      0    stevel 	}
   2682      0    stevel }
   2683      0    stevel 
   2684      0    stevel static mrec_t *
   2685      0    stevel mcast_bldmrec(mcast_record_t type, in6_addr_t *grp, slist_t *srclist,
   2686      0    stevel     mrec_t *next)
   2687      0    stevel {
   2688      0    stevel 	mrec_t *rp;
   2689      0    stevel 	int i;
   2690      0    stevel 
   2691      0    stevel 	if ((type == ALLOW_NEW_SOURCES || type == BLOCK_OLD_SOURCES) &&
   2692      0    stevel 	    SLIST_IS_EMPTY(srclist))
   2693      0    stevel 		return (next);
   2694      0    stevel 
   2695      0    stevel 	rp = (mrec_t *)mi_alloc(sizeof (mrec_t), BPRI_HI);
   2696      0    stevel 	if (rp == NULL)
   2697      0    stevel 		return (next);
   2698      0    stevel 
   2699      0    stevel 	rp->mrec_next = next;
   2700      0    stevel 	rp->mrec_type = type;
   2701      0    stevel 	rp->mrec_auxlen = 0;
   2702      0    stevel 	rp->mrec_group = *grp;
   2703      0    stevel 	if (srclist == NULL) {
   2704      0    stevel 		rp->mrec_srcs.sl_numsrc = 0;
   2705      0    stevel 	} else {
   2706      0    stevel 		rp->mrec_srcs.sl_numsrc = srclist->sl_numsrc;
   2707      0    stevel 		for (i = 0; i < srclist->sl_numsrc; i++)
   2708      0    stevel 			rp->mrec_srcs.sl_addr[i] = srclist->sl_addr[i];
   2709      0    stevel 	}
   2710      0    stevel 
   2711      0    stevel 	return (rp);
   2712      0    stevel }
   2713      0    stevel 
   2714      0    stevel /*
   2715      0    stevel  * Set up initial retransmit state.  If memory cannot be allocated for
   2716      0    stevel  * the source lists, simply create as much state as is possible; memory
   2717      0    stevel  * allocation failures are considered one type of transient error that
   2718      0    stevel  * the retransmissions are designed to overcome (and if they aren't
   2719      0    stevel  * transient, there are bigger problems than failing to notify the
   2720      0    stevel  * router about multicast group membership state changes).
   2721      0    stevel  */
   2722      0    stevel static void
   2723      0    stevel mcast_init_rtx(ill_t *ill, rtx_state_t *rtxp, mcast_record_t rtype,
   2724      0    stevel     slist_t *flist)
   2725      0    stevel {
   2726      0    stevel 	/*
   2727      0    stevel 	 * There are only three possibilities for rtype:
   2728      0    stevel 	 *	New join, transition from INCLUDE {} to INCLUDE {flist}
   2729      0    stevel 	 *	  => rtype is ALLOW_NEW_SOURCES
   2730      0    stevel 	 *	New join, transition from INCLUDE {} to EXCLUDE {flist}
   2731      0    stevel 	 *	  => rtype is CHANGE_TO_EXCLUDE
   2732      0    stevel 	 *	State change that involves a filter mode change
   2733      0    stevel 	 *	  => rtype is either CHANGE_TO_INCLUDE or CHANGE_TO_EXCLUDE
   2734      0    stevel 	 */
   2735      0    stevel 	ASSERT(rtype == CHANGE_TO_EXCLUDE || rtype == CHANGE_TO_INCLUDE ||
   2736      0    stevel 	    rtype == ALLOW_NEW_SOURCES);
   2737      0    stevel 
   2738      0    stevel 	rtxp->rtx_cnt = ill->ill_mcast_rv;
   2739      0    stevel 
   2740      0    stevel 	switch (rtype) {
   2741      0    stevel 	case CHANGE_TO_EXCLUDE:
   2742      0    stevel 		rtxp->rtx_fmode_cnt = ill->ill_mcast_rv;
   2743      0    stevel 		CLEAR_SLIST(rtxp->rtx_allow);
   2744      0    stevel 		COPY_SLIST(flist, rtxp->rtx_block);
   2745      0    stevel 		break;
   2746      0    stevel 	case ALLOW_NEW_SOURCES:
   2747      0    stevel 	case CHANGE_TO_INCLUDE:
   2748      0    stevel 		rtxp->rtx_fmode_cnt =
   2749      0    stevel 		    rtype == ALLOW_NEW_SOURCES ? 0 : ill->ill_mcast_rv;
   2750      0    stevel 		CLEAR_SLIST(rtxp->rtx_block);
   2751      0    stevel 		COPY_SLIST(flist, rtxp->rtx_allow);
   2752      0    stevel 		break;
   2753      0    stevel 	}
   2754      0    stevel }
   2755      0    stevel 
   2756      0    stevel /*
   2757      0    stevel  * The basic strategy here, as extrapolated from RFC 3810 section 6.1 and
   2758      0    stevel  * RFC 3376 section 5.1, covers three cases:
   2759      0    stevel  *	* The current state change is a filter mode change
   2760      0    stevel  *		Set filter mode retransmit counter; set retransmit allow or
   2761      0    stevel  *		block list to new source list as appropriate, and clear the
   2762      0    stevel  *		retransmit list that was not set; send TO_IN or TO_EX with
   2763      0    stevel  *		new source list.
   2764      0    stevel  *	* The current state change is a source list change, but the filter
   2765      0    stevel  *	  mode retransmit counter is > 0
   2766      0    stevel  *		Decrement filter mode retransmit counter; set retransmit
   2767      0    stevel  *		allow or block list to  new source list as appropriate,
   2768      0    stevel  *		and clear the retransmit list that was not set; send TO_IN
   2769      0    stevel  *		or TO_EX with new source list.
   2770      0    stevel  *	* The current state change is a source list change, and the filter
   2771      0    stevel  *	  mode retransmit counter is 0.
   2772      0    stevel  *		Merge existing rtx allow and block lists with new state:
   2773      0    stevel  *		  rtx_allow = (new allow + rtx_allow) - new block
   2774      0    stevel  *		  rtx_block = (new block + rtx_block) - new allow
   2775      0    stevel  *		Send ALLOW and BLOCK records for new retransmit lists;
   2776      0    stevel  *		decrement retransmit counter.
   2777      0    stevel  *
   2778      0    stevel  * As is the case for mcast_init_rtx(), memory allocation failures are
   2779      0    stevel  * acceptable; we just create as much state as we can.
   2780      0    stevel  */
   2781      0    stevel static mrec_t *
   2782      0    stevel mcast_merge_rtx(ilm_t *ilm, mrec_t *mreclist, slist_t *flist)
   2783      0    stevel {
   2784      0    stevel 	ill_t *ill;
   2785      0    stevel 	rtx_state_t *rtxp = &ilm->ilm_rtx;
   2786      0    stevel 	mcast_record_t txtype;
   2787      0    stevel 	mrec_t *rp, *rpnext, *rtnmrec;
   2788      0    stevel 	boolean_t ovf;
   2789      0    stevel 
   2790  11042      Erik 	ill = ilm->ilm_ill;
   2791      0    stevel 
   2792      0    stevel 	if (mreclist == NULL)
   2793      0    stevel 		return (mreclist);
   2794      0    stevel 
   2795      0    stevel 	/*
   2796      0    stevel 	 * A filter mode change is indicated by a single mrec, which is
   2797      0    stevel 	 * either TO_IN or TO_EX.  In this case, we just need to set new
   2798      0    stevel 	 * retransmit state as if this were an initial join.  There is
   2799      0    stevel 	 * no change to the mrec list.
   2800      0    stevel 	 */
   2801      0    stevel 	if (mreclist->mrec_type == CHANGE_TO_INCLUDE ||
   2802      0    stevel 	    mreclist->mrec_type == CHANGE_TO_EXCLUDE) {
   2803      0    stevel 		mcast_init_rtx(ill, rtxp, mreclist->mrec_type,
   2804      0    stevel 		    &mreclist->mrec_srcs);
   2805      0    stevel 		return (mreclist);
   2806      0    stevel 	}
   2807      0    stevel 
   2808      0    stevel 	/*
   2809      0    stevel 	 * Only the source list has changed
   2810      0    stevel 	 */
   2811      0    stevel 	rtxp->rtx_cnt = ill->ill_mcast_rv;
   2812      0    stevel 	if (rtxp->rtx_fmode_cnt > 0) {
   2813      0    stevel 		/* but we're still sending filter mode change reports */
   2814      0    stevel 		rtxp->rtx_fmode_cnt--;
   2815      0    stevel 		if (ilm->ilm_fmode == MODE_IS_INCLUDE) {
   2816      0    stevel 			CLEAR_SLIST(rtxp->rtx_block);
   2817      0    stevel 			COPY_SLIST(flist, rtxp->rtx_allow);
   2818      0    stevel 			txtype = CHANGE_TO_INCLUDE;
   2819      0    stevel 		} else {
   2820      0    stevel 			CLEAR_SLIST(rtxp->rtx_allow);
   2821      0    stevel 			COPY_SLIST(flist, rtxp->rtx_block);
   2822      0    stevel 			txtype = CHANGE_TO_EXCLUDE;
   2823      0    stevel 		}
   2824      0    stevel 		/* overwrite first mrec with new info */
   2825      0    stevel 		mreclist->mrec_type = txtype;
   2826      0    stevel 		l_copy(flist, &mreclist->mrec_srcs);
   2827      0    stevel 		/* then free any remaining mrecs */
   2828      0    stevel 		for (rp = mreclist->mrec_next; rp != NULL; rp = rpnext) {
   2829      0    stevel 			rpnext = rp->mrec_next;
   2830      0    stevel 			mi_free(rp);
   2831      0    stevel 		}
   2832      0    stevel 		mreclist->mrec_next = NULL;
   2833      0    stevel 		rtnmrec = mreclist;
   2834      0    stevel 	} else {
   2835      0    stevel 		mrec_t *allow_mrec, *block_mrec;
   2836      0    stevel 		/*
   2837      0    stevel 		 * Just send the source change reports; but we need to
   2838      0    stevel 		 * recalculate the ALLOW and BLOCK lists based on previous
   2839      0    stevel 		 * state and new changes.
   2840      0    stevel 		 */
   2841      0    stevel 		rtnmrec = mreclist;
   2842      0    stevel 		allow_mrec = block_mrec = NULL;
   2843      0    stevel 		for (rp = mreclist; rp != NULL; rp = rp->mrec_next) {
   2844      0    stevel 			ASSERT(rp->mrec_type == ALLOW_NEW_SOURCES ||
   2845      0    stevel 			    rp->mrec_type == BLOCK_OLD_SOURCES);
   2846      0    stevel 			if (rp->mrec_type == ALLOW_NEW_SOURCES)
   2847      0    stevel 				allow_mrec = rp;
   2848      0    stevel 			else
   2849      0    stevel 				block_mrec = rp;
   2850      0    stevel 		}
   2851      0    stevel 		/*
   2852      0    stevel 		 * Perform calculations:
   2853      0    stevel 		 *   new_allow = mrec_allow + (rtx_allow - mrec_block)
   2854      0    stevel 		 *   new_block = mrec_block + (rtx_block - mrec_allow)
   2855      0    stevel 		 *
   2856      0    stevel 		 * Each calc requires two steps, for example:
   2857      0    stevel 		 *   rtx_allow = rtx_allow - mrec_block;
   2858      0    stevel 		 *   new_allow = mrec_allow + rtx_allow;
   2859      0    stevel 		 *
   2860      0    stevel 		 * Store results in mrec lists, and then copy into rtx lists.
   2861      0    stevel 		 * We do it in this order in case the rtx list hasn't been
   2862      0    stevel 		 * alloc'd yet; if it hasn't and our alloc fails, that's okay,
   2863      0    stevel 		 * Overflows are also okay.
   2864      0    stevel 		 */
   2865      0    stevel 		if (block_mrec != NULL) {
   2866      0    stevel 			l_difference_in_a(rtxp->rtx_allow,
   2867      0    stevel 			    &block_mrec->mrec_srcs);
   2868      0    stevel 		}
   2869      0    stevel 		if (allow_mrec != NULL) {
   2870      0    stevel 			l_difference_in_a(rtxp->rtx_block,
   2871      0    stevel 			    &allow_mrec->mrec_srcs);
   2872      0    stevel 			l_union_in_a(&allow_mrec->mrec_srcs, rtxp->rtx_allow,
   2873      0    stevel 			    &ovf);
   2874      0    stevel 		}
   2875      0    stevel 		if (block_mrec != NULL) {
   2876      0    stevel 			l_union_in_a(&block_mrec->mrec_srcs, rtxp->rtx_block,
   2877      0    stevel 			    &ovf);
   2878      0    stevel 			COPY_SLIST(&block_mrec->mrec_srcs, rtxp->rtx_block);
   2879      0    stevel 		} else {
   2880      0    stevel 			rtnmrec = mcast_bldmrec(BLOCK_OLD_SOURCES,
   2881      0    stevel 			    &ilm->ilm_v6addr, rtxp->rtx_block, allow_mrec);
   2882      0    stevel 		}
   2883      0    stevel 		if (allow_mrec != NULL) {
   2884      0    stevel 			COPY_SLIST(&allow_mrec->mrec_srcs, rtxp->rtx_allow);
   2885      0    stevel 		} else {
   2886      0    stevel 			rtnmrec = mcast_bldmrec(ALLOW_NEW_SOURCES,
   2887      0    stevel 			    &ilm->ilm_v6addr, rtxp->rtx_allow, block_mrec);
   2888      0    stevel 		}
   2889      0    stevel 	}
   2890      0    stevel 
   2891      0    stevel 	return (rtnmrec);
   2892      0    stevel }
   2893