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