xref: /illumos-gate/usr/src/uts/common/inet/ip/igmp.c (revision ab82c29b)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
221eee170aSErik Nordmark  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Internet Group Management Protocol (IGMP) routines.
287c478bd9Sstevel@tonic-gate  * Multicast Listener Discovery Protocol (MLD) routines.
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * Written by Steve Deering, Stanford, May 1988.
317c478bd9Sstevel@tonic-gate  * Modified by Rosen Sharma, Stanford, Aug 1994.
327c478bd9Sstevel@tonic-gate  * Modified by Bill Fenner, Xerox PARC, Feb. 1995.
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * MULTICAST 3.5.1.1
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/types.h>
387c478bd9Sstevel@tonic-gate #include <sys/stream.h>
397c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
407c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
417c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
427c478bd9Sstevel@tonic-gate #include <sys/systm.h>
437c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
447c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
457c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
467c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
477c478bd9Sstevel@tonic-gate #include <sys/zone.h>
48e11c3f44Smeem #include <sys/callb.h>
497c478bd9Sstevel@tonic-gate #include <sys/param.h>
507c478bd9Sstevel@tonic-gate #include <sys/socket.h>
517c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
527c478bd9Sstevel@tonic-gate #include <net/if.h>
537c478bd9Sstevel@tonic-gate #include <net/route.h>
547c478bd9Sstevel@tonic-gate #include <netinet/in.h>
557c478bd9Sstevel@tonic-gate #include <netinet/igmp_var.h>
567c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
577c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
58bd670b35SErik Nordmark #include <inet/ipsec_impl.h>
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate #include <inet/common.h>
617c478bd9Sstevel@tonic-gate #include <inet/mi.h>
627c478bd9Sstevel@tonic-gate #include <inet/nd.h>
636e91bba0SGirish Moodalbail #include <inet/tunables.h>
647c478bd9Sstevel@tonic-gate #include <inet/ip.h>
657c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
667c478bd9Sstevel@tonic-gate #include <inet/ip_multi.h>
677c478bd9Sstevel@tonic-gate #include <inet/ip_listutils.h>
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #include <netinet/igmp.h>
70bd670b35SErik Nordmark #include <inet/ip_ndp.h>
717c478bd9Sstevel@tonic-gate #include <inet/ip_if.h>
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate static uint_t	igmp_query_in(ipha_t *ipha, igmpa_t *igmpa, ill_t *ill);
747c478bd9Sstevel@tonic-gate static uint_t	igmpv3_query_in(igmp3qa_t *igmp3qa, ill_t *ill, int igmplen);
757c478bd9Sstevel@tonic-gate static uint_t	mld_query_in(mld_hdr_t *mldh, ill_t *ill);
767c478bd9Sstevel@tonic-gate static uint_t	mldv2_query_in(mld2q_t *mld2q, ill_t *ill, int mldlen);
777c478bd9Sstevel@tonic-gate static void	igmp_sendpkt(ilm_t *ilm, uchar_t type, ipaddr_t addr);
787c478bd9Sstevel@tonic-gate static void	mld_sendpkt(ilm_t *ilm, uchar_t type, const in6_addr_t *v6addr);
79bd670b35SErik Nordmark static void	igmpv3_sendrpt(ill_t *ill, mrec_t *reclist);
807c478bd9Sstevel@tonic-gate static void	mldv2_sendrpt(ill_t *ill, mrec_t *reclist);
817c478bd9Sstevel@tonic-gate static mrec_t	*mcast_bldmrec(mcast_record_t type, in6_addr_t *grp,
827c478bd9Sstevel@tonic-gate 		    slist_t *srclist, mrec_t *next);
837c478bd9Sstevel@tonic-gate static void	mcast_init_rtx(ill_t *ill, rtx_state_t *rtxp,
847c478bd9Sstevel@tonic-gate 		    mcast_record_t rtype, slist_t *flist);
857c478bd9Sstevel@tonic-gate static mrec_t	*mcast_merge_rtx(ilm_t *ilm, mrec_t *rp, slist_t *flist);
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * Macros used to do timer len conversions.  Timer values are always
897c478bd9Sstevel@tonic-gate  * stored and passed to the timer functions as milliseconds; but the
907c478bd9Sstevel@tonic-gate  * default values and values from the wire may not be.
917c478bd9Sstevel@tonic-gate  *
927c478bd9Sstevel@tonic-gate  * And yes, it's obscure, but decisecond is easier to abbreviate than
937c478bd9Sstevel@tonic-gate  * "tenths of a second".
947c478bd9Sstevel@tonic-gate  */
957c478bd9Sstevel@tonic-gate #define	DSEC_TO_MSEC(dsec)	((dsec) * 100)
967c478bd9Sstevel@tonic-gate #define	SEC_TO_MSEC(sec)	((sec) * 1000)
977c478bd9Sstevel@tonic-gate 
988dc47d9fSudpa /*
998dc47d9fSudpa  * A running timer (scheduled thru timeout) can be cancelled if another
1008dc47d9fSudpa  * timer with a shorter timeout value is scheduled before it has timed
1018dc47d9fSudpa  * out.  When the shorter timer expires, the original timer is updated
1028dc47d9fSudpa  * to account for the time elapsed while the shorter timer ran; but this
1038dc47d9fSudpa  * does not take into account the amount of time already spent in timeout
1048dc47d9fSudpa  * state before being preempted by the shorter timer, that is the time
1058dc47d9fSudpa  * interval between time scheduled to time cancelled.  This can cause
1068dc47d9fSudpa  * delays in sending out multicast membership reports.  To resolve this
1078dc47d9fSudpa  * problem, wallclock time (absolute time) is used instead of deltas
1088dc47d9fSudpa  * (relative time) to track timers.
1098dc47d9fSudpa  *
1108dc47d9fSudpa  * The MACRO below gets the lbolt value, used for proper timer scheduling
1118dc47d9fSudpa  * and firing. Therefore multicast membership reports are sent on time.
1128dc47d9fSudpa  * The timer does not exactly fire at the time it was scehduled to fire,
1138dc47d9fSudpa  * there is a difference of a few milliseconds observed. An offset is used
1148dc47d9fSudpa  * to take care of the difference.
1158dc47d9fSudpa  */
1168dc47d9fSudpa 
1178dc47d9fSudpa #define	CURRENT_MSTIME	((uint_t)TICK_TO_MSEC(ddi_get_lbolt()))
1188dc47d9fSudpa #define	CURRENT_OFFSET	(999)
1198dc47d9fSudpa 
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate  * The first multicast join will trigger the igmp timers / mld timers
1227c478bd9Sstevel@tonic-gate  * The unit for next is milliseconds.
1237c478bd9Sstevel@tonic-gate  */
124bd670b35SErik Nordmark void
igmp_start_timers(unsigned next,ip_stack_t * ipst)125f4b3ec61Sdh igmp_start_timers(unsigned next, ip_stack_t *ipst)
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	int	time_left;
1287c478bd9Sstevel@tonic-gate 	int	ret;
129bd670b35SErik Nordmark 	timeout_id_t id;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	ASSERT(next != 0 && next != INFINITY);
1327c478bd9Sstevel@tonic-gate 
133f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1347c478bd9Sstevel@tonic-gate 
135f4b3ec61Sdh 	if (ipst->ips_igmp_timer_setter_active) {
1367c478bd9Sstevel@tonic-gate 		/*
1377c478bd9Sstevel@tonic-gate 		 * Serialize timer setters, one at a time. If the
1387c478bd9Sstevel@tonic-gate 		 * timer is currently being set by someone,
1397c478bd9Sstevel@tonic-gate 		 * just record the next time when it has to be
1407c478bd9Sstevel@tonic-gate 		 * invoked and return. The current setter will
1417c478bd9Sstevel@tonic-gate 		 * take care.
1427c478bd9Sstevel@tonic-gate 		 */
143f4b3ec61Sdh 		ipst->ips_igmp_time_to_next =
144f4b3ec61Sdh 		    MIN(ipst->ips_igmp_time_to_next, next);
145f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1467c478bd9Sstevel@tonic-gate 		return;
1477c478bd9Sstevel@tonic-gate 	} else {
148f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_TRUE;
1497c478bd9Sstevel@tonic-gate 	}
150f4b3ec61Sdh 	if (ipst->ips_igmp_timeout_id == 0) {
1517c478bd9Sstevel@tonic-gate 		/*
152f5db8fb0SRobert Mustacchi 		 * The timer is inactive. We need to start a timer if we haven't
153f5db8fb0SRobert Mustacchi 		 * been asked to quiesce.
1547c478bd9Sstevel@tonic-gate 		 */
155f4b3ec61Sdh 		ipst->ips_igmp_time_to_next = next;
156f5db8fb0SRobert Mustacchi 		if (ipst->ips_igmp_timer_quiesce != B_TRUE) {
157f5db8fb0SRobert Mustacchi 			ipst->ips_igmp_timeout_id =
158f5db8fb0SRobert Mustacchi 			    timeout(igmp_timeout_handler, (void *)ipst,
159f5db8fb0SRobert Mustacchi 			    MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
160f5db8fb0SRobert Mustacchi 			ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
161f5db8fb0SRobert Mustacchi 		}
162f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_FALSE;
163f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1647c478bd9Sstevel@tonic-gate 		return;
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	/*
1687c478bd9Sstevel@tonic-gate 	 * The timer was scheduled sometime back for firing in
1697c478bd9Sstevel@tonic-gate 	 * 'igmp_time_to_next' ms and is active. We need to
1707c478bd9Sstevel@tonic-gate 	 * reschedule the timeout if the new 'next' will happen
1717c478bd9Sstevel@tonic-gate 	 * earlier than the currently scheduled timeout
1727c478bd9Sstevel@tonic-gate 	 */
1738dc47d9fSudpa 	time_left = ipst->ips_igmp_timer_scheduled_last +
174f4b3ec61Sdh 	    MSEC_TO_TICK(ipst->ips_igmp_time_to_next) - ddi_get_lbolt();
1757c478bd9Sstevel@tonic-gate 	if (time_left < MSEC_TO_TICK(next)) {
176f4b3ec61Sdh 		ipst->ips_igmp_timer_setter_active = B_FALSE;
177f4b3ec61Sdh 		mutex_exit(&ipst->ips_igmp_timer_lock);
1787c478bd9Sstevel@tonic-gate 		return;
1797c478bd9Sstevel@tonic-gate 	}
180bd670b35SErik Nordmark 	id = ipst->ips_igmp_timeout_id;
1817c478bd9Sstevel@tonic-gate 
182f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
183bd670b35SErik Nordmark 	ret = untimeout(id);
184f4b3ec61Sdh 	mutex_enter(&ipst->ips_igmp_timer_lock);
1857c478bd9Sstevel@tonic-gate 	/*
1867c478bd9Sstevel@tonic-gate 	 * The timeout was cancelled, or the timeout handler
1877c478bd9Sstevel@tonic-gate 	 * completed, while we were blocked in the untimeout.
1887c478bd9Sstevel@tonic-gate 	 * No other thread could have set the timer meanwhile
1897c478bd9Sstevel@tonic-gate 	 * since we serialized all the timer setters. Thus
1907c478bd9Sstevel@tonic-gate 	 * no timer is currently active nor executing nor will
1917c478bd9Sstevel@tonic-gate 	 * any timer fire in the future. We start the timer now
1927c478bd9Sstevel@tonic-gate 	 * if needed.
1937c478bd9Sstevel@tonic-gate 	 */
1947c478bd9Sstevel@tonic-gate 	if (ret == -1) {
195f4b3ec61Sdh 		ASSERT(ipst->ips_igmp_timeout_id == 0);
1967c478bd9Sstevel@tonic-gate 	} else {
197f4b3ec61Sdh 		ASSERT(ipst->ips_igmp_timeout_id != 0);
198f4b3ec61Sdh 		ipst->ips_igmp_timeout_id = 0;
1997c478bd9Sstevel@tonic-gate 	}
200f5db8fb0SRobert Mustacchi 	if (ipst->ips_igmp_time_to_next != 0 &&
201f5db8fb0SRobert Mustacchi 	    ipst->ips_igmp_timer_quiesce != B_TRUE) {
202f4b3ec61Sdh 		ipst->ips_igmp_time_to_next =
203f4b3ec61Sdh 		    MIN(ipst->ips_igmp_time_to_next, next);
204f4b3ec61Sdh 		ipst->ips_igmp_timeout_id = timeout(igmp_timeout_handler,
205f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_igmp_time_to_next));
2068dc47d9fSudpa 		ipst->ips_igmp_timer_scheduled_last = ddi_get_lbolt();
2077c478bd9Sstevel@tonic-gate 	}
208f4b3ec61Sdh 	ipst->ips_igmp_timer_setter_active = B_FALSE;
209f4b3ec61Sdh 	mutex_exit(&ipst->ips_igmp_timer_lock);
2107c478bd9Sstevel@tonic-gate }
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate /*
2137c478bd9Sstevel@tonic-gate  * mld_start_timers:
2147c478bd9Sstevel@tonic-gate  * The unit for next is milliseconds.
2157c478bd9Sstevel@tonic-gate  */
216bd670b35SErik Nordmark void
mld_start_timers(unsigned next,ip_stack_t * ipst)217f4b3ec61Sdh mld_start_timers(unsigned next, ip_stack_t *ipst)
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	int	time_left;
2207c478bd9Sstevel@tonic-gate 	int	ret;
221bd670b35SErik Nordmark 	timeout_id_t id;
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	ASSERT(next != 0 && next != INFINITY);
2247c478bd9Sstevel@tonic-gate 
225f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
226f4b3ec61Sdh 	if (ipst->ips_mld_timer_setter_active) {
2277c478bd9Sstevel@tonic-gate 		/*
2287c478bd9Sstevel@tonic-gate 		 * Serialize timer setters, one at a time. If the
2297c478bd9Sstevel@tonic-gate 		 * timer is currently being set by someone,
2307c478bd9Sstevel@tonic-gate 		 * just record the next time when it has to be
2317c478bd9Sstevel@tonic-gate 		 * invoked and return. The current setter will
2327c478bd9Sstevel@tonic-gate 		 * take care.
2337c478bd9Sstevel@tonic-gate 		 */
234f4b3ec61Sdh 		ipst->ips_mld_time_to_next =
235f4b3ec61Sdh 		    MIN(ipst->ips_mld_time_to_next, next);
236f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2377c478bd9Sstevel@tonic-gate 		return;
2387c478bd9Sstevel@tonic-gate 	} else {
239f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_TRUE;
2407c478bd9Sstevel@tonic-gate 	}
241f4b3ec61Sdh 	if (ipst->ips_mld_timeout_id == 0) {
2427c478bd9Sstevel@tonic-gate 		/*
243f5db8fb0SRobert Mustacchi 		 * The timer is inactive. We need to start a timer, if we
244f5db8fb0SRobert Mustacchi 		 * haven't been asked to quiesce.
2457c478bd9Sstevel@tonic-gate 		 */
246f4b3ec61Sdh 		ipst->ips_mld_time_to_next = next;
247f5db8fb0SRobert Mustacchi 		if (ipst->ips_mld_timer_quiesce != B_TRUE) {
248f5db8fb0SRobert Mustacchi 			ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
249f5db8fb0SRobert Mustacchi 			    (void *)ipst,
250f5db8fb0SRobert Mustacchi 			    MSEC_TO_TICK(ipst->ips_mld_time_to_next));
251f5db8fb0SRobert Mustacchi 			ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
252f5db8fb0SRobert Mustacchi 		}
253f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_FALSE;
254f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2557c478bd9Sstevel@tonic-gate 		return;
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/*
2597c478bd9Sstevel@tonic-gate 	 * The timer was scheduled sometime back for firing in
2607c478bd9Sstevel@tonic-gate 	 * 'igmp_time_to_next' ms and is active. We need to
2617c478bd9Sstevel@tonic-gate 	 * reschedule the timeout if the new 'next' will happen
2627c478bd9Sstevel@tonic-gate 	 * earlier than the currently scheduled timeout
2637c478bd9Sstevel@tonic-gate 	 */
2648dc47d9fSudpa 	time_left = ipst->ips_mld_timer_scheduled_last +
265f4b3ec61Sdh 	    MSEC_TO_TICK(ipst->ips_mld_time_to_next) - ddi_get_lbolt();
2667c478bd9Sstevel@tonic-gate 	if (time_left < MSEC_TO_TICK(next)) {
267f4b3ec61Sdh 		ipst->ips_mld_timer_setter_active = B_FALSE;
268f4b3ec61Sdh 		mutex_exit(&ipst->ips_mld_timer_lock);
2697c478bd9Sstevel@tonic-gate 		return;
2707c478bd9Sstevel@tonic-gate 	}
271bd670b35SErik Nordmark 	id = ipst->ips_mld_timeout_id;
2727c478bd9Sstevel@tonic-gate 
273f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
274bd670b35SErik Nordmark 	ret = untimeout(id);
275f4b3ec61Sdh 	mutex_enter(&ipst->ips_mld_timer_lock);
2767c478bd9Sstevel@tonic-gate 	/*
2777c478bd9Sstevel@tonic-gate 	 * The timeout was cancelled, or the timeout handler
2787c478bd9Sstevel@tonic-gate 	 * completed, while we were blocked in the untimeout.
2797c478bd9Sstevel@tonic-gate 	 * No other thread could have set the timer meanwhile
2807c478bd9Sstevel@tonic-gate 	 * since we serialized all the timer setters. Thus
2817c478bd9Sstevel@tonic-gate 	 * no timer is currently active nor executing nor will
2827c478bd9Sstevel@tonic-gate 	 * any timer fire in the future. We start the timer now
2837c478bd9Sstevel@tonic-gate 	 * if needed.
2847c478bd9Sstevel@tonic-gate 	 */
2857c478bd9Sstevel@tonic-gate 	if (ret == -1) {
286f4b3ec61Sdh 		ASSERT(ipst->ips_mld_timeout_id == 0);
2877c478bd9Sstevel@tonic-gate 	} else {
288f4b3ec61Sdh 		ASSERT(ipst->ips_mld_timeout_id != 0);
289f4b3ec61Sdh 		ipst->ips_mld_timeout_id = 0;
2907c478bd9Sstevel@tonic-gate 	}
291f5db8fb0SRobert Mustacchi 	if (ipst->ips_mld_time_to_next != 0 &&
292f5db8fb0SRobert Mustacchi 	    ipst->ips_mld_timer_quiesce == B_FALSE) {
293f4b3ec61Sdh 		ipst->ips_mld_time_to_next =
294f4b3ec61Sdh 		    MIN(ipst->ips_mld_time_to_next, next);
295f4b3ec61Sdh 		ipst->ips_mld_timeout_id = timeout(mld_timeout_handler,
296f4b3ec61Sdh 		    (void *)ipst, MSEC_TO_TICK(ipst->ips_mld_time_to_next));
2978dc47d9fSudpa 		ipst->ips_mld_timer_scheduled_last = ddi_get_lbolt();
2987c478bd9Sstevel@tonic-gate 	}
299f4b3ec61Sdh 	ipst->ips_mld_timer_setter_active = B_FALSE;
300f4b3ec61Sdh 	mutex_exit(&ipst->ips_mld_timer_lock);
3017c478bd9Sstevel@tonic-gate }
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate /*
3047c478bd9Sstevel@tonic-gate  * igmp_input:
3056226e9d9Sethindra  * Return NULL for a bad packet that is discarded here.
3066226e9d9Sethindra  * Return mp if the message is OK and should be handed to "raw" receivers.
3077c478bd9Sstevel@tonic-gate  * Callers of igmp_input() may need to reinitialize variables that were copied
3087c478bd9Sstevel@tonic-gate  * from the mblk as this calls pullupmsg().
3097c478bd9Sstevel@tonic-gate  */
3106226e9d9Sethindra mblk_t *
igmp_input(mblk_t * mp,ip_recv_attr_t * ira)311bd670b35SErik Nordmark igmp_input(mblk_t *mp, ip_recv_attr_t *ira)
3127c478bd9Sstevel@tonic-gate {
313*ab82c29bSToomas Soome 	igmpa_t		*igmpa;
3147c478bd9Sstevel@tonic-gate 	ipha_t		*ipha = (ipha_t *)(mp->b_rptr);
3157c478bd9Sstevel@tonic-gate 	int		iphlen, igmplen, mblklen;
316*ab82c29bSToomas Soome 	ilm_t		*ilm;
3177c478bd9Sstevel@tonic-gate 	uint32_t	src, dst;
318*ab82c29bSToomas Soome 	uint32_t	group;
319bd670b35SErik Nordmark 	in6_addr_t	v6group;
3207c478bd9Sstevel@tonic-gate 	uint_t		next;
321*ab82c29bSToomas Soome 	ipif_t		*ipif;
322bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
323bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	ASSERT(!ill->ill_isv6);
326f4b3ec61Sdh 	++ipst->ips_igmpstat.igps_rcv_total;
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	mblklen = MBLKL(mp);
329bd670b35SErik Nordmark 	iphlen = ira->ira_ip_hdr_length;
330bd670b35SErik Nordmark 	if (mblklen < 1 || mblklen < iphlen) {
331f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
3326226e9d9Sethindra 		goto bad_pkt;
3337c478bd9Sstevel@tonic-gate 	}
334bd670b35SErik Nordmark 	igmplen = ira->ira_pktlen - iphlen;
3357c478bd9Sstevel@tonic-gate 	/*
3367c478bd9Sstevel@tonic-gate 	 * Since msg sizes are more variable with v3, just pullup the
3377c478bd9Sstevel@tonic-gate 	 * whole thing now.
3387c478bd9Sstevel@tonic-gate 	 */
3397c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < (igmplen + iphlen)) {
3407c478bd9Sstevel@tonic-gate 		mblk_t *mp1;
3417c478bd9Sstevel@tonic-gate 		if ((mp1 = msgpullup(mp, -1)) == NULL) {
342f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_tooshort;
3436226e9d9Sethindra 			goto bad_pkt;
3447c478bd9Sstevel@tonic-gate 		}
3457c478bd9Sstevel@tonic-gate 		freemsg(mp);
3467c478bd9Sstevel@tonic-gate 		mp = mp1;
3477c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)(mp->b_rptr);
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	/*
3517c478bd9Sstevel@tonic-gate 	 * Validate lengths
3527c478bd9Sstevel@tonic-gate 	 */
3537c478bd9Sstevel@tonic-gate 	if (igmplen < IGMP_MINLEN) {
354f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_tooshort;
3556226e9d9Sethindra 		goto bad_pkt;
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	igmpa = (igmpa_t *)(&mp->b_rptr[iphlen]);
3597c478bd9Sstevel@tonic-gate 	src = ipha->ipha_src;
3607c478bd9Sstevel@tonic-gate 	dst = ipha->ipha_dst;
3617c478bd9Sstevel@tonic-gate 	if (ip_debug > 1)
3627c478bd9Sstevel@tonic-gate 		(void) mi_strlog(ill->ill_rq, 1, SL_TRACE,
3637c478bd9Sstevel@tonic-gate 		    "igmp_input: src 0x%x, dst 0x%x on %s\n",
3647c478bd9Sstevel@tonic-gate 		    (int)ntohl(src), (int)ntohl(dst),
3657c478bd9Sstevel@tonic-gate 		    ill->ill_name);
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	switch (igmpa->igmpa_type) {
3687c478bd9Sstevel@tonic-gate 	case IGMP_MEMBERSHIP_QUERY:
3697c478bd9Sstevel@tonic-gate 		/*
3707c478bd9Sstevel@tonic-gate 		 * packet length differentiates between v1/v2 and v3
3717c478bd9Sstevel@tonic-gate 		 * v1/v2 should be exactly 8 octets long; v3 is >= 12
3727c478bd9Sstevel@tonic-gate 		 */
3738dc47d9fSudpa 		if ((igmplen == IGMP_MINLEN) ||
3748dc47d9fSudpa 		    (ipst->ips_igmp_max_version <= IGMP_V2_ROUTER)) {
3757c478bd9Sstevel@tonic-gate 			next = igmp_query_in(ipha, igmpa, ill);
3767c478bd9Sstevel@tonic-gate 		} else if (igmplen >= IGMP_V3_QUERY_MINLEN) {
3777c478bd9Sstevel@tonic-gate 			next = igmpv3_query_in((igmp3qa_t *)igmpa, ill,
3787c478bd9Sstevel@tonic-gate 			    igmplen);
3797c478bd9Sstevel@tonic-gate 		} else {
380f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_tooshort;
3816226e9d9Sethindra 			goto bad_pkt;
3827c478bd9Sstevel@tonic-gate 		}
3836226e9d9Sethindra 		if (next == 0)
3846226e9d9Sethindra 			goto bad_pkt;
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		if (next != INFINITY)
387f4b3ec61Sdh 			igmp_start_timers(next, ipst);
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 		break;
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	case IGMP_V1_MEMBERSHIP_REPORT:
3927c478bd9Sstevel@tonic-gate 	case IGMP_V2_MEMBERSHIP_REPORT:
3937c478bd9Sstevel@tonic-gate 		/*
3947c478bd9Sstevel@tonic-gate 		 * For fast leave to work, we have to know that we are the
3957c478bd9Sstevel@tonic-gate 		 * last person to send a report for this group. Reports
3967c478bd9Sstevel@tonic-gate 		 * generated by us are looped back since we could potentially
3977c478bd9Sstevel@tonic-gate 		 * be a multicast router, so discard reports sourced by me.
3987c478bd9Sstevel@tonic-gate 		 */
3997c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
4007c478bd9Sstevel@tonic-gate 		for (ipif = ill->ill_ipif; ipif != NULL;
4017c478bd9Sstevel@tonic-gate 		    ipif = ipif->ipif_next) {
4027c478bd9Sstevel@tonic-gate 			if (ipif->ipif_lcl_addr == src) {
4037c478bd9Sstevel@tonic-gate 				if (ip_debug > 1) {
4047c478bd9Sstevel@tonic-gate 					(void) mi_strlog(ill->ill_rq,
4057c478bd9Sstevel@tonic-gate 					    1,
4067c478bd9Sstevel@tonic-gate 					    SL_TRACE,
4077c478bd9Sstevel@tonic-gate 					    "igmp_input: we are only "
408bd670b35SErik Nordmark 					    "member src 0x%x\n",
409bd670b35SErik Nordmark 					    (int)ntohl(src));
4107c478bd9Sstevel@tonic-gate 				}
4117c478bd9Sstevel@tonic-gate 				mutex_exit(&ill->ill_lock);
4126226e9d9Sethindra 				return (mp);
4137c478bd9Sstevel@tonic-gate 			}
4147c478bd9Sstevel@tonic-gate 		}
4157c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
4167c478bd9Sstevel@tonic-gate 
417f4b3ec61Sdh 		++ipst->ips_igmpstat.igps_rcv_reports;
4187c478bd9Sstevel@tonic-gate 		group = igmpa->igmpa_group;
4197c478bd9Sstevel@tonic-gate 		if (!CLASSD(group)) {
420f4b3ec61Sdh 			++ipst->ips_igmpstat.igps_rcv_badreports;
4216226e9d9Sethindra 			goto bad_pkt;
4227c478bd9Sstevel@tonic-gate 		}
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 		/*
4257c478bd9Sstevel@tonic-gate 		 * KLUDGE: if the IP source address of the report has an
4267c478bd9Sstevel@tonic-gate 		 * unspecified (i.e., zero) subnet number, as is allowed for
4277c478bd9Sstevel@tonic-gate 		 * a booting host, replace it with the correct subnet number
4287c478bd9Sstevel@tonic-gate 		 * so that a process-level multicast routing demon can
4297c478bd9Sstevel@tonic-gate 		 * determine which subnet it arrived from.  This is necessary
4307c478bd9Sstevel@tonic-gate 		 * to compensate for the lack of any way for a process to
4317c478bd9Sstevel@tonic-gate 		 * determine the arrival interface of an incoming packet.
4327c478bd9Sstevel@tonic-gate 		 *
4337c478bd9Sstevel@tonic-gate 		 * Requires that a copy of *this* message it passed up
4347c478bd9Sstevel@tonic-gate 		 * to the raw interface which is done by our caller.
4357c478bd9Sstevel@tonic-gate 		 */
4367c478bd9Sstevel@tonic-gate 		if ((src & htonl(0xFF000000U)) == 0) {	/* Minimum net mask */
4377c478bd9Sstevel@tonic-gate 			/* Pick the first ipif on this ill */
4387c478bd9Sstevel@tonic-gate 			mutex_enter(&ill->ill_lock);
4397c478bd9Sstevel@tonic-gate 			src = ill->ill_ipif->ipif_subnet;
4407c478bd9Sstevel@tonic-gate 			mutex_exit(&ill->ill_lock);
4417c478bd9Sstevel@tonic-gate 			ip1dbg(("igmp_input: changed src to 0x%x\n",
4427c478bd9Sstevel@tonic-gate 			    (int)ntohl(src)));
4437c478bd9Sstevel@tonic-gate 			ipha->ipha_src = src;
4447c478bd9Sstevel@tonic-gate 		}
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 		/*
447e11c3f44Smeem 		 * If our ill has ILMs that belong to the group being
448e11c3f44Smeem 		 * reported, and we are a 'Delaying Member' in the RFC
449e11c3f44Smeem 		 * terminology, stop our timer for that group and 'clear
450e11c3f44Smeem 		 * flag' i.e. mark as IGMP_OTHERMEMBER.
4517c478bd9Sstevel@tonic-gate 		 */
452bd670b35SErik Nordmark 		rw_enter(&ill->ill_mcast_lock, RW_WRITER);
453bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
454