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