igmp.c revision 189931
1139823Simp/*- 2189592Sbms * Copyright (c) 2007-2009 Bruce Simpson. 31541Srgrimes * Copyright (c) 1988 Stephen Deering. 41541Srgrimes * Copyright (c) 1992, 1993 51541Srgrimes * The Regents of the University of California. All rights reserved. 61541Srgrimes * 71541Srgrimes * This code is derived from software contributed to Berkeley by 81541Srgrimes * Stephen Deering of Stanford University. 91541Srgrimes * 101541Srgrimes * Redistribution and use in source and binary forms, with or without 111541Srgrimes * modification, are permitted provided that the following conditions 121541Srgrimes * are met: 131541Srgrimes * 1. Redistributions of source code must retain the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer. 151541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161541Srgrimes * notice, this list of conditions and the following disclaimer in the 171541Srgrimes * documentation and/or other materials provided with the distribution. 181541Srgrimes * 4. Neither the name of the University nor the names of its contributors 191541Srgrimes * may be used to endorse or promote products derived from this software 201541Srgrimes * without specific prior written permission. 211541Srgrimes * 221541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321541Srgrimes * SUCH DAMAGE. 331541Srgrimes * 341541Srgrimes * @(#)igmp.c 8.1 (Berkeley) 7/19/93 351541Srgrimes */ 361541Srgrimes 372531Swollman/* 382531Swollman * Internet Group Management Protocol (IGMP) routines. 39189592Sbms * [RFC1112, RFC2236, RFC3376] 402531Swollman * 412531Swollman * Written by Steve Deering, Stanford, May 1988. 422531Swollman * Modified by Rosen Sharma, Stanford, Aug 1994. 439209Swollman * Modified by Bill Fenner, Xerox PARC, Feb 1995. 4414622Sfenner * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995. 45189592Sbms * Significantly rewritten for IGMPv3, VIMAGE, and SMP by Bruce Simpson. 462531Swollman * 4714622Sfenner * MULTICAST Revision: 3.5.1.4 482531Swollman */ 491541Srgrimes 50172467Ssilby#include <sys/cdefs.h> 51172467Ssilby__FBSDID("$FreeBSD: head/sys/netinet/igmp.c 189931 2009-03-17 14:41:54Z bms $"); 52172467Ssilby 53101091Srwatson#include "opt_mac.h" 54189106Sbz#include "opt_route.h" 55101091Srwatson 561541Srgrimes#include <sys/param.h> 571549Srgrimes#include <sys/systm.h> 58189592Sbms#include <sys/module.h> 5929024Sbde#include <sys/malloc.h> 601541Srgrimes#include <sys/mbuf.h> 611541Srgrimes#include <sys/socket.h> 621541Srgrimes#include <sys/protosw.h> 6312296Sphk#include <sys/kernel.h> 646472Swollman#include <sys/sysctl.h> 65181803Sbz#include <sys/vimage.h> 66189592Sbms#include <sys/ktr.h> 67189592Sbms#include <sys/condvar.h> 681541Srgrimes 691541Srgrimes#include <net/if.h> 70189592Sbms#include <net/netisr.h> 711541Srgrimes#include <net/route.h> 72185571Sbz#include <net/vnet.h> 731541Srgrimes 741541Srgrimes#include <netinet/in.h> 751541Srgrimes#include <netinet/in_var.h> 761541Srgrimes#include <netinet/in_systm.h> 771541Srgrimes#include <netinet/ip.h> 781541Srgrimes#include <netinet/ip_var.h> 79152592Sandre#include <netinet/ip_options.h> 801541Srgrimes#include <netinet/igmp.h> 811541Srgrimes#include <netinet/igmp_var.h> 82185571Sbz#include <netinet/vinet.h> 831541Srgrimes 8460105Sjlemon#include <machine/in_cksum.h> 8560105Sjlemon 86163606Srwatson#include <security/mac/mac_framework.h> 87163606Srwatson 88189592Sbms#ifndef KTR_IGMPV3 89189592Sbms#define KTR_IGMPV3 KTR_SUBSYS 90189592Sbms#endif 9130309Sphk 92189592Sbmsstatic struct igmp_ifinfo * 93189592Sbms igi_alloc_locked(struct ifnet *); 94189592Sbmsstatic void igi_delete_locked(const struct ifnet *); 95189592Sbmsstatic void igmp_dispatch_queue(struct ifqueue *, int, const int); 96189592Sbmsstatic void igmp_fasttimo_vnet(void); 97189592Sbmsstatic void igmp_final_leave(struct in_multi *, struct igmp_ifinfo *); 98189592Sbmsstatic int igmp_handle_state_change(struct in_multi *, 99189592Sbms struct igmp_ifinfo *); 100189592Sbmsstatic int igmp_initial_join(struct in_multi *, struct igmp_ifinfo *); 101189592Sbmsstatic int igmp_input_v1_query(struct ifnet *, const struct ip *); 102189592Sbmsstatic int igmp_input_v2_query(struct ifnet *, const struct ip *, 103189592Sbms const struct igmp *); 104189592Sbmsstatic int igmp_input_v3_query(struct ifnet *, const struct ip *, 105189592Sbms /*const*/ struct igmpv3 *); 106189592Sbmsstatic int igmp_input_v3_group_query(struct in_multi *, 107189592Sbms struct igmp_ifinfo *, int, /*const*/ struct igmpv3 *); 108189592Sbmsstatic int igmp_input_v1_report(struct ifnet *, /*const*/ struct ip *, 109189592Sbms /*const*/ struct igmp *); 110189592Sbmsstatic int igmp_input_v2_report(struct ifnet *, /*const*/ struct ip *, 111189592Sbms /*const*/ struct igmp *); 112189592Sbmsstatic void igmp_intr(struct mbuf *); 113189592Sbmsstatic int igmp_isgroupreported(const struct in_addr); 114189592Sbmsstatic struct mbuf * 115189592Sbms igmp_ra_alloc(void); 116189592Sbms#ifdef KTR 117189592Sbmsstatic char * igmp_rec_type_to_str(const int); 118185088Szec#endif 119189592Sbmsstatic void igmp_set_version(struct igmp_ifinfo *, const int); 120189592Sbmsstatic void igmp_slowtimo_vnet(void); 121189592Sbmsstatic void igmp_sysinit(void); 122189592Sbmsstatic int igmp_v1v2_queue_report(struct in_multi *, const int); 123189592Sbmsstatic void igmp_v1v2_process_group_timer(struct in_multi *, const int); 124189592Sbmsstatic void igmp_v1v2_process_querier_timers(struct igmp_ifinfo *); 125189592Sbmsstatic void igmp_v2_update_group(struct in_multi *, const int); 126189592Sbmsstatic void igmp_v3_cancel_link_timers(struct igmp_ifinfo *); 127189592Sbmsstatic void igmp_v3_dispatch_general_query(struct igmp_ifinfo *); 128189592Sbmsstatic struct mbuf * 129189592Sbms igmp_v3_encap_report(struct ifnet *, struct mbuf *); 130189592Sbmsstatic int igmp_v3_enqueue_group_record(struct ifqueue *, 131189592Sbms struct in_multi *, const int, const int, const int); 132189592Sbmsstatic int igmp_v3_enqueue_filter_change(struct ifqueue *, 133189592Sbms struct in_multi *); 134189592Sbmsstatic void igmp_v3_process_group_timers(struct igmp_ifinfo *, 135189592Sbms struct ifqueue *, struct ifqueue *, struct in_multi *, 136189592Sbms const int); 137189592Sbmsstatic int igmp_v3_merge_state_changes(struct in_multi *, 138189592Sbms struct ifqueue *); 139189592Sbmsstatic void igmp_v3_suppress_group_record(struct in_multi *); 140189592Sbmsstatic int sysctl_igmp_default_version(SYSCTL_HANDLER_ARGS); 141189592Sbmsstatic int sysctl_igmp_gsr(SYSCTL_HANDLER_ARGS); 142189592Sbmsstatic int sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS); 1432531Swollman 144189592Sbms#ifdef VIMAGE 145189592Sbmsstatic vnet_attach_fn vnet_igmp_iattach; 146189592Sbmsstatic vnet_detach_fn vnet_igmp_idetach; 147189592Sbms#else 148189592Sbmsstatic int vnet_igmp_iattach(const void *); 149189592Sbmsstatic int vnet_igmp_idetach(const void *); 150189592Sbms#endif /* VIMAGE */ 15112296Sphk 152130333Srwatson/* 153189592Sbms * System-wide globals. 154189592Sbms * 155189592Sbms * Unlocked access to these is OK, except for the global IGMP output 156189592Sbms * queue. The IGMP subsystem lock ends up being system-wide for the moment, 157189592Sbms * because all VIMAGEs have to share a global output queue, as netisrs 158189592Sbms * themselves are not virtualized. 159189592Sbms * 160189592Sbms * Locking: 161189592Sbms * * The permitted lock order is: IN_MULTI_LOCK, IGMP_LOCK, IF_ADDR_LOCK. 162189592Sbms * Any may be taken independently; if any are held at the same 163189592Sbms * time, the above lock order must be followed. 164189592Sbms * * All output is delegated to the netisr to handle IFF_NEEDSGIANT. 165189592Sbms * Most of the time, direct dispatch will be fine. 166189592Sbms * * IN_MULTI_LOCK covers in_multi. 167189592Sbms * * IGMP_LOCK covers igmp_ifinfo and any global variables in this file, 168189592Sbms * including the output queue. 169189592Sbms * * IF_ADDR_LOCK covers if_multiaddrs, which is used for a variety of 170189592Sbms * per-link state iterators. 171189592Sbms * * igmp_ifinfo is valid as long as PF_INET is attached to the interface, 172189592Sbms * therefore it is not refcounted. 173189592Sbms * We allow unlocked reads of igmp_ifinfo when accessed via in_multi. 174189592Sbms * 175189592Sbms * Reference counting 176189592Sbms * * IGMP acquires its own reference every time an in_multi is passed to 177189592Sbms * it and the group is being joined for the first time. 178189592Sbms * * IGMP releases its reference(s) on in_multi in a deferred way, 179189592Sbms * because the operations which process the release run as part of 180189592Sbms * a loop whose control variables are directly affected by the release 181189592Sbms * (that, and not recursing on the IF_ADDR_LOCK). 182189592Sbms * 183189592Sbms * VIMAGE: Each in_multi corresponds to an ifp, and each ifp corresponds 184189592Sbms * to a vnet in ifp->if_vnet. 185189592Sbms * 186189931Sbms * SMPng: XXX We may potentially race operations on ifma_protospec. 187189931Sbms * The problem is that we currently lack a clean way of taking the 188189931Sbms * IF_ADDR_LOCK() between the ifnet and in layers w/o recursing, 189189931Sbms * as anything which modifies ifma needs to be covered by that lock. 190189931Sbms * So check for ifma_protospec being NULL before proceeding. 191130333Srwatson */ 192189592Sbmsstruct mtx igmp_mtx; 193189592Sbmsint mpsafe_igmp = 0; 194189592SbmsSYSCTL_INT(_debug, OID_AUTO, mpsafe_igmp, CTLFLAG_RDTUN, &mpsafe_igmp, 0, 195189592Sbms "Enable SMP-safe IGMPv3"); 196189592Sbms 197189592Sbmsstruct mbuf *m_raopt; /* Router Alert option */ 198189592SbmsMALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); 199189592Sbms 200189592Sbms/* 201189592Sbms * Global netisr output queue. 202189592Sbms * This is only used as a last resort if we cannot directly dispatch. 203189592Sbms * As IN_MULTI_LOCK is no longer in the bottom half of IP, we can do 204189592Sbms * this, providing mpsafe_igmp is set. If it is not, we take Giant, 205189592Sbms * and queueing is forced. 206189592Sbms */ 207189592Sbmsstruct ifqueue igmpoq; 208189592Sbms 209189592Sbms/* 210189592Sbms * VIMAGE-wide globals. 211189592Sbms * 212189592Sbms * The IGMPv3 timers themselves need to run per-image, however, 213189592Sbms * protosw timers run globally (see tcp). 214189592Sbms * An ifnet can only be in one vimage at a time, and the loopback 215189592Sbms * ifnet, loif, is itself virtualized. 216189592Sbms * It would otherwise be possible to seriously hose IGMP state, 217189592Sbms * and create inconsistencies in upstream multicast routing, if you have 218189592Sbms * multiple VIMAGEs running on the same link joining different multicast 219189592Sbms * groups, UNLESS the "primary IP address" is different. This is because 220189592Sbms * IGMP for IPv4 does not force link-local addresses to be used for each 221189592Sbms * node, unlike MLD for IPv6. 222189592Sbms * Obviously the IGMPv3 per-interface state has per-vimage granularity 223189592Sbms * also as a result. 224189592Sbms * 225189592Sbms * FUTURE: Stop using IFP_TO_IA/INADDR_ANY, and use source address selection 226189592Sbms * policy to control the address used by IGMP on the link. 227189592Sbms */ 228185088Szec#ifdef VIMAGE_GLOBALS 229189592Sbmsint interface_timers_running; /* IGMPv3 general query response */ 230189592Sbmsint state_change_timers_running; /* IGMPv3 state-change retransmit */ 231189592Sbmsint current_state_timers_running; /* IGMPv1/v2 host report; 232189592Sbms * IGMPv3 g/sg query response */ 233130333Srwatson 234189592SbmsLIST_HEAD(, igmp_ifinfo) igi_head; 235189592Sbmsstruct igmpstat igmpstat; 236189592Sbmsstruct timeval igmp_gsrdelay; 237189592Sbms 238189592Sbmsint igmp_recvifkludge; 239189592Sbmsint igmp_sendra; 240189592Sbmsint igmp_sendlocal; 241189592Sbmsint igmp_v1enable; 242189592Sbmsint igmp_v2enable; 243189592Sbmsint igmp_legacysupp; 244189592Sbmsint igmp_default_version; 245189592Sbms#endif /* VIMAGE_GLOBALS */ 246189592Sbms 247130333Srwatson/* 248189592Sbms * Virtualized sysctls. 249130333Srwatson */ 250189592SbmsSYSCTL_V_STRUCT(V_NET, vnet_inet, _net_inet_igmp, IGMPCTL_STATS, stats, 251189592Sbms CTLFLAG_RW, igmpstat, igmpstat, ""); 252189592SbmsSYSCTL_V_INT(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, recvifkludge, 253189592Sbms CTLFLAG_RW, igmp_recvifkludge, 0, 254189592Sbms "Rewrite IGMPv1/v2 reports from 0.0.0.0 to contain subnet address"); 255189592SbmsSYSCTL_V_INT(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, sendra, 256189592Sbms CTLFLAG_RW, igmp_sendra, 0, 257189592Sbms "Send IP Router Alert option in IGMPv2/v3 messages"); 258189592SbmsSYSCTL_V_INT(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, sendlocal, 259189592Sbms CTLFLAG_RW, igmp_sendlocal, 0, 260189592Sbms "Send IGMP membership reports for 224.0.0.0/24 groups"); 261189592SbmsSYSCTL_V_INT(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, v1enable, 262189592Sbms CTLFLAG_RW, igmp_v1enable, 0, 263189592Sbms "Enable backwards compatibility with IGMPv1"); 264189592SbmsSYSCTL_V_INT(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, v2enable, 265189592Sbms CTLFLAG_RW, igmp_v2enable, 0, 266189592Sbms "Enable backwards compatibility with IGMPv2"); 267189592SbmsSYSCTL_V_INT(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, legacysupp, 268189592Sbms CTLFLAG_RW, igmp_legacysupp, 0, 269189592Sbms "Allow v1/v2 reports to suppress v3 group responses"); 270189592SbmsSYSCTL_V_PROC(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, default_version, 271189592Sbms CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, igmp_default_version, 0, 272189592Sbms sysctl_igmp_default_version, "I", 273189592Sbms "Default version of IGMP to run on each interface"); 274189592SbmsSYSCTL_V_PROC(V_NET, vnet_inet, _net_inet_igmp, OID_AUTO, gsrdelay, 275189592Sbms CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, igmp_gsrdelay.tv_sec, 0, 276189592Sbms sysctl_igmp_gsr, "I", 277189592Sbms "Rate limit for IGMPv3 Group-and-Source queries in seconds"); 278130333Srwatson 279189592Sbms/* 280189592Sbms * Non-virtualized sysctls. 281189592Sbms */ 282189592SbmsSYSCTL_NODE(_net_inet_igmp, OID_AUTO, ifinfo, CTLFLAG_RD | CTLFLAG_MPSAFE, 283189592Sbms sysctl_igmp_ifinfo, "Per-interface IGMPv3 state"); 2841541Srgrimes 285189592Sbmsstatic __inline void 286189592Sbmsigmp_save_context(struct mbuf *m, struct ifnet *ifp) 287189592Sbms{ 288189592Sbms 289189592Sbms#ifdef VIMAGE 290189592Sbms m->m_pkthdr.header = ifp->if_vnet; 291189592Sbms#endif /* VIMAGE */ 292189592Sbms m->m_pkthdr.flowid = ifp->if_index; 293189592Sbms} 294189592Sbms 295189592Sbmsstatic __inline void 296189592Sbmsigmp_scrub_context(struct mbuf *m) 297189592Sbms{ 298189592Sbms 299189592Sbms m->m_pkthdr.header = NULL; 300189592Sbms m->m_pkthdr.flowid = 0; 301189592Sbms} 302189592Sbms 303189592Sbms#ifdef KTR 304189592Sbmsstatic __inline char * 305189592Sbmsinet_ntoa_haddr(in_addr_t haddr) 306189592Sbms{ 307189592Sbms struct in_addr ia; 308189592Sbms 309189592Sbms ia.s_addr = htonl(haddr); 310189592Sbms return (inet_ntoa(ia)); 311189592Sbms} 312119180Srwatson#endif 313119180Srwatson 314189592Sbms/* 315189592Sbms * Restore context from a queued IGMP output chain. 316189592Sbms * Return saved ifindex. 317189592Sbms * 318189592Sbms * VIMAGE: The assertion is there to make sure that we 319189592Sbms * actually called CURVNET_SET() with what's in the mbuf chain. 320189592Sbms */ 321189592Sbmsstatic __inline uint32_t 322189592Sbmsigmp_restore_context(struct mbuf *m) 3231541Srgrimes{ 32414622Sfenner 325189592Sbms#ifdef notyet 326189592Sbms#if defined(VIMAGE) && defined(INVARIANTS) 327189592Sbms KASSERT(curvnet == (m->m_pkthdr.header), 328189592Sbms ("%s: called when curvnet was not restored", __func__)); 329189592Sbms#endif 330189592Sbms#endif 331189592Sbms return (m->m_pkthdr.flowid); 332189592Sbms} 333189592Sbms 334189592Sbms/* 335189592Sbms * Retrieve or set default IGMP version. 336189592Sbms * 337189592Sbms * VIMAGE: Assume curvnet set by caller. 338189592Sbms * SMPng: NOTE: Serialized by IGMP lock. 339189592Sbms */ 340189592Sbmsstatic int 341189592Sbmssysctl_igmp_default_version(SYSCTL_HANDLER_ARGS) 342189592Sbms{ 343189592Sbms int error; 344189592Sbms int new; 345189592Sbms 346189592Sbms error = sysctl_wire_old_buffer(req, sizeof(int)); 347189592Sbms if (error) 348189592Sbms return (error); 349189592Sbms 350189592Sbms IGMP_LOCK(); 351189592Sbms 352189592Sbms new = V_igmp_default_version; 353189592Sbms 354189592Sbms error = sysctl_handle_int(oidp, &new, 0, req); 355189592Sbms if (error || !req->newptr) 356189592Sbms goto out_locked; 357189592Sbms 358189592Sbms if (new < IGMP_VERSION_1 || new > IGMP_VERSION_3) { 359189592Sbms error = EINVAL; 360189592Sbms goto out_locked; 361189592Sbms } 362189592Sbms 363189592Sbms CTR2(KTR_IGMPV3, "change igmp_default_version from %d to %d", 364189592Sbms V_igmp_default_version, new); 365189592Sbms 366189592Sbms V_igmp_default_version = new; 367189592Sbms 368189592Sbmsout_locked: 369189592Sbms IGMP_UNLOCK(); 370189592Sbms return (error); 371189592Sbms} 372189592Sbms 373189592Sbms/* 374189592Sbms * Retrieve or set threshold between group-source queries in seconds. 375189592Sbms * 376189592Sbms * VIMAGE: Assume curvnet set by caller. 377189592Sbms * SMPng: NOTE: Serialized by IGMP lock. 378189592Sbms */ 379189592Sbmsstatic int 380189592Sbmssysctl_igmp_gsr(SYSCTL_HANDLER_ARGS) 381189592Sbms{ 382189592Sbms int error; 383189592Sbms int i; 384189592Sbms 385189592Sbms error = sysctl_wire_old_buffer(req, sizeof(int)); 386189592Sbms if (error) 387189592Sbms return (error); 388189592Sbms 389189592Sbms IGMP_LOCK(); 390189592Sbms 391189592Sbms i = V_igmp_gsrdelay.tv_sec; 392189592Sbms 393189592Sbms error = sysctl_handle_int(oidp, &i, 0, req); 394189592Sbms if (error || !req->newptr) 395189592Sbms goto out_locked; 396189592Sbms 397189592Sbms if (i < -1 || i >= 60) { 398189592Sbms error = EINVAL; 399189592Sbms goto out_locked; 400189592Sbms } 401189592Sbms 402189592Sbms CTR2(KTR_IGMPV3, "change igmp_gsrdelay from %d to %d", 403189592Sbms V_igmp_gsrdelay.tv_sec, i); 404189592Sbms V_igmp_gsrdelay.tv_sec = i; 405189592Sbms 406189592Sbmsout_locked: 407189592Sbms IGMP_UNLOCK(); 408189592Sbms return (error); 409189592Sbms} 410189592Sbms 411189592Sbms/* 412189592Sbms * Expose struct igmp_ifinfo to userland, keyed by ifindex. 413189592Sbms * For use by ifmcstat(8). 414189592Sbms * 415189592Sbms * SMPng: NOTE: Does an unlocked ifindex space read. 416189592Sbms * VIMAGE: Assume curvnet set by caller. The node handler itself 417189592Sbms * is not directly virtualized. 418189592Sbms */ 419189592Sbmsstatic int 420189592Sbmssysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS) 421189592Sbms{ 422189592Sbms INIT_VNET_NET(curvnet); 423189592Sbms int *name; 424189592Sbms int error; 425189592Sbms u_int namelen; 426189592Sbms struct ifnet *ifp; 427189592Sbms struct igmp_ifinfo *igi; 428189592Sbms 429189592Sbms name = (int *)arg1; 430189592Sbms namelen = arg2; 431189592Sbms 432189592Sbms if (req->newptr != NULL) 433189592Sbms return (EPERM); 434189592Sbms 435189592Sbms if (namelen != 1) 436189592Sbms return (EINVAL); 437189592Sbms 438189592Sbms error = sysctl_wire_old_buffer(req, sizeof(struct igmp_ifinfo)); 439189592Sbms if (error) 440189592Sbms return (error); 441189592Sbms 442189592Sbms IN_MULTI_LOCK(); 443189592Sbms IGMP_LOCK(); 444189592Sbms 445189592Sbms if (name[0] <= 0 || name[0] > V_if_index) { 446189592Sbms error = ENOENT; 447189592Sbms goto out_locked; 448189592Sbms } 449189592Sbms 450189592Sbms error = ENOENT; 451189592Sbms 452189592Sbms ifp = ifnet_byindex(name[0]); 453189592Sbms if (ifp == NULL) 454189592Sbms goto out_locked; 455189592Sbms 456189592Sbms LIST_FOREACH(igi, &V_igi_head, igi_link) { 457189592Sbms if (ifp == igi->igi_ifp) { 458189592Sbms error = SYSCTL_OUT(req, igi, 459189592Sbms sizeof(struct igmp_ifinfo)); 460189592Sbms break; 461189592Sbms } 462189592Sbms } 463189592Sbms 464189592Sbmsout_locked: 465189592Sbms IGMP_UNLOCK(); 466189592Sbms IN_MULTI_UNLOCK(); 467189592Sbms return (error); 468189592Sbms} 469189592Sbms 470189592Sbms/* 471189592Sbms * Dispatch an entire queue of pending packet chains 472189592Sbms * using the netisr. 473189592Sbms * VIMAGE: Assumes the vnet pointer has been set. 474189592Sbms */ 475189592Sbmsstatic void 476189592Sbmsigmp_dispatch_queue(struct ifqueue *ifq, int limit, const int loop) 477189592Sbms{ 478189592Sbms struct mbuf *m; 479189592Sbms 480189592Sbms for (;;) { 481189592Sbms _IF_DEQUEUE(ifq, m); 482189592Sbms if (m == NULL) 483189592Sbms break; 484189592Sbms CTR3(KTR_IGMPV3, "%s: dispatch %p from %p", __func__, ifq, m); 485189592Sbms if (loop) 486189592Sbms m->m_flags |= M_IGMP_LOOP; 487189592Sbms netisr_dispatch(NETISR_IGMP, m); 488189592Sbms if (--limit == 0) 489189592Sbms break; 490189592Sbms } 491189592Sbms} 492189592Sbms 493189592Sbms/* 494189592Sbms * Filter outgoing IGMP report state by group. 495189592Sbms * 496189592Sbms * Reports are ALWAYS suppressed for ALL-HOSTS (224.0.0.1). 497189592Sbms * If the net.inet.igmp.sendlocal sysctl is 0, then IGMP reports are 498189592Sbms * disabled for all groups in the 224.0.0.0/24 link-local scope. However, 499189592Sbms * this may break certain IGMP snooping switches which rely on the old 500189592Sbms * report behaviour. 501189592Sbms * 502189592Sbms * Return zero if the given group is one for which IGMP reports 503189592Sbms * should be suppressed, or non-zero if reports should be issued. 504189592Sbms */ 505189592Sbmsstatic __inline int 506189592Sbmsigmp_isgroupreported(const struct in_addr addr) 507189592Sbms{ 508189592Sbms 509189592Sbms if (in_allhosts(addr) || 510189592Sbms ((!V_igmp_sendlocal && IN_LOCAL_GROUP(ntohl(addr.s_addr))))) 511189592Sbms return (0); 512189592Sbms 513189592Sbms return (1); 514189592Sbms} 515189592Sbms 516189592Sbms/* 517189592Sbms * Construct a Router Alert option to use in outgoing packets. 518189592Sbms */ 519189592Sbmsstatic struct mbuf * 520189592Sbmsigmp_ra_alloc(void) 521189592Sbms{ 522189592Sbms struct mbuf *m; 523189592Sbms struct ipoption *p; 524189592Sbms 525189592Sbms MGET(m, M_DONTWAIT, MT_DATA); 526189592Sbms p = mtod(m, struct ipoption *); 527189592Sbms p->ipopt_dst.s_addr = INADDR_ANY; 528189592Sbms p->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */ 529189592Sbms p->ipopt_list[1] = 0x04; /* 4 bytes long */ 530189592Sbms p->ipopt_list[2] = IPOPT_EOL; /* End of IP option list */ 531189592Sbms p->ipopt_list[3] = 0x00; /* pad byte */ 532189592Sbms m->m_len = sizeof(p->ipopt_dst) + p->ipopt_list[1]; 533189592Sbms 534189592Sbms return (m); 535189592Sbms} 536189592Sbms 537189592Sbms/* 538189592Sbms * Attach IGMP when PF_INET is attached to an interface. 539189592Sbms * 540189592Sbms * VIMAGE: Currently we set the vnet pointer, although it is 541189592Sbms * likely that it was already set by our caller. 542189592Sbms */ 543189592Sbmsstruct igmp_ifinfo * 544189592Sbmsigmp_domifattach(struct ifnet *ifp) 545189592Sbms{ 546189592Sbms struct igmp_ifinfo *igi; 547189592Sbms 548189592Sbms CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", 549189592Sbms __func__, ifp, ifp->if_xname); 550189592Sbms 551189592Sbms CURVNET_SET(ifp->if_vnet); 552189592Sbms IGMP_LOCK(); 553189592Sbms 554189592Sbms igi = igi_alloc_locked(ifp); 555189592Sbms if (!(ifp->if_flags & IFF_MULTICAST)) 556189592Sbms igi->igi_flags |= IGIF_SILENT; 557189592Sbms 558189592Sbms IGMP_UNLOCK(); 559189592Sbms CURVNET_RESTORE(); 560189592Sbms 561189592Sbms return (igi); 562189592Sbms} 563189592Sbms 564189592Sbms/* 565189592Sbms * VIMAGE: assume curvnet set by caller. 566189592Sbms */ 567189592Sbmsstatic struct igmp_ifinfo * 568189592Sbmsigi_alloc_locked(/*const*/ struct ifnet *ifp) 569189592Sbms{ 570189592Sbms struct igmp_ifinfo *igi; 571189592Sbms 572189592Sbms IGMP_LOCK_ASSERT(); 573189592Sbms 574189592Sbms igi = malloc(sizeof(struct igmp_ifinfo), M_IGMP, M_NOWAIT|M_ZERO); 575189592Sbms if (igi == NULL) 576189592Sbms goto out; 577189592Sbms 578189592Sbms igi->igi_ifp = ifp; 579189592Sbms igi->igi_version = V_igmp_default_version; 580189592Sbms igi->igi_flags = 0; 581189592Sbms igi->igi_rv = IGMP_RV_INIT; 582189592Sbms igi->igi_qi = IGMP_QI_INIT; 583189592Sbms igi->igi_qri = IGMP_QRI_INIT; 584189592Sbms igi->igi_uri = IGMP_URI_INIT; 585189592Sbms 586189592Sbms SLIST_INIT(&igi->igi_relinmhead); 587189592Sbms 5881541Srgrimes /* 589189592Sbms * Responses to general queries are subject to bounds. 5901541Srgrimes */ 591189592Sbms IFQ_SET_MAXLEN(&igi->igi_gq, IGMP_MAX_RESPONSE_PACKETS); 5929209Swollman 593189592Sbms LIST_INSERT_HEAD(&V_igi_head, igi, igi_link); 5949209Swollman 595189592Sbms CTR2(KTR_IGMPV3, "allocate igmp_ifinfo for ifp %p(%s)", 596189592Sbms ifp, ifp->if_xname); 597189592Sbms 598189592Sbmsout: 599189592Sbms return (igi); 600189592Sbms} 601189592Sbms 602189592Sbms/* 603189592Sbms * Hook for ifdetach. 604189592Sbms * 605189592Sbms * NOTE: Some finalization tasks need to run before the protocol domain 606189592Sbms * is detached, but also before the link layer does its cleanup. 607189592Sbms * 608189592Sbms * SMPNG: igmp_ifdetach() needs to take IF_ADDR_LOCK(). 609189931Sbms * XXX This is also bitten by unlocked ifma_protospec access. 610189592Sbms * 611189592Sbms * VIMAGE: curvnet should have been set by caller, but let's not assume 612189592Sbms * that for now. 613189592Sbms */ 614189592Sbmsvoid 615189592Sbmsigmp_ifdetach(struct ifnet *ifp) 616189592Sbms{ 617189592Sbms struct igmp_ifinfo *igi; 618189592Sbms struct ifmultiaddr *ifma; 619189592Sbms struct in_multi *inm, *tinm; 620189592Sbms 621189592Sbms CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", __func__, ifp, 622189592Sbms ifp->if_xname); 623189592Sbms 624189592Sbms CURVNET_SET(ifp->if_vnet); 625189592Sbms 626189592Sbms IGMP_LOCK(); 627189592Sbms 628189592Sbms igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; 629189592Sbms if (igi->igi_version == IGMP_VERSION_3) { 630189592Sbms IF_ADDR_LOCK(ifp); 631189592Sbms TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 632189931Sbms if (ifma->ifma_addr->sa_family != AF_INET || 633189931Sbms ifma->ifma_protospec == NULL) 634189592Sbms continue; 635189931Sbms#if 0 636189931Sbms KASSERT(ifma->ifma_protospec != NULL, 637189931Sbms ("%s: ifma_protospec is NULL", __func__)); 638189931Sbms#endif 639189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 640189592Sbms if (inm->inm_state == IGMP_LEAVING_MEMBER) { 641189592Sbms SLIST_INSERT_HEAD(&igi->igi_relinmhead, 642189592Sbms inm, inm_nrele); 643189592Sbms } 644189592Sbms inm_clear_recorded(inm); 645189592Sbms } 646189592Sbms IF_ADDR_UNLOCK(ifp); 647189592Sbms /* 648189592Sbms * Free the in_multi reference(s) for this IGMP lifecycle. 649189592Sbms */ 650189592Sbms SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead, inm_nrele, 651189592Sbms tinm) { 652189592Sbms SLIST_REMOVE_HEAD(&igi->igi_relinmhead, inm_nrele); 653189592Sbms inm_release_locked(inm); 654189592Sbms } 655189592Sbms } 656189592Sbms 657189592Sbms IGMP_UNLOCK(); 658189592Sbms 659189592Sbms#ifdef VIMAGE 66014622Sfenner /* 661189592Sbms * Plug the potential race which may occur when a VIMAGE 662189592Sbms * is detached and we are forced to queue pending IGMP output for 663189592Sbms * output netisr processing due to !mpsafe_igmp. In this case it 664189592Sbms * is possible that igmp_intr() is about to see mbuf chains with 665189592Sbms * invalid cached curvnet pointers. 666189592Sbms * This is a rare condition, so just blow them all away. 667189592Sbms * FUTURE: This may in fact not be needed, because IFF_NEEDSGIANT 668189592Sbms * is being removed in 8.x and the netisr may then be eliminated; 669189592Sbms * it is needed only if VIMAGE and IFF_NEEDSGIANT need to co-exist 67014622Sfenner */ 671189592Sbms if (!mpsafe_igmp) { 672189592Sbms int drops; 67314622Sfenner 674189592Sbms IF_LOCK(&igmpoq); 675189592Sbms drops = igmpoq.ifq_len; 676189592Sbms _IF_DRAIN(&igmpoq); 677189592Sbms IF_UNLOCK(&igmpoq); 678189592Sbms if (bootverbose && drops) { 679189592Sbms printf("%s: dropped %d pending IGMP output packets\n", 680189592Sbms __func__, drops); 681189592Sbms } 682189592Sbms } 683189592Sbms#endif /* VIMAGE */ 684189592Sbms 685189592Sbms CURVNET_RESTORE(); 6861541Srgrimes} 6871541Srgrimes 688189592Sbms/* 689189592Sbms * Hook for domifdetach. 690189592Sbms * 691189592Sbms * VIMAGE: curvnet should have been set by caller, but let's not assume 692189592Sbms * that for now. 693189592Sbms */ 694189592Sbmsvoid 695189592Sbmsigmp_domifdetach(struct ifnet *ifp) 6962531Swollman{ 697189592Sbms struct igmp_ifinfo *igi; 698189592Sbms 699189592Sbms CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", 700189592Sbms __func__, ifp, ifp->if_xname); 701189592Sbms 702189592Sbms CURVNET_SET(ifp->if_vnet); 703189592Sbms IGMP_LOCK(); 704189592Sbms 705189592Sbms igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; 706189592Sbms igi_delete_locked(ifp); 707189592Sbms 708189592Sbms IGMP_UNLOCK(); 709189592Sbms CURVNET_RESTORE(); 710189592Sbms} 711189592Sbms 712189592Sbmsstatic void 713189592Sbmsigi_delete_locked(const struct ifnet *ifp) 714189592Sbms{ 715189592Sbms struct igmp_ifinfo *igi, *tigi; 716189592Sbms 717189592Sbms CTR3(KTR_IGMPV3, "%s: freeing igmp_ifinfo for ifp %p(%s)", 718189592Sbms __func__, ifp, ifp->if_xname); 719189592Sbms 720189592Sbms IGMP_LOCK_ASSERT(); 721189592Sbms 722189592Sbms LIST_FOREACH_SAFE(igi, &V_igi_head, igi_link, tigi) { 723189592Sbms if (igi->igi_ifp == ifp) { 724189592Sbms /* 725189592Sbms * Free deferred General Query responses. 726189592Sbms */ 727189592Sbms _IF_DRAIN(&igi->igi_gq); 728189592Sbms 729189592Sbms LIST_REMOVE(igi, igi_link); 730189592Sbms 731189592Sbms KASSERT(SLIST_EMPTY(&igi->igi_relinmhead), 732189592Sbms ("%s: there are dangling in_multi references", 733189592Sbms __func__)); 734189592Sbms 735189592Sbms free(igi, M_IGMP); 736189592Sbms return; 737189592Sbms } 738189592Sbms } 739189592Sbms 740189592Sbms#ifdef INVARIANTS 741189592Sbms panic("%s: igmp_ifinfo not found for ifp %p\n", __func__, ifp); 742189592Sbms#endif 743189592Sbms} 744189592Sbms 745189592Sbms/* 746189592Sbms * Process a received IGMPv1 query. 747189592Sbms * Return non-zero if the message should be dropped. 748189592Sbms * 749189592Sbms * VIMAGE: The curvnet pointer is derived from the input ifp. 750189592Sbms */ 751189592Sbmsstatic int 752189592Sbmsigmp_input_v1_query(struct ifnet *ifp, const struct ip *ip) 753189592Sbms{ 754183550Szec INIT_VNET_INET(ifp->if_vnet); 755189592Sbms struct ifmultiaddr *ifma; 756189592Sbms struct igmp_ifinfo *igi; 757189592Sbms struct in_multi *inm; 7582531Swollman 759189592Sbms /* 760189592Sbms * IGMPv1 General Queries SHOULD always addressed to 224.0.0.1. 761189592Sbms * igmp_group is always ignored. Do not drop it as a userland 762189592Sbms * daemon may wish to see it. 763189592Sbms */ 764189592Sbms if (!in_allhosts(ip->ip_dst)) { 765189592Sbms ++V_igmpstat.igps_rcv_badqueries; 766189592Sbms return (0); 767189592Sbms } 768189592Sbms 769189592Sbms ++V_igmpstat.igps_rcv_gen_queries; 770189592Sbms 771189592Sbms /* 772189592Sbms * Switch to IGMPv1 host compatibility mode. 773189592Sbms */ 774189592Sbms IN_MULTI_LOCK(); 775189592Sbms IGMP_LOCK(); 776189592Sbms 777189592Sbms igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; 778189592Sbms KASSERT(igi != NULL, ("%s: no igmp_ifinfo for ifp %p", __func__, ifp)); 779189592Sbms 780189592Sbms if (igi->igi_flags & IGIF_LOOPBACK) { 781189592Sbms CTR2(KTR_IGMPV3, "ignore v1 query on IGIF_LOOPBACK ifp %p(%s)", 782189592Sbms ifp, ifp->if_xname); 783189592Sbms goto out_locked; 784189592Sbms } 785189592Sbms 786189592Sbms igmp_set_version(igi, IGMP_VERSION_1); 787189592Sbms 788189592Sbms CTR2(KTR_IGMPV3, "process v1 query on ifp %p(%s)", ifp, ifp->if_xname); 789189592Sbms 790189592Sbms /* 791189592Sbms * Start the timers in all of our group records 792189592Sbms * for the interface on which the query arrived, 793189592Sbms * except those which are already running. 794189592Sbms */ 795189592Sbms IF_ADDR_LOCK(ifp); 796189592Sbms TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 797189931Sbms if (ifma->ifma_addr->sa_family != AF_INET || 798189931Sbms ifma->ifma_protospec == NULL) 799189592Sbms continue; 800189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 801189592Sbms if (inm->inm_timer != 0) 802189592Sbms continue; 803189592Sbms switch (inm->inm_state) { 804189592Sbms case IGMP_NOT_MEMBER: 805189592Sbms case IGMP_SILENT_MEMBER: 806189592Sbms break; 807189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 808189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 809189592Sbms case IGMP_REPORTING_MEMBER: 810189592Sbms case IGMP_IDLE_MEMBER: 811189592Sbms case IGMP_LAZY_MEMBER: 812189592Sbms case IGMP_SLEEPING_MEMBER: 813189592Sbms case IGMP_AWAKENING_MEMBER: 814189592Sbms inm->inm_state = IGMP_REPORTING_MEMBER; 815189592Sbms inm->inm_timer = IGMP_RANDOM_DELAY( 816189592Sbms IGMP_V1V2_MAX_RI * PR_FASTHZ); 817189592Sbms V_current_state_timers_running = 1; 818189592Sbms break; 819189592Sbms case IGMP_LEAVING_MEMBER: 820189592Sbms break; 821119181Srwatson } 822119181Srwatson } 823189592Sbms IF_ADDR_UNLOCK(ifp); 824189592Sbms 825189592Sbmsout_locked: 826189592Sbms IGMP_UNLOCK(); 827189592Sbms IN_MULTI_UNLOCK(); 828189592Sbms 829189592Sbms return (0); 830189592Sbms} 831189592Sbms 832189592Sbms/* 833189592Sbms * Process a received IGMPv2 general or group-specific query. 834189592Sbms */ 835189592Sbmsstatic int 836189592Sbmsigmp_input_v2_query(struct ifnet *ifp, const struct ip *ip, 837189592Sbms const struct igmp *igmp) 838189592Sbms{ 839189592Sbms struct ifmultiaddr *ifma; 840189592Sbms struct igmp_ifinfo *igi; 841189592Sbms struct in_multi *inm; 842189592Sbms uint16_t timer; 843189592Sbms 844189592Sbms /* 845189592Sbms * Perform lazy allocation of IGMP link info if required, 846189592Sbms * and switch to IGMPv2 host compatibility mode. 847189592Sbms */ 848189592Sbms IN_MULTI_LOCK(); 849189592Sbms IGMP_LOCK(); 850189592Sbms 851189592Sbms igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; 852189592Sbms KASSERT(igi != NULL, ("%s: no igmp_ifinfo for ifp %p", __func__, ifp)); 853189592Sbms 854189592Sbms if (igi->igi_flags & IGIF_LOOPBACK) { 855189592Sbms CTR2(KTR_IGMPV3, "ignore v2 query on IGIF_LOOPBACK ifp %p(%s)", 856189592Sbms ifp, ifp->if_xname); 857189592Sbms goto out_locked; 858144163Ssam } 859189592Sbms 860189592Sbms igmp_set_version(igi, IGMP_VERSION_2); 861189592Sbms 862189592Sbms timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 863189592Sbms if (timer == 0) 864189592Sbms timer = 1; 865189592Sbms 866189592Sbms if (!in_nullhost(igmp->igmp_group)) { 867189592Sbms /* 868189592Sbms * IGMPv2 Group-Specific Query. 869189592Sbms * If this is a group-specific IGMPv2 query, we need only 870189592Sbms * look up the single group to process it. 871189592Sbms */ 872189592Sbms inm = inm_lookup(ifp, igmp->igmp_group); 873189592Sbms if (inm != NULL) { 874189592Sbms CTR3(KTR_IGMPV3, "process v2 query %s on ifp %p(%s)", 875189592Sbms inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname); 876189592Sbms igmp_v2_update_group(inm, timer); 877189592Sbms } 878189592Sbms ++V_igmpstat.igps_rcv_group_queries; 879189592Sbms } else { 880189592Sbms /* 881189592Sbms * IGMPv2 General Query. 882189592Sbms * If this was not sent to the all-hosts group, ignore it. 883189592Sbms */ 884189592Sbms if (in_allhosts(ip->ip_dst)) { 885189592Sbms /* 886189592Sbms * For each reporting group joined on this 887189592Sbms * interface, kick the report timer. 888189592Sbms */ 889189592Sbms CTR2(KTR_IGMPV3, 890189592Sbms "process v2 general query on ifp %p(%s)", 891189592Sbms ifp, ifp->if_xname); 892189592Sbms 893189592Sbms IF_ADDR_LOCK(ifp); 894189592Sbms TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 895189931Sbms if (ifma->ifma_addr->sa_family != AF_INET || 896189931Sbms ifma->ifma_protospec == NULL) 897189592Sbms continue; 898189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 899189592Sbms igmp_v2_update_group(inm, timer); 900189592Sbms } 901189592Sbms IF_ADDR_UNLOCK(ifp); 902189592Sbms } 903189592Sbms ++V_igmpstat.igps_rcv_gen_queries; 904189592Sbms } 905189592Sbms 906189592Sbmsout_locked: 907189592Sbms IGMP_UNLOCK(); 908189592Sbms IN_MULTI_UNLOCK(); 909189592Sbms 910189592Sbms return (0); 9112531Swollman} 9122531Swollman 913189592Sbms/* 914189592Sbms * Update the report timer on a group in response to an IGMPv2 query. 915189592Sbms * 916189592Sbms * If we are becoming the reporting member for this group, start the timer. 917189592Sbms * If we already are the reporting member for this group, and timer is 918189592Sbms * below the threshold, reset it. 919189592Sbms * 920189592Sbms * We may be updating the group for the first time since we switched 921189592Sbms * to IGMPv3. If we are, then we must clear any recorded source lists, 922189592Sbms * and transition to REPORTING state; the group timer is overloaded 923189592Sbms * for group and group-source query responses. 924189592Sbms * 925189592Sbms * Unlike IGMPv3, the delay per group should be jittered 926189592Sbms * to avoid bursts of IGMPv2 reports. 927189592Sbms */ 928189592Sbmsstatic void 929189592Sbmsigmp_v2_update_group(struct in_multi *inm, const int timer) 930189592Sbms{ 931189592Sbms 932189592Sbms CTR4(KTR_IGMPV3, "%s: %s/%s timer=%d", __func__, 933189592Sbms inet_ntoa(inm->inm_addr), inm->inm_ifp->if_xname, timer); 934189592Sbms 935189592Sbms IN_MULTI_LOCK_ASSERT(); 936189592Sbms 937189592Sbms switch (inm->inm_state) { 938189592Sbms case IGMP_NOT_MEMBER: 939189592Sbms case IGMP_SILENT_MEMBER: 940189592Sbms break; 941189592Sbms case IGMP_REPORTING_MEMBER: 942189592Sbms if (inm->inm_timer != 0 && 943189592Sbms inm->inm_timer <= timer) { 944189592Sbms CTR1(KTR_IGMPV3, "%s: REPORTING and timer running, " 945189592Sbms "skipping.", __func__); 946189592Sbms break; 947189592Sbms } 948189592Sbms /* FALLTHROUGH */ 949189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 950189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 951189592Sbms case IGMP_IDLE_MEMBER: 952189592Sbms case IGMP_LAZY_MEMBER: 953189592Sbms case IGMP_AWAKENING_MEMBER: 954189592Sbms CTR1(KTR_IGMPV3, "%s: ->REPORTING", __func__); 955189592Sbms inm->inm_state = IGMP_REPORTING_MEMBER; 956189592Sbms inm->inm_timer = IGMP_RANDOM_DELAY(timer); 957189592Sbms V_current_state_timers_running = 1; 958189592Sbms break; 959189592Sbms case IGMP_SLEEPING_MEMBER: 960189592Sbms CTR1(KTR_IGMPV3, "%s: ->AWAKENING", __func__); 961189592Sbms inm->inm_state = IGMP_AWAKENING_MEMBER; 962189592Sbms break; 963189592Sbms case IGMP_LEAVING_MEMBER: 964189592Sbms break; 965189592Sbms } 966189592Sbms} 967189592Sbms 968189592Sbms/* 969189592Sbms * Process a received IGMPv3 general, group-specific or 970189592Sbms * group-and-source-specific query. 971189592Sbms * Assumes m has already been pulled up to the full IGMP message length. 972189592Sbms * Return 0 if successful, otherwise an appropriate error code is returned. 973189592Sbms */ 974189592Sbmsstatic int 975189592Sbmsigmp_input_v3_query(struct ifnet *ifp, const struct ip *ip, 976189592Sbms /*const*/ struct igmpv3 *igmpv3) 977189592Sbms{ 978189592Sbms struct igmp_ifinfo *igi; 979189592Sbms struct in_multi *inm; 980189592Sbms uint32_t maxresp, nsrc, qqi; 981189592Sbms uint16_t timer; 982189592Sbms uint8_t qrv; 983189592Sbms 984189592Sbms CTR2(KTR_IGMPV3, "process v3 query on ifp %p(%s)", ifp, ifp->if_xname); 985189592Sbms 986189592Sbms maxresp = igmpv3->igmp_code; /* in 1/10ths of a second */ 987189592Sbms if (maxresp >= 128) { 988189592Sbms maxresp = IGMP_MANT(igmpv3->igmp_code) << 989189592Sbms (IGMP_EXP(igmpv3->igmp_code) + 3); 990189592Sbms } 991189592Sbms 992189592Sbms /* 993189592Sbms * Robustness must never be less than 2 for on-wire IGMPv3. 994189592Sbms * FIXME: Check if ifp has IGIF_LOOPBACK set, as we make 995189592Sbms * an exception for interfaces whose IGMPv3 state changes 996189592Sbms * are redirected to loopback (e.g. MANET). 997189592Sbms */ 998189592Sbms qrv = IGMP_QRV(igmpv3->igmp_misc); 999189592Sbms if (qrv < 2) { 1000189592Sbms CTR3(KTR_IGMPV3, "%s: clamping qrv %d to %d", __func__, 1001189592Sbms qrv, IGMP_RV_INIT); 1002189592Sbms qrv = IGMP_RV_INIT; 1003189592Sbms } 1004189592Sbms 1005189592Sbms qqi = igmpv3->igmp_qqi; 1006189592Sbms if (qqi >= 128) { 1007189592Sbms maxresp = IGMP_MANT(igmpv3->igmp_qqi) << 1008189592Sbms (IGMP_EXP(igmpv3->igmp_qqi) + 3); 1009189592Sbms } 1010189592Sbms 1011189592Sbms timer = maxresp * PR_FASTHZ / IGMP_TIMER_SCALE; 1012189592Sbms if (timer == 0) 1013189592Sbms timer = 1; 1014189592Sbms 1015189592Sbms nsrc = ntohs(igmpv3->igmp_numsrc); 1016189592Sbms 1017189592Sbms IN_MULTI_LOCK(); 1018189592Sbms IGMP_LOCK(); 1019189592Sbms 1020189592Sbms igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; 1021189592Sbms KASSERT(igi != NULL, ("%s: no igmp_ifinfo for ifp %p", __func__, ifp)); 1022189592Sbms 1023189592Sbms if (igi->igi_flags & IGIF_LOOPBACK) { 1024189592Sbms CTR2(KTR_IGMPV3, "ignore v3 query on IGIF_LOOPBACK ifp %p(%s)", 1025189592Sbms ifp, ifp->if_xname); 1026189592Sbms goto out_locked; 1027189592Sbms } 1028189592Sbms 1029189592Sbms igmp_set_version(igi, IGMP_VERSION_3); 1030189592Sbms 1031189592Sbms igi->igi_rv = qrv; 1032189592Sbms igi->igi_qi = qqi; 1033189592Sbms igi->igi_qri = maxresp; 1034189592Sbms 1035189592Sbms CTR4(KTR_IGMPV3, "%s: qrv %d qi %d qri %d", __func__, qrv, qqi, 1036189592Sbms maxresp); 1037189592Sbms 1038189592Sbms if (in_nullhost(igmpv3->igmp_group)) { 1039189592Sbms /* 1040189592Sbms * IGMPv3 General Query. 1041189592Sbms * Schedule a current-state report on this ifp for 1042189592Sbms * all groups, possibly containing source lists. 1043189592Sbms */ 1044189592Sbms ++V_igmpstat.igps_rcv_gen_queries; 1045189592Sbms 1046189592Sbms if (!in_allhosts(ip->ip_dst) || nsrc > 0) { 1047189592Sbms /* 1048189592Sbms * General Queries SHOULD be directed to 224.0.0.1. 1049189592Sbms * A general query with a source list has undefined 1050189592Sbms * behaviour; discard it. 1051189592Sbms */ 1052189592Sbms ++V_igmpstat.igps_rcv_badqueries; 1053189592Sbms goto out_locked; 1054189592Sbms } 1055189592Sbms 1056189592Sbms CTR2(KTR_IGMPV3, "process v3 general query on ifp %p(%s)", 1057189592Sbms ifp, ifp->if_xname); 1058189592Sbms 1059189592Sbms /* 1060189592Sbms * If there is a pending General Query response 1061189592Sbms * scheduled earlier than the selected delay, do 1062189592Sbms * not schedule any other reports. 1063189592Sbms * Otherwise, reset the interface timer. 1064189592Sbms */ 1065189592Sbms if (igi->igi_v3_timer == 0 || igi->igi_v3_timer >= timer) { 1066189592Sbms igi->igi_v3_timer = IGMP_RANDOM_DELAY(timer); 1067189592Sbms V_interface_timers_running = 1; 1068189592Sbms } 1069189592Sbms } else { 1070189592Sbms /* 1071189592Sbms * IGMPv3 Group-specific or Group-and-source-specific Query. 1072189592Sbms * 1073189592Sbms * Group-source-specific queries are throttled on 1074189592Sbms * a per-group basis to defeat denial-of-service attempts. 1075189592Sbms * Queries for groups we are not a member of on this 1076189592Sbms * link are simply ignored. 1077189592Sbms */ 1078189592Sbms inm = inm_lookup(ifp, igmpv3->igmp_group); 1079189592Sbms if (inm == NULL) 1080189592Sbms goto out_locked; 1081189592Sbms if (nsrc > 0) { 1082189592Sbms ++V_igmpstat.igps_rcv_gsr_queries; 1083189592Sbms if (!ratecheck(&inm->inm_lastgsrtv, 1084189592Sbms &V_igmp_gsrdelay)) { 1085189592Sbms CTR1(KTR_IGMPV3, "%s: GS query throttled.", 1086189592Sbms __func__); 1087189592Sbms ++V_igmpstat.igps_drop_gsr_queries; 1088189592Sbms goto out_locked; 1089189592Sbms } 1090189592Sbms } else { 1091189592Sbms ++V_igmpstat.igps_rcv_group_queries; 1092189592Sbms } 1093189592Sbms CTR3(KTR_IGMPV3, "process v3 %s query on ifp %p(%s)", 1094189592Sbms inet_ntoa(igmpv3->igmp_group), ifp, ifp->if_xname); 1095189592Sbms /* 1096189592Sbms * If there is a pending General Query response 1097189592Sbms * scheduled sooner than the selected delay, no 1098189592Sbms * further report need be scheduled. 1099189592Sbms * Otherwise, prepare to respond to the 1100189592Sbms * group-specific or group-and-source query. 1101189592Sbms */ 1102189592Sbms if (igi->igi_v3_timer == 0 || igi->igi_v3_timer >= timer) 1103189592Sbms igmp_input_v3_group_query(inm, igi, timer, igmpv3); 1104189592Sbms } 1105189592Sbms 1106189592Sbmsout_locked: 1107189592Sbms IGMP_UNLOCK(); 1108189592Sbms IN_MULTI_UNLOCK(); 1109189592Sbms 1110189592Sbms return (0); 1111189592Sbms} 1112189592Sbms 1113189592Sbms/* 1114189592Sbms * Process a recieved IGMPv3 group-specific or group-and-source-specific 1115189592Sbms * query. 1116189592Sbms * Return <0 if any error occured. Currently this is ignored. 1117189592Sbms */ 1118189592Sbmsstatic int 1119189592Sbmsigmp_input_v3_group_query(struct in_multi *inm, struct igmp_ifinfo *igi, 1120189592Sbms int timer, /*const*/ struct igmpv3 *igmpv3) 1121189592Sbms{ 1122189592Sbms int retval; 1123189592Sbms uint16_t nsrc; 1124189592Sbms 1125189592Sbms IN_MULTI_LOCK_ASSERT(); 1126189592Sbms IGMP_LOCK_ASSERT(); 1127189592Sbms 1128189592Sbms retval = 0; 1129189592Sbms 1130189592Sbms switch (inm->inm_state) { 1131189592Sbms case IGMP_NOT_MEMBER: 1132189592Sbms case IGMP_SILENT_MEMBER: 1133189592Sbms case IGMP_SLEEPING_MEMBER: 1134189592Sbms case IGMP_LAZY_MEMBER: 1135189592Sbms case IGMP_AWAKENING_MEMBER: 1136189592Sbms case IGMP_IDLE_MEMBER: 1137189592Sbms case IGMP_LEAVING_MEMBER: 1138189592Sbms return (retval); 1139189592Sbms break; 1140189592Sbms case IGMP_REPORTING_MEMBER: 1141189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 1142189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 1143189592Sbms break; 1144189592Sbms } 1145189592Sbms 1146189592Sbms nsrc = ntohs(igmpv3->igmp_numsrc); 1147189592Sbms 1148189592Sbms /* 1149189592Sbms * Deal with group-specific queries upfront. 1150189592Sbms * If any group query is already pending, purge any recorded 1151189592Sbms * source-list state if it exists, and schedule a query response 1152189592Sbms * for this group-specific query. 1153189592Sbms */ 1154189592Sbms if (nsrc == 0) { 1155189592Sbms if (inm->inm_state == IGMP_G_QUERY_PENDING_MEMBER || 1156189592Sbms inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER) { 1157189592Sbms inm_clear_recorded(inm); 1158189592Sbms timer = min(inm->inm_timer, timer); 1159189592Sbms } 1160189592Sbms inm->inm_state = IGMP_G_QUERY_PENDING_MEMBER; 1161189592Sbms inm->inm_timer = IGMP_RANDOM_DELAY(timer); 1162189592Sbms V_current_state_timers_running = 1; 1163189592Sbms return (retval); 1164189592Sbms } 1165189592Sbms 1166189592Sbms /* 1167189592Sbms * Deal with the case where a group-and-source-specific query has 1168189592Sbms * been received but a group-specific query is already pending. 1169189592Sbms */ 1170189592Sbms if (inm->inm_state == IGMP_G_QUERY_PENDING_MEMBER) { 1171189592Sbms timer = min(inm->inm_timer, timer); 1172189592Sbms inm->inm_timer = IGMP_RANDOM_DELAY(timer); 1173189592Sbms V_current_state_timers_running = 1; 1174189592Sbms return (retval); 1175189592Sbms } 1176189592Sbms 1177189592Sbms /* 1178189592Sbms * Finally, deal with the case where a group-and-source-specific 1179189592Sbms * query has been received, where a response to a previous g-s-r 1180189592Sbms * query exists, or none exists. 1181189592Sbms * In this case, we need to parse the source-list which the Querier 1182189592Sbms * has provided us with and check if we have any source list filter 1183189592Sbms * entries at T1 for these sources. If we do not, there is no need 1184189592Sbms * schedule a report and the query may be dropped. 1185189592Sbms * If we do, we must record them and schedule a current-state 1186189592Sbms * report for those sources. 1187189592Sbms * FIXME: Handling source lists larger than 1 mbuf requires that 1188189592Sbms * we pass the mbuf chain pointer down to this function, and use 1189189592Sbms * m_getptr() to walk the chain. 1190189592Sbms */ 1191189592Sbms if (inm->inm_nsrc > 0) { 1192189592Sbms const struct in_addr *ap; 1193189592Sbms int i, nrecorded; 1194189592Sbms 1195189592Sbms ap = (const struct in_addr *)(igmpv3 + 1); 1196189592Sbms nrecorded = 0; 1197189592Sbms for (i = 0; i < nsrc; i++, ap++) { 1198189592Sbms retval = inm_record_source(inm, ap->s_addr); 1199189592Sbms if (retval < 0) 1200189592Sbms break; 1201189592Sbms nrecorded += retval; 1202189592Sbms } 1203189592Sbms if (nrecorded > 0) { 1204189592Sbms CTR1(KTR_IGMPV3, 1205189592Sbms "%s: schedule response to SG query", __func__); 1206189592Sbms inm->inm_state = IGMP_SG_QUERY_PENDING_MEMBER; 1207189592Sbms inm->inm_timer = IGMP_RANDOM_DELAY(timer); 1208189592Sbms V_current_state_timers_running = 1; 1209189592Sbms } 1210189592Sbms } 1211189592Sbms 1212189592Sbms return (retval); 1213189592Sbms} 1214189592Sbms 1215189592Sbms/* 1216189592Sbms * Process a received IGMPv1 host membership report. 1217189592Sbms * 1218189592Sbms * NOTE: 0.0.0.0 workaround breaks const correctness. 1219189592Sbms */ 1220189592Sbmsstatic int 1221189592Sbmsigmp_input_v1_report(struct ifnet *ifp, /*const*/ struct ip *ip, 1222189592Sbms /*const*/ struct igmp *igmp) 1223189592Sbms{ 1224189592Sbms struct in_ifaddr *ia; 1225189592Sbms struct in_multi *inm; 1226189592Sbms 1227189592Sbms ++V_igmpstat.igps_rcv_reports; 1228189592Sbms 1229189592Sbms if (ifp->if_flags & IFF_LOOPBACK) 1230189592Sbms return (0); 1231189592Sbms 1232189592Sbms if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr) || 1233189592Sbms !in_hosteq(igmp->igmp_group, ip->ip_dst))) { 1234189592Sbms ++V_igmpstat.igps_rcv_badreports; 1235189592Sbms return (EINVAL); 1236189592Sbms } 1237189592Sbms 1238189592Sbms /* 1239189592Sbms * RFC 3376, Section 4.2.13, 9.2, 9.3: 1240189592Sbms * Booting clients may use the source address 0.0.0.0. Some 1241189592Sbms * IGMP daemons may not know how to use IP_RECVIF to determine 1242189592Sbms * the interface upon which this message was received. 1243189592Sbms * Replace 0.0.0.0 with the subnet address if told to do so. 1244189592Sbms */ 1245189592Sbms if (V_igmp_recvifkludge && in_nullhost(ip->ip_src)) { 1246189592Sbms IFP_TO_IA(ifp, ia); 1247189592Sbms if (ia != NULL) 1248189592Sbms ip->ip_src.s_addr = htonl(ia->ia_subnet); 1249189592Sbms } 1250189592Sbms 1251189592Sbms CTR3(KTR_IGMPV3, "process v1 report %s on ifp %p(%s)", 1252189592Sbms inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname); 1253189592Sbms 1254189592Sbms /* 1255189592Sbms * IGMPv1 report suppression. 1256189592Sbms * If we are a member of this group, and our membership should be 1257189592Sbms * reported, stop our group timer and transition to the 'lazy' state. 1258189592Sbms */ 1259189592Sbms IN_MULTI_LOCK(); 1260189592Sbms inm = inm_lookup(ifp, igmp->igmp_group); 1261189592Sbms if (inm != NULL) { 1262189592Sbms struct igmp_ifinfo *igi; 1263189592Sbms 1264189592Sbms igi = inm->inm_igi; 1265189592Sbms if (igi == NULL) { 1266189592Sbms KASSERT(igi != NULL, 1267189592Sbms ("%s: no igi for ifp %p", __func__, ifp)); 1268189592Sbms goto out_locked; 1269189592Sbms } 1270189592Sbms 1271189592Sbms ++V_igmpstat.igps_rcv_ourreports; 1272189592Sbms 1273189592Sbms /* 1274189592Sbms * If we are in IGMPv3 host mode, do not allow the 1275189592Sbms * other host's IGMPv1 report to suppress our reports 1276189592Sbms * unless explicitly configured to do so. 1277189592Sbms */ 1278189592Sbms if (igi->igi_version == IGMP_VERSION_3) { 1279189592Sbms if (V_igmp_legacysupp) 1280189592Sbms igmp_v3_suppress_group_record(inm); 1281189592Sbms goto out_locked; 1282189592Sbms } 1283189592Sbms 1284189592Sbms inm->inm_timer = 0; 1285189592Sbms 1286189592Sbms switch (inm->inm_state) { 1287189592Sbms case IGMP_NOT_MEMBER: 1288189592Sbms case IGMP_SILENT_MEMBER: 1289189592Sbms break; 1290189592Sbms case IGMP_IDLE_MEMBER: 1291189592Sbms case IGMP_LAZY_MEMBER: 1292189592Sbms case IGMP_AWAKENING_MEMBER: 1293189592Sbms CTR3(KTR_IGMPV3, 1294189592Sbms "report suppressed for %s on ifp %p(%s)", 1295189592Sbms inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname); 1296189592Sbms case IGMP_SLEEPING_MEMBER: 1297189592Sbms inm->inm_state = IGMP_SLEEPING_MEMBER; 1298189592Sbms break; 1299189592Sbms case IGMP_REPORTING_MEMBER: 1300189592Sbms CTR3(KTR_IGMPV3, 1301189592Sbms "report suppressed for %s on ifp %p(%s)", 1302189592Sbms inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname); 1303189592Sbms if (igi->igi_version == IGMP_VERSION_1) 1304189592Sbms inm->inm_state = IGMP_LAZY_MEMBER; 1305189592Sbms else if (igi->igi_version == IGMP_VERSION_2) 1306189592Sbms inm->inm_state = IGMP_SLEEPING_MEMBER; 1307189592Sbms break; 1308189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 1309189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 1310189592Sbms case IGMP_LEAVING_MEMBER: 1311189592Sbms break; 1312189592Sbms } 1313189592Sbms } 1314189592Sbms 1315189592Sbmsout_locked: 1316189592Sbms IN_MULTI_UNLOCK(); 1317189592Sbms 1318189592Sbms return (0); 1319189592Sbms} 1320189592Sbms 1321189592Sbms/* 1322189592Sbms * Process a received IGMPv2 host membership report. 1323189592Sbms * 1324189592Sbms * NOTE: 0.0.0.0 workaround breaks const correctness. 1325189592Sbms */ 1326189592Sbmsstatic int 1327189592Sbmsigmp_input_v2_report(struct ifnet *ifp, /*const*/ struct ip *ip, 1328189592Sbms /*const*/ struct igmp *igmp) 1329189592Sbms{ 1330189592Sbms struct in_ifaddr *ia; 1331189592Sbms struct in_multi *inm; 1332189592Sbms 1333189592Sbms /* 1334189592Sbms * Make sure we don't hear our own membership report. Fast 1335189592Sbms * leave requires knowing that we are the only member of a 1336189592Sbms * group. 1337189592Sbms */ 1338189592Sbms IFP_TO_IA(ifp, ia); 1339189592Sbms if (ia != NULL && in_hosteq(ip->ip_src, IA_SIN(ia)->sin_addr)) 1340189592Sbms return (0); 1341189592Sbms 1342189592Sbms ++V_igmpstat.igps_rcv_reports; 1343189592Sbms 1344189592Sbms if (ifp->if_flags & IFF_LOOPBACK) 1345189592Sbms return (0); 1346189592Sbms 1347189592Sbms if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 1348189592Sbms !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 1349189592Sbms ++V_igmpstat.igps_rcv_badreports; 1350189592Sbms return (EINVAL); 1351189592Sbms } 1352189592Sbms 1353189592Sbms /* 1354189592Sbms * RFC 3376, Section 4.2.13, 9.2, 9.3: 1355189592Sbms * Booting clients may use the source address 0.0.0.0. Some 1356189592Sbms * IGMP daemons may not know how to use IP_RECVIF to determine 1357189592Sbms * the interface upon which this message was received. 1358189592Sbms * Replace 0.0.0.0 with the subnet address if told to do so. 1359189592Sbms */ 1360189592Sbms if (V_igmp_recvifkludge && in_nullhost(ip->ip_src)) { 1361189592Sbms if (ia != NULL) 1362189592Sbms ip->ip_src.s_addr = htonl(ia->ia_subnet); 1363189592Sbms } 1364189592Sbms 1365189592Sbms CTR3(KTR_IGMPV3, "process v2 report %s on ifp %p(%s)", 1366189592Sbms inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname); 1367189592Sbms 1368189592Sbms /* 1369189592Sbms * IGMPv2 report suppression. 1370189592Sbms * If we are a member of this group, and our membership should be 1371189592Sbms * reported, and our group timer is pending or about to be reset, 1372189592Sbms * stop our group timer by transitioning to the 'lazy' state. 1373189592Sbms */ 1374189592Sbms IN_MULTI_LOCK(); 1375189592Sbms inm = inm_lookup(ifp, igmp->igmp_group); 1376189592Sbms if (inm != NULL) { 1377189592Sbms struct igmp_ifinfo *igi; 1378189592Sbms 1379189592Sbms igi = inm->inm_igi; 1380189592Sbms KASSERT(igi != NULL, ("%s: no igi for ifp %p", __func__, ifp)); 1381189592Sbms 1382189592Sbms ++V_igmpstat.igps_rcv_ourreports; 1383189592Sbms 1384189592Sbms /* 1385189592Sbms * If we are in IGMPv3 host mode, do not allow the 1386189592Sbms * other host's IGMPv1 report to suppress our reports 1387189592Sbms * unless explicitly configured to do so. 1388189592Sbms */ 1389189592Sbms if (igi->igi_version == IGMP_VERSION_3) { 1390189592Sbms if (V_igmp_legacysupp) 1391189592Sbms igmp_v3_suppress_group_record(inm); 1392189592Sbms goto out_locked; 1393189592Sbms } 1394189592Sbms 1395189592Sbms inm->inm_timer = 0; 1396189592Sbms 1397189592Sbms switch (inm->inm_state) { 1398189592Sbms case IGMP_NOT_MEMBER: 1399189592Sbms case IGMP_SILENT_MEMBER: 1400189592Sbms case IGMP_SLEEPING_MEMBER: 1401189592Sbms break; 1402189592Sbms case IGMP_REPORTING_MEMBER: 1403189592Sbms case IGMP_IDLE_MEMBER: 1404189592Sbms case IGMP_AWAKENING_MEMBER: 1405189592Sbms CTR3(KTR_IGMPV3, 1406189592Sbms "report suppressed for %s on ifp %p(%s)", 1407189592Sbms inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname); 1408189592Sbms case IGMP_LAZY_MEMBER: 1409189592Sbms inm->inm_state = IGMP_LAZY_MEMBER; 1410189592Sbms break; 1411189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 1412189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 1413189592Sbms case IGMP_LEAVING_MEMBER: 1414189592Sbms break; 1415189592Sbms } 1416189592Sbms } 1417189592Sbms 1418189592Sbmsout_locked: 1419189592Sbms IN_MULTI_UNLOCK(); 1420189592Sbms 1421189592Sbms return (0); 1422189592Sbms} 1423189592Sbms 14241541Srgrimesvoid 1425189592Sbmsigmp_input(struct mbuf *m, int off) 14261541Srgrimes{ 1427189592Sbms int iphlen; 1428189592Sbms struct ifnet *ifp; 1429189592Sbms struct igmp *igmp; 1430189592Sbms struct ip *ip; 1431189592Sbms int igmplen; 1432189592Sbms int minlen; 1433189592Sbms int queryver; 1434189592Sbms 1435189592Sbms CTR3(KTR_IGMPV3, "%s: called w/mbuf (%p,%d)", __func__, m, off); 1436189592Sbms 1437189592Sbms ifp = m->m_pkthdr.rcvif; 1438183550Szec INIT_VNET_INET(ifp->if_vnet); 14391541Srgrimes 1440181803Sbz ++V_igmpstat.igps_rcv_total; 14411541Srgrimes 14421541Srgrimes ip = mtod(m, struct ip *); 1443189592Sbms iphlen = off; 14441541Srgrimes igmplen = ip->ip_len; 14451541Srgrimes 14461541Srgrimes /* 1447164863Srwatson * Validate lengths. 14481541Srgrimes */ 14491541Srgrimes if (igmplen < IGMP_MINLEN) { 1450181803Sbz ++V_igmpstat.igps_rcv_tooshort; 14511541Srgrimes m_freem(m); 14521541Srgrimes return; 14531541Srgrimes } 1454189592Sbms 1455189592Sbms /* 1456189592Sbms * Always pullup to the minimum size for v1/v2 or v3 1457189592Sbms * to amortize calls to m_pullup(). 1458189592Sbms */ 1459189592Sbms minlen = iphlen; 1460189592Sbms if (igmplen >= IGMP_V3_QUERY_MINLEN) 1461189592Sbms minlen += IGMP_V3_QUERY_MINLEN; 1462189592Sbms else 1463189592Sbms minlen += IGMP_MINLEN; 14641541Srgrimes if ((m->m_flags & M_EXT || m->m_len < minlen) && 14651541Srgrimes (m = m_pullup(m, minlen)) == 0) { 1466181803Sbz ++V_igmpstat.igps_rcv_tooshort; 14671541Srgrimes return; 14681541Srgrimes } 1469189592Sbms ip = mtod(m, struct ip *); 14701541Srgrimes 1471189592Sbms if (ip->ip_ttl != 1) { 1472189592Sbms ++V_igmpstat.igps_rcv_badttl; 1473189592Sbms m_freem(m); 1474189592Sbms return; 1475189592Sbms } 1476189592Sbms 14771541Srgrimes /* 1478164863Srwatson * Validate checksum. 14791541Srgrimes */ 14801541Srgrimes m->m_data += iphlen; 14811541Srgrimes m->m_len -= iphlen; 14821541Srgrimes igmp = mtod(m, struct igmp *); 14831541Srgrimes if (in_cksum(m, igmplen)) { 1484181803Sbz ++V_igmpstat.igps_rcv_badsum; 14851541Srgrimes m_freem(m); 14861541Srgrimes return; 14871541Srgrimes } 14881541Srgrimes m->m_data -= iphlen; 14891541Srgrimes m->m_len += iphlen; 14902531Swollman 14911541Srgrimes switch (igmp->igmp_type) { 1492189592Sbms case IGMP_HOST_MEMBERSHIP_QUERY: 1493189592Sbms if (igmplen == IGMP_MINLEN) { 1494189592Sbms if (igmp->igmp_code == 0) 1495189592Sbms queryver = IGMP_VERSION_1; 1496189592Sbms else 1497189592Sbms queryver = IGMP_VERSION_2; 1498189592Sbms } else if (igmplen >= IGMP_V3_QUERY_MINLEN) { 1499189592Sbms queryver = IGMP_VERSION_3; 1500189592Sbms } else { 1501189592Sbms ++V_igmpstat.igps_rcv_tooshort; 1502189592Sbms m_freem(m); 1503189592Sbms return; 1504189592Sbms } 15051541Srgrimes 1506189592Sbms switch (queryver) { 1507189592Sbms case IGMP_VERSION_1: 1508189592Sbms ++V_igmpstat.igps_rcv_v1v2_queries; 1509189592Sbms if (!V_igmp_v1enable) 1510189592Sbms break; 1511189592Sbms if (igmp_input_v1_query(ifp, ip) != 0) { 1512144163Ssam m_freem(m); 1513144163Ssam return; 1514144163Ssam } 1515189592Sbms break; 15164028Spst 1517189592Sbms case IGMP_VERSION_2: 1518189592Sbms ++V_igmpstat.igps_rcv_v1v2_queries; 1519189592Sbms if (!V_igmp_v2enable) 1520189592Sbms break; 1521189592Sbms if (igmp_input_v2_query(ifp, ip, igmp) != 0) { 15222531Swollman m_freem(m); 15232531Swollman return; 15242531Swollman } 1525189592Sbms break; 15262531Swollman 1527189592Sbms case IGMP_VERSION_3: { 1528189592Sbms struct igmpv3 *igmpv3; 1529189592Sbms uint16_t igmpv3len; 1530189592Sbms uint16_t srclen; 1531189592Sbms int nsrc; 1532189592Sbms 1533189592Sbms ++V_igmpstat.igps_rcv_v3_queries; 1534189592Sbms igmpv3 = (struct igmpv3 *)igmp; 1535189592Sbms /* 1536189592Sbms * Validate length based on source count. 1537189592Sbms */ 1538189592Sbms nsrc = ntohs(igmpv3->igmp_numsrc); 1539189592Sbms srclen = sizeof(struct in_addr) * nsrc; 1540189592Sbms if (nsrc * sizeof(in_addr_t) > srclen) { 1541189592Sbms ++V_igmpstat.igps_rcv_tooshort; 1542189592Sbms return; 15432531Swollman } 1544189592Sbms /* 1545189592Sbms * m_pullup() may modify m, so pullup in 1546189592Sbms * this scope. 1547189592Sbms */ 1548189592Sbms igmpv3len = iphlen + IGMP_V3_QUERY_MINLEN + 1549189592Sbms srclen; 1550189592Sbms if ((m->m_flags & M_EXT || 1551189592Sbms m->m_len < igmpv3len) && 1552189592Sbms (m = m_pullup(m, igmpv3len)) == NULL) { 1553189592Sbms ++V_igmpstat.igps_rcv_tooshort; 1554189592Sbms return; 1555189592Sbms } 1556189592Sbms igmpv3 = (struct igmpv3 *)(mtod(m, uint8_t *) 1557189592Sbms + iphlen); 1558189592Sbms if (igmp_input_v3_query(ifp, ip, igmpv3) != 0) { 1559189592Sbms m_freem(m); 1560189592Sbms return; 1561189592Sbms } 15621541Srgrimes } 1563189592Sbms break; 15641541Srgrimes } 15651541Srgrimes break; 15661541Srgrimes 1567189592Sbms case IGMP_v1_HOST_MEMBERSHIP_REPORT: 1568189592Sbms if (!V_igmp_v1enable) 156914622Sfenner break; 1570189592Sbms if (igmp_input_v1_report(ifp, ip, igmp) != 0) { 1571189592Sbms m_freem(m); 1572189592Sbms return; 1573189592Sbms } 1574189592Sbms break; 157514622Sfenner 1576189592Sbms case IGMP_v2_HOST_MEMBERSHIP_REPORT: 1577189592Sbms if (!V_igmp_v2enable) 15781541Srgrimes break; 1579189592Sbms if (!ip_checkrouteralert(m)) 1580189592Sbms ++V_igmpstat.igps_rcv_nora; 1581189592Sbms if (igmp_input_v2_report(ifp, ip, igmp) != 0) { 15821541Srgrimes m_freem(m); 15831541Srgrimes return; 15841541Srgrimes } 1585189592Sbms break; 15861541Srgrimes 1587189592Sbms case IGMP_v3_HOST_MEMBERSHIP_REPORT: 15881541Srgrimes /* 1589189592Sbms * Hosts do not need to process IGMPv3 membership reports, 1590189592Sbms * as report suppression is no longer required. 15911541Srgrimes */ 1592189592Sbms if (!ip_checkrouteralert(m)) 1593189592Sbms ++V_igmpstat.igps_rcv_nora; 1594189592Sbms break; 15951541Srgrimes 1596189592Sbms default: 15971541Srgrimes break; 15981541Srgrimes } 15991541Srgrimes 16001541Srgrimes /* 1601164863Srwatson * Pass all valid IGMP packets up to any process(es) listening on a 1602164863Srwatson * raw IGMP socket. 16031541Srgrimes */ 160482890Sjulian rip_input(m, off); 16051541Srgrimes} 16061541Srgrimes 1607189592Sbms 1608189592Sbms/* 1609189592Sbms * Fast timeout handler (global). 1610189592Sbms * VIMAGE: Timeout handlers are expected to service all vimages. 1611189592Sbms */ 16121541Srgrimesvoid 1613189592Sbmsigmp_fasttimo(void) 16141541Srgrimes{ 1615189592Sbms#ifdef VIMAGE 1616189592Sbms VNET_ITERATOR_DECL(vnet_iter); 16171541Srgrimes 1618189592Sbms VNET_LIST_RLOCK(); 1619189592Sbms VNET_FOREACH(vnet_iter) { 1620189592Sbms CURVNET_SET(vnet_iter); 1621189592Sbms INIT_VNET_INET(vnet_iter); 1622189592Sbms igmp_fasttimo_vnet(); 1623189592Sbms CURVNET_RESTORE(); 1624189592Sbms } 1625189592Sbms VNET_LIST_RUNLOCK(); 1626189592Sbms#else /* !VIMAGE */ 1627189592Sbms 1628189592Sbms igmp_fasttimo_vnet(); 1629189592Sbms#endif /* VIMAGE */ 1630189592Sbms} 1631189592Sbms 1632189592Sbms/* 1633189592Sbms * Fast timeout handler (per-vnet). 1634189592Sbms * Sends are shuffled off to a netisr to deal with Giant. 1635189592Sbms * 1636189592Sbms * VIMAGE: Assume caller has set up our curvnet. 1637189592Sbms */ 1638189592Sbmsstatic void 1639189592Sbmsigmp_fasttimo_vnet(void) 1640189592Sbms{ 1641189592Sbms struct ifqueue scq; /* State-change packets */ 1642189592Sbms struct ifqueue qrq; /* Query response packets */ 1643189592Sbms struct ifnet *ifp; 1644189592Sbms struct igmp_ifinfo *igi; 1645189592Sbms struct ifmultiaddr *ifma, *tifma; 1646189592Sbms struct in_multi *inm; 1647189592Sbms int loop, uri_fasthz; 1648189592Sbms 1649189592Sbms loop = 0; 1650189592Sbms uri_fasthz = 0; 1651189592Sbms 1652189592Sbms /* 1653189592Sbms * Quick check to see if any work needs to be done, in order to 1654189592Sbms * minimize the overhead of fasttimo processing. 1655189592Sbms * SMPng: XXX Unlocked reads. 1656189592Sbms */ 1657189592Sbms if (!V_current_state_timers_running && 1658189592Sbms !V_interface_timers_running && 1659189592Sbms !V_state_change_timers_running) 1660189592Sbms return; 1661189592Sbms 1662189592Sbms if (!mpsafe_igmp) 1663189592Sbms mtx_lock(&Giant); 1664189592Sbms 1665189592Sbms IN_MULTI_LOCK(); 1666189592Sbms IGMP_LOCK(); 1667189592Sbms 1668189592Sbms /* 1669189592Sbms * IGMPv3 General Query response timer processing. 1670189592Sbms */ 1671189592Sbms if (V_interface_timers_running) { 1672189592Sbms CTR1(KTR_IGMPV3, "%s: interface timers running", __func__); 1673189592Sbms 1674189592Sbms V_interface_timers_running = 0; 1675189592Sbms LIST_FOREACH(igi, &V_igi_head, igi_link) { 1676189592Sbms if (igi->igi_v3_timer == 0) { 1677189592Sbms /* Do nothing. */ 1678189592Sbms } else if (--igi->igi_v3_timer == 0) { 1679189592Sbms igmp_v3_dispatch_general_query(igi); 1680189592Sbms } else { 1681189592Sbms V_interface_timers_running = 1; 1682189592Sbms } 1683189592Sbms } 1684189592Sbms } 1685189592Sbms 1686189592Sbms if (!V_current_state_timers_running && 1687189592Sbms !V_state_change_timers_running) 1688189592Sbms goto out_locked; 1689189592Sbms 1690189592Sbms V_current_state_timers_running = 0; 1691189592Sbms V_state_change_timers_running = 0; 1692189592Sbms 1693189592Sbms CTR1(KTR_IGMPV3, "%s: state change timers running", __func__); 1694189592Sbms 1695189592Sbms /* 1696189592Sbms * IGMPv1/v2/v3 host report and state-change timer processing. 1697189592Sbms * Note: Processing a v3 group timer may remove a node. 1698189592Sbms */ 1699189592Sbms LIST_FOREACH(igi, &V_igi_head, igi_link) { 1700189592Sbms ifp = igi->igi_ifp; 1701189592Sbms 1702189592Sbms if (igi->igi_version == IGMP_VERSION_3) { 1703189592Sbms loop = (igi->igi_flags & IGIF_LOOPBACK) ? 1 : 0; 1704189592Sbms uri_fasthz = IGMP_RANDOM_DELAY(igi->igi_uri * 1705189592Sbms PR_FASTHZ); 1706189592Sbms 1707189592Sbms memset(&qrq, 0, sizeof(struct ifqueue)); 1708189592Sbms IFQ_SET_MAXLEN(&qrq, IGMP_MAX_G_GS_PACKETS); 1709189592Sbms 1710189592Sbms memset(&scq, 0, sizeof(struct ifqueue)); 1711189592Sbms IFQ_SET_MAXLEN(&scq, IGMP_MAX_STATE_CHANGE_PACKETS); 1712189592Sbms } 1713189592Sbms 1714189592Sbms IF_ADDR_LOCK(ifp); 1715189592Sbms TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, 1716189592Sbms tifma) { 1717189931Sbms if (ifma->ifma_addr->sa_family != AF_INET || 1718189931Sbms ifma->ifma_protospec == NULL) 1719189592Sbms continue; 1720189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 1721189592Sbms switch (igi->igi_version) { 1722189592Sbms case IGMP_VERSION_1: 1723189592Sbms case IGMP_VERSION_2: 1724189592Sbms igmp_v1v2_process_group_timer(inm, 1725189592Sbms igi->igi_version); 1726189592Sbms break; 1727189592Sbms case IGMP_VERSION_3: 1728189592Sbms igmp_v3_process_group_timers(igi, &qrq, 1729189592Sbms &scq, inm, uri_fasthz); 1730189592Sbms break; 1731189592Sbms } 1732189592Sbms } 1733189592Sbms IF_ADDR_UNLOCK(ifp); 1734189592Sbms 1735189592Sbms if (igi->igi_version == IGMP_VERSION_3) { 1736189592Sbms struct in_multi *tinm; 1737189592Sbms 1738189592Sbms igmp_dispatch_queue(&qrq, 0, loop); 1739189592Sbms igmp_dispatch_queue(&scq, 0, loop); 1740189592Sbms 1741189592Sbms /* 1742189592Sbms * Free the in_multi reference(s) for this 1743189592Sbms * IGMP lifecycle. 1744189592Sbms */ 1745189592Sbms SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead, 1746189592Sbms inm_nrele, tinm) { 1747189592Sbms SLIST_REMOVE_HEAD(&igi->igi_relinmhead, 1748189592Sbms inm_nrele); 1749189592Sbms inm_release_locked(inm); 1750189592Sbms } 1751189592Sbms } 1752189592Sbms } 1753189592Sbms 1754189592Sbmsout_locked: 1755189592Sbms IGMP_UNLOCK(); 1756189592Sbms IN_MULTI_UNLOCK(); 1757189592Sbms if (!mpsafe_igmp) 1758189592Sbms mtx_unlock(&Giant); 1759189592Sbms} 1760189592Sbms 1761189592Sbms/* 1762189592Sbms * Update host report group timer for IGMPv1/v2. 1763189592Sbms * Will update the global pending timer flags. 1764189592Sbms */ 1765189592Sbmsstatic void 1766189592Sbmsigmp_v1v2_process_group_timer(struct in_multi *inm, const int version) 1767189592Sbms{ 1768189592Sbms int report_timer_expired; 1769189592Sbms 1770148682Srwatson IN_MULTI_LOCK_ASSERT(); 1771189592Sbms IGMP_LOCK_ASSERT(); 1772148682Srwatson 1773189592Sbms if (inm->inm_timer == 0) { 1774189592Sbms report_timer_expired = 0; 1775189592Sbms } else if (--inm->inm_timer == 0) { 1776189592Sbms report_timer_expired = 1; 177714622Sfenner } else { 1778189592Sbms V_current_state_timers_running = 1; 1779189592Sbms return; 1780189592Sbms } 1781189592Sbms 1782189592Sbms switch (inm->inm_state) { 1783189592Sbms case IGMP_NOT_MEMBER: 1784189592Sbms case IGMP_SILENT_MEMBER: 1785189592Sbms case IGMP_IDLE_MEMBER: 1786189592Sbms case IGMP_LAZY_MEMBER: 1787189592Sbms case IGMP_SLEEPING_MEMBER: 1788189592Sbms case IGMP_AWAKENING_MEMBER: 1789189592Sbms break; 1790189592Sbms case IGMP_REPORTING_MEMBER: 1791189592Sbms if (report_timer_expired) { 1792189592Sbms inm->inm_state = IGMP_IDLE_MEMBER; 1793189592Sbms (void)igmp_v1v2_queue_report(inm, 1794189592Sbms (version == IGMP_VERSION_2) ? 1795189592Sbms IGMP_v2_HOST_MEMBERSHIP_REPORT : 1796189592Sbms IGMP_v1_HOST_MEMBERSHIP_REPORT); 1797144163Ssam } 1798189592Sbms break; 1799189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 1800189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 1801189592Sbms case IGMP_LEAVING_MEMBER: 1802189592Sbms break; 18031541Srgrimes } 18041541Srgrimes} 18051541Srgrimes 1806189592Sbms/* 1807189592Sbms * Update a group's timers for IGMPv3. 1808189592Sbms * Will update the global pending timer flags. 1809189592Sbms * Note: Unlocked read from igi. 1810189592Sbms */ 1811189592Sbmsstatic void 1812189592Sbmsigmp_v3_process_group_timers(struct igmp_ifinfo *igi, 1813189592Sbms struct ifqueue *qrq, struct ifqueue *scq, 1814189592Sbms struct in_multi *inm, const int uri_fasthz) 18151541Srgrimes{ 1816189592Sbms int query_response_timer_expired; 1817189592Sbms int state_change_retransmit_timer_expired; 1818119181Srwatson 1819148682Srwatson IN_MULTI_LOCK_ASSERT(); 1820189592Sbms IGMP_LOCK_ASSERT(); 1821148682Srwatson 1822189592Sbms query_response_timer_expired = 0; 1823189592Sbms state_change_retransmit_timer_expired = 0; 1824189592Sbms 1825189592Sbms /* 1826189592Sbms * During a transition from v1/v2 compatibility mode back to v3, 1827189592Sbms * a group record in REPORTING state may still have its group 1828189592Sbms * timer active. This is a no-op in this function; it is easier 1829189592Sbms * to deal with it here than to complicate the slow-timeout path. 1830189592Sbms */ 1831189592Sbms if (inm->inm_timer == 0) { 1832189592Sbms query_response_timer_expired = 0; 1833189592Sbms } else if (--inm->inm_timer == 0) { 1834189592Sbms query_response_timer_expired = 1; 1835189592Sbms } else { 1836189592Sbms V_current_state_timers_running = 1; 1837189592Sbms } 1838189592Sbms 1839189592Sbms if (inm->inm_sctimer == 0) { 1840189592Sbms state_change_retransmit_timer_expired = 0; 1841189592Sbms } else if (--inm->inm_sctimer == 0) { 1842189592Sbms state_change_retransmit_timer_expired = 1; 1843189592Sbms } else { 1844189592Sbms V_state_change_timers_running = 1; 1845189592Sbms } 1846189592Sbms 1847189592Sbms /* We are in fasttimo, so be quick about it. */ 1848189592Sbms if (!state_change_retransmit_timer_expired && 1849189592Sbms !query_response_timer_expired) 1850189592Sbms return; 1851189592Sbms 1852189592Sbms switch (inm->inm_state) { 1853189592Sbms case IGMP_NOT_MEMBER: 1854189592Sbms case IGMP_SILENT_MEMBER: 1855189592Sbms case IGMP_SLEEPING_MEMBER: 1856189592Sbms case IGMP_LAZY_MEMBER: 1857189592Sbms case IGMP_AWAKENING_MEMBER: 1858189592Sbms case IGMP_IDLE_MEMBER: 1859189592Sbms break; 1860189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 1861189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 1862189592Sbms /* 1863189592Sbms * Respond to a previously pending Group-Specific 1864189592Sbms * or Group-and-Source-Specific query by enqueueing 1865189592Sbms * the appropriate Current-State report for 1866189592Sbms * immediate transmission. 1867189592Sbms */ 1868189592Sbms if (query_response_timer_expired) { 1869189592Sbms int retval; 1870189592Sbms 1871189592Sbms retval = igmp_v3_enqueue_group_record(qrq, inm, 0, 1, 1872189592Sbms (inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER)); 1873189592Sbms CTR2(KTR_IGMPV3, "%s: enqueue record = %d", 1874189592Sbms __func__, retval); 1875189592Sbms inm->inm_state = IGMP_REPORTING_MEMBER; 1876189592Sbms /* XXX Clear recorded sources for next time. */ 1877189592Sbms inm_clear_recorded(inm); 1878189592Sbms } 1879189592Sbms /* FALLTHROUGH */ 1880189592Sbms case IGMP_REPORTING_MEMBER: 1881189592Sbms case IGMP_LEAVING_MEMBER: 1882189592Sbms if (state_change_retransmit_timer_expired) { 1883189592Sbms /* 1884189592Sbms * State-change retransmission timer fired. 1885189592Sbms * If there are any further pending retransmissions, 1886189592Sbms * set the global pending state-change flag, and 1887189592Sbms * reset the timer. 1888189592Sbms */ 1889189592Sbms if (--inm->inm_scrv > 0) { 1890189592Sbms inm->inm_sctimer = uri_fasthz; 1891189592Sbms V_state_change_timers_running = 1; 1892189592Sbms } 1893189592Sbms /* 1894189592Sbms * Retransmit the previously computed state-change 1895189592Sbms * report. If there are no further pending 1896189592Sbms * retransmissions, the mbuf queue will be consumed. 1897189592Sbms * Update T0 state to T1 as we have now sent 1898189592Sbms * a state-change. 1899189592Sbms */ 1900189592Sbms (void)igmp_v3_merge_state_changes(inm, scq); 1901189592Sbms 1902189592Sbms inm_commit(inm); 1903189592Sbms CTR3(KTR_IGMPV3, "%s: T1 -> T0 for %s/%s", __func__, 1904189592Sbms inet_ntoa(inm->inm_addr), inm->inm_ifp->if_xname); 1905189592Sbms 1906189592Sbms /* 1907189592Sbms * If we are leaving the group for good, make sure 1908189592Sbms * we release IGMP's reference to it. 1909189592Sbms * This release must be deferred using a SLIST, 1910189592Sbms * as we are called from a loop which traverses 1911189592Sbms * the in_ifmultiaddr TAILQ. 1912189592Sbms */ 1913189592Sbms if (inm->inm_state == IGMP_LEAVING_MEMBER && 1914189592Sbms inm->inm_scrv == 0) { 1915189592Sbms inm->inm_state = IGMP_NOT_MEMBER; 1916189592Sbms SLIST_INSERT_HEAD(&igi->igi_relinmhead, 1917189592Sbms inm, inm_nrele); 1918189592Sbms } 1919189592Sbms } 1920189592Sbms break; 1921189592Sbms } 19221541Srgrimes} 19231541Srgrimes 1924189592Sbms 1925189592Sbms/* 1926189592Sbms * Suppress a group's pending response to a group or source/group query. 1927189592Sbms * 1928189592Sbms * Do NOT suppress state changes. This leads to IGMPv3 inconsistency. 1929189592Sbms * Do NOT update ST1/ST0 as this operation merely suppresses 1930189592Sbms * the currently pending group record. 1931189592Sbms * Do NOT suppress the response to a general query. It is possible but 1932189592Sbms * it would require adding another state or flag. 1933189592Sbms */ 1934189592Sbmsstatic void 1935189592Sbmsigmp_v3_suppress_group_record(struct in_multi *inm) 19361541Srgrimes{ 19371541Srgrimes 1938189592Sbms IN_MULTI_LOCK_ASSERT(); 1939189592Sbms 1940189592Sbms KASSERT(inm->inm_igi->igi_version == IGMP_VERSION_3, 1941189592Sbms ("%s: not IGMPv3 mode on link", __func__)); 1942189592Sbms 1943189592Sbms if (inm->inm_state != IGMP_G_QUERY_PENDING_MEMBER || 1944189592Sbms inm->inm_state != IGMP_SG_QUERY_PENDING_MEMBER) 1945189592Sbms return; 1946189592Sbms 1947189592Sbms if (inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER) 1948189592Sbms inm_clear_recorded(inm); 1949189592Sbms 1950189592Sbms inm->inm_timer = 0; 1951189592Sbms inm->inm_state = IGMP_REPORTING_MEMBER; 1952189592Sbms} 1953189592Sbms 1954189592Sbms/* 1955189592Sbms * Switch to a different IGMP version on the given interface, 1956189592Sbms * as per Section 7.2.1. 1957189592Sbms */ 1958189592Sbmsstatic void 1959189592Sbmsigmp_set_version(struct igmp_ifinfo *igi, const int version) 1960189592Sbms{ 1961189592Sbms 1962189592Sbms IGMP_LOCK_ASSERT(); 1963189592Sbms 1964189592Sbms CTR4(KTR_IGMPV3, "%s: switching to v%d on ifp %p(%s)", __func__, 1965189592Sbms version, igi->igi_ifp, igi->igi_ifp->if_xname); 1966189592Sbms 1967189592Sbms if (version == IGMP_VERSION_1 || version == IGMP_VERSION_2) { 1968189592Sbms int old_version_timer; 1969189592Sbms /* 1970189592Sbms * Compute the "Older Version Querier Present" timer as per 1971189592Sbms * Section 8.12. 1972189592Sbms */ 1973189592Sbms old_version_timer = igi->igi_rv * igi->igi_qi + igi->igi_qri; 1974189592Sbms old_version_timer *= PR_SLOWHZ; 1975189592Sbms 1976189592Sbms if (version == IGMP_VERSION_1) { 1977189592Sbms igi->igi_v1_timer = old_version_timer; 1978189592Sbms igi->igi_v2_timer = 0; 1979189592Sbms } else if (version == IGMP_VERSION_2) { 1980189592Sbms igi->igi_v1_timer = 0; 1981189592Sbms igi->igi_v2_timer = old_version_timer; 1982189592Sbms } 1983189592Sbms } 1984189592Sbms 1985189592Sbms if (igi->igi_v1_timer == 0 && igi->igi_v2_timer > 0) { 1986189592Sbms if (igi->igi_version != IGMP_VERSION_2) { 1987189592Sbms igi->igi_version = IGMP_VERSION_2; 1988189592Sbms igmp_v3_cancel_link_timers(igi); 1989189592Sbms } 1990189592Sbms } else if (igi->igi_v1_timer > 0) { 1991189592Sbms if (igi->igi_version != IGMP_VERSION_1) { 1992189592Sbms igi->igi_version = IGMP_VERSION_1; 1993189592Sbms igmp_v3_cancel_link_timers(igi); 1994189592Sbms } 1995189592Sbms } 1996189592Sbms} 1997189592Sbms 1998189592Sbms/* 1999189592Sbms * Cancel pending IGMPv3 timers for the given link and all groups 2000189592Sbms * joined on it; state-change, general-query, and group-query timers. 2001189592Sbms */ 2002189592Sbmsstatic void 2003189592Sbmsigmp_v3_cancel_link_timers(struct igmp_ifinfo *igi) 2004189592Sbms{ 2005189592Sbms struct ifmultiaddr *ifma; 2006189592Sbms struct ifnet *ifp; 2007189592Sbms struct in_multi *inm; 2008189592Sbms 2009189592Sbms CTR3(KTR_IGMPV3, "%s: cancel v3 timers on ifp %p(%s)", __func__, 2010189592Sbms igi->igi_ifp, igi->igi_ifp->if_xname); 2011189592Sbms 2012189592Sbms IN_MULTI_LOCK_ASSERT(); 2013189592Sbms IGMP_LOCK_ASSERT(); 2014189592Sbms 20151541Srgrimes /* 2016189592Sbms * Fast-track this potentially expensive operation 2017189592Sbms * by checking all the global 'timer pending' flags. 20181541Srgrimes */ 2019189592Sbms if (!V_interface_timers_running && 2020189592Sbms !V_state_change_timers_running && 2021189592Sbms !V_current_state_timers_running) 20221541Srgrimes return; 20231541Srgrimes 2024189592Sbms igi->igi_v3_timer = 0; 2025189592Sbms 2026189592Sbms ifp = igi->igi_ifp; 2027189592Sbms 2028189592Sbms IF_ADDR_LOCK(ifp); 2029189592Sbms TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2030189592Sbms if (ifma->ifma_addr->sa_family != AF_INET) 2031189592Sbms continue; 2032189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 2033189592Sbms switch (inm->inm_state) { 2034189592Sbms case IGMP_NOT_MEMBER: 2035189592Sbms case IGMP_SILENT_MEMBER: 2036189592Sbms case IGMP_IDLE_MEMBER: 2037189592Sbms case IGMP_LAZY_MEMBER: 2038189592Sbms case IGMP_SLEEPING_MEMBER: 2039189592Sbms case IGMP_AWAKENING_MEMBER: 2040189592Sbms break; 2041189592Sbms case IGMP_LEAVING_MEMBER: 2042189592Sbms /* 2043189592Sbms * If we are leaving the group and switching 2044189592Sbms * IGMP version, we need to release the final 2045189592Sbms * reference held for issuing the INCLUDE {}. 2046189592Sbms * 2047189592Sbms * SMPNG: Must drop and re-acquire IF_ADDR_LOCK 2048189592Sbms * around inm_release_locked(), as it is not 2049189592Sbms * a recursive mutex. 2050189592Sbms */ 2051189592Sbms IF_ADDR_UNLOCK(ifp); 2052189592Sbms inm_release_locked(inm); 2053189592Sbms IF_ADDR_LOCK(ifp); 2054189592Sbms /* FALLTHROUGH */ 2055189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 2056189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 2057189592Sbms inm_clear_recorded(inm); 2058189592Sbms /* FALLTHROUGH */ 2059189592Sbms case IGMP_REPORTING_MEMBER: 2060189592Sbms inm->inm_sctimer = 0; 2061189592Sbms inm->inm_timer = 0; 2062189592Sbms inm->inm_state = IGMP_REPORTING_MEMBER; 2063189592Sbms /* 2064189592Sbms * Free any pending IGMPv3 state-change records. 2065189592Sbms */ 2066189592Sbms _IF_DRAIN(&inm->inm_scq); 2067189592Sbms break; 2068189592Sbms } 2069189592Sbms } 2070189592Sbms IF_ADDR_UNLOCK(ifp); 2071189592Sbms} 2072189592Sbms 2073189592Sbms/* 2074189592Sbms * Update the Older Version Querier Present timers for a link. 2075189592Sbms * See Section 7.2.1 of RFC 3376. 2076189592Sbms */ 2077189592Sbmsstatic void 2078189592Sbmsigmp_v1v2_process_querier_timers(struct igmp_ifinfo *igi) 2079189592Sbms{ 2080189592Sbms 2081189592Sbms IGMP_LOCK_ASSERT(); 2082189592Sbms 2083189592Sbms if (igi->igi_v1_timer == 0 && igi->igi_v2_timer == 0) { 2084189592Sbms /* 2085189592Sbms * IGMPv1 and IGMPv2 Querier Present timers expired. 2086189592Sbms * 2087189592Sbms * Revert to IGMPv3. 2088189592Sbms */ 2089189592Sbms if (igi->igi_version != IGMP_VERSION_3) { 2090189592Sbms CTR5(KTR_IGMPV3, 2091189592Sbms "%s: transition from v%d -> v%d on %p(%s)", 2092189592Sbms __func__, igi->igi_version, IGMP_VERSION_3, 2093189592Sbms igi->igi_ifp, igi->igi_ifp->if_xname); 2094189592Sbms igi->igi_version = IGMP_VERSION_3; 2095189592Sbms } 2096189592Sbms } else if (igi->igi_v1_timer == 0 && igi->igi_v2_timer > 0) { 2097189592Sbms /* 2098189592Sbms * IGMPv1 Querier Present timer expired, 2099189592Sbms * IGMPv2 Querier Present timer running. 2100189592Sbms * If IGMPv2 was disabled since last timeout, 2101189592Sbms * revert to IGMPv3. 2102189592Sbms * If IGMPv2 is enabled, revert to IGMPv2. 2103189592Sbms */ 2104189592Sbms if (!V_igmp_v2enable) { 2105189592Sbms CTR5(KTR_IGMPV3, 2106189592Sbms "%s: transition from v%d -> v%d on %p(%s)", 2107189592Sbms __func__, igi->igi_version, IGMP_VERSION_3, 2108189592Sbms igi->igi_ifp, igi->igi_ifp->if_xname); 2109189592Sbms igi->igi_v2_timer = 0; 2110189592Sbms igi->igi_version = IGMP_VERSION_3; 2111189592Sbms } else { 2112189592Sbms --igi->igi_v2_timer; 2113189592Sbms if (igi->igi_version != IGMP_VERSION_2) { 2114189592Sbms CTR5(KTR_IGMPV3, 2115189592Sbms "%s: transition from v%d -> v%d on %p(%s)", 2116189592Sbms __func__, igi->igi_version, IGMP_VERSION_2, 2117189592Sbms igi->igi_ifp, igi->igi_ifp->if_xname); 2118189592Sbms igi->igi_version = IGMP_VERSION_2; 2119183550Szec } 21201541Srgrimes } 2121189592Sbms } else if (igi->igi_v1_timer > 0) { 2122189592Sbms /* 2123189592Sbms * IGMPv1 Querier Present timer running. 2124189592Sbms * Stop IGMPv2 timer if running. 2125189592Sbms * 2126189592Sbms * If IGMPv1 was disabled since last timeout, 2127189592Sbms * revert to IGMPv3. 2128189592Sbms * If IGMPv1 is enabled, reset IGMPv2 timer if running. 2129189592Sbms */ 2130189592Sbms if (!V_igmp_v1enable) { 2131189592Sbms CTR5(KTR_IGMPV3, 2132189592Sbms "%s: transition from v%d -> v%d on %p(%s)", 2133189592Sbms __func__, igi->igi_version, IGMP_VERSION_3, 2134189592Sbms igi->igi_ifp, igi->igi_ifp->if_xname); 2135189592Sbms igi->igi_v1_timer = 0; 2136189592Sbms igi->igi_version = IGMP_VERSION_3; 2137189592Sbms } else { 2138189592Sbms --igi->igi_v1_timer; 2139189592Sbms } 2140189592Sbms if (igi->igi_v2_timer > 0) { 2141189592Sbms CTR3(KTR_IGMPV3, 2142189592Sbms "%s: cancel v2 timer on %p(%s)", 2143189592Sbms __func__, igi->igi_ifp, igi->igi_ifp->if_xname); 2144189592Sbms igi->igi_v2_timer = 0; 2145189592Sbms } 21461541Srgrimes } 21471541Srgrimes} 21481541Srgrimes 2149189592Sbms/* 2150189592Sbms * Global slowtimo handler. 2151189592Sbms * VIMAGE: Timeout handlers are expected to service all vimages. 2152189592Sbms */ 21532531Swollmanvoid 2154119181Srwatsonigmp_slowtimo(void) 21552531Swollman{ 2156189592Sbms#ifdef VIMAGE 2157183550Szec VNET_ITERATOR_DECL(vnet_iter); 21582531Swollman 2159183550Szec VNET_LIST_RLOCK(); 2160183550Szec VNET_FOREACH(vnet_iter) { 2161183550Szec CURVNET_SET(vnet_iter); 2162183550Szec INIT_VNET_INET(vnet_iter); 2163189592Sbms igmp_slowtimo_vnet(); 2164183550Szec CURVNET_RESTORE(); 21652531Swollman } 2166183550Szec VNET_LIST_RUNLOCK(); 2167189592Sbms#else /* !VIMAGE */ 2168189592Sbms igmp_slowtimo_vnet(); 2169189592Sbms#endif /* VIMAGE */ 21702531Swollman} 21712531Swollman 2172189592Sbms/* 2173189592Sbms * Per-vnet slowtimo handler. 2174189592Sbms */ 21751541Srgrimesstatic void 2176189592Sbmsigmp_slowtimo_vnet(void) 21771541Srgrimes{ 2178189592Sbms struct igmp_ifinfo *igi; 21791541Srgrimes 2180189592Sbms IGMP_LOCK(); 2181189592Sbms 2182189592Sbms LIST_FOREACH(igi, &V_igi_head, igi_link) { 2183189592Sbms igmp_v1v2_process_querier_timers(igi); 2184189592Sbms } 2185189592Sbms 2186189592Sbms IGMP_UNLOCK(); 2187189592Sbms} 2188189592Sbms 2189189592Sbms/* 2190189592Sbms * Dispatch an IGMPv1/v2 host report or leave message. 2191189592Sbms * These are always small enough to fit inside a single mbuf. 2192189592Sbms */ 2193189592Sbmsstatic int 2194189592Sbmsigmp_v1v2_queue_report(struct in_multi *inm, const int type) 2195189592Sbms{ 2196189592Sbms struct ifnet *ifp; 2197189592Sbms struct igmp *igmp; 2198189592Sbms struct ip *ip; 2199189592Sbms struct mbuf *m; 2200189592Sbms 2201148682Srwatson IN_MULTI_LOCK_ASSERT(); 2202189592Sbms IGMP_LOCK_ASSERT(); 2203148682Srwatson 2204189592Sbms ifp = inm->inm_ifp; 2205189592Sbms /* XXX are these needed ? */ 2206189592Sbms INIT_VNET_NET(ifp->if_vnet); 2207189592Sbms INIT_VNET_INET(ifp->if_vnet); 2208189592Sbms 2209151967Sandre MGETHDR(m, M_DONTWAIT, MT_DATA); 2210119181Srwatson if (m == NULL) 2211189592Sbms return (ENOMEM); 2212189592Sbms MH_ALIGN(m, sizeof(struct ip) + sizeof(struct igmp)); 22132531Swollman 2214189592Sbms m->m_pkthdr.len = sizeof(struct ip) + sizeof(struct igmp); 2215189592Sbms 22162531Swollman m->m_data += sizeof(struct ip); 2217189592Sbms m->m_len = sizeof(struct igmp); 2218189592Sbms 2219119181Srwatson igmp = mtod(m, struct igmp *); 2220119181Srwatson igmp->igmp_type = type; 2221119181Srwatson igmp->igmp_code = 0; 2222119181Srwatson igmp->igmp_group = inm->inm_addr; 2223119181Srwatson igmp->igmp_cksum = 0; 2224189592Sbms igmp->igmp_cksum = in_cksum(m, sizeof(struct igmp)); 22251541Srgrimes 2226119181Srwatson m->m_data -= sizeof(struct ip); 2227119181Srwatson m->m_len += sizeof(struct ip); 2228189592Sbms 2229119181Srwatson ip = mtod(m, struct ip *); 2230119181Srwatson ip->ip_tos = 0; 2231189592Sbms ip->ip_len = sizeof(struct ip) + sizeof(struct igmp); 2232119181Srwatson ip->ip_off = 0; 2233119181Srwatson ip->ip_p = IPPROTO_IGMP; 2234119181Srwatson ip->ip_src.s_addr = INADDR_ANY; 22351541Srgrimes 2236189592Sbms if (type == IGMP_HOST_LEAVE_MESSAGE) 2237189592Sbms ip->ip_dst.s_addr = htonl(INADDR_ALLRTRS_GROUP); 2238189592Sbms else 2239189592Sbms ip->ip_dst = inm->inm_addr; 2240189592Sbms 2241189592Sbms igmp_save_context(m, ifp); 2242189592Sbms 2243189592Sbms m->m_flags |= M_IGMPV2; 2244189592Sbms if (inm->inm_igi->igi_flags & IGIF_LOOPBACK) 2245189592Sbms m->m_flags |= M_IGMP_LOOP; 2246189592Sbms 2247189592Sbms CTR2(KTR_IGMPV3, "%s: netisr_dispatch(NETISR_IGMP, %p)", __func__, m); 2248189592Sbms netisr_dispatch(NETISR_IGMP, m); 2249189592Sbms 2250189592Sbms return (0); 2251189592Sbms} 2252189592Sbms 2253189592Sbms/* 2254189592Sbms * Process a state change from the upper layer for the given IPv4 group. 2255189592Sbms * 2256189592Sbms * Each socket holds a reference on the in_multi in its own ip_moptions. 2257189592Sbms * The socket layer will have made the necessary updates to.the group 2258189592Sbms * state, it is now up to IGMP to issue a state change report if there 2259189592Sbms * has been any change between T0 (when the last state-change was issued) 2260189592Sbms * and T1 (now). 2261189592Sbms * 2262189592Sbms * We use the IGMPv3 state machine at group level. The IGMP module 2263189592Sbms * however makes the decision as to which IGMP protocol version to speak. 2264189592Sbms * A state change *from* INCLUDE {} always means an initial join. 2265189592Sbms * A state change *to* INCLUDE {} always means a final leave. 2266189592Sbms * 2267189592Sbms * FUTURE: If IGIF_V3LITE is enabled for this interface, then we can 2268189592Sbms * save ourselves a bunch of work; any exclusive mode groups need not 2269189592Sbms * compute source filter lists. 2270189592Sbms * 2271189592Sbms * VIMAGE: curvnet should have been set by caller, as this routine 2272189592Sbms * is called from the socket option handlers. 2273189592Sbms */ 2274189592Sbmsint 2275189592Sbmsigmp_change_state(struct in_multi *inm) 2276189592Sbms{ 2277189592Sbms struct igmp_ifinfo *igi; 2278189592Sbms struct ifnet *ifp; 2279189592Sbms int error; 2280189592Sbms 2281189592Sbms IN_MULTI_LOCK_ASSERT(); 2282189592Sbms 2283189592Sbms error = 0; 2284189592Sbms 2285189592Sbms /* 2286189592Sbms * Try to detect if the upper layer just asked us to change state 2287189592Sbms * for an interface which has now gone away. 2288189592Sbms */ 2289189592Sbms KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__)); 2290189592Sbms ifp = inm->inm_ifma->ifma_ifp; 2291189592Sbms if (ifp != NULL) { 2292189592Sbms /* 2293189592Sbms * Sanity check that netinet's notion of ifp is the 2294189592Sbms * same as net's. 2295189592Sbms */ 2296189592Sbms KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__)); 2297189592Sbms } 2298189592Sbms 2299189592Sbms IGMP_LOCK(); 2300189592Sbms 2301189592Sbms igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; 2302189592Sbms KASSERT(igi != NULL, ("%s: no igmp_ifinfo for ifp %p", __func__, ifp)); 2303189592Sbms 2304189592Sbms /* 2305189592Sbms * If we detect a state transition to or from MCAST_UNDEFINED 2306189592Sbms * for this group, then we are starting or finishing an IGMP 2307189592Sbms * life cycle for this group. 2308189592Sbms */ 2309189592Sbms if (inm->inm_st[1].iss_fmode != inm->inm_st[0].iss_fmode) { 2310189592Sbms CTR3(KTR_IGMPV3, "%s: inm transition %d -> %d", __func__, 2311189592Sbms inm->inm_st[0].iss_fmode, inm->inm_st[1].iss_fmode); 2312189592Sbms if (inm->inm_st[0].iss_fmode == MCAST_UNDEFINED) { 2313189592Sbms CTR1(KTR_IGMPV3, "%s: initial join", __func__); 2314189592Sbms error = igmp_initial_join(inm, igi); 2315189592Sbms goto out_locked; 2316189592Sbms } else if (inm->inm_st[1].iss_fmode == MCAST_UNDEFINED) { 2317189592Sbms CTR1(KTR_IGMPV3, "%s: final leave", __func__); 2318189592Sbms igmp_final_leave(inm, igi); 2319189592Sbms goto out_locked; 2320189592Sbms } 2321189592Sbms } else { 2322189592Sbms CTR1(KTR_IGMPV3, "%s: filter set change", __func__); 2323189592Sbms } 2324189592Sbms 2325189592Sbms error = igmp_handle_state_change(inm, igi); 2326189592Sbms 2327189592Sbmsout_locked: 2328189592Sbms IGMP_UNLOCK(); 2329189592Sbms return (error); 2330189592Sbms} 2331189592Sbms 2332189592Sbms/* 2333189592Sbms * Perform the initial join for an IGMP group. 2334189592Sbms * 2335189592Sbms * When joining a group: 2336189592Sbms * If the group should have its IGMP traffic suppressed, do nothing. 2337189592Sbms * IGMPv1 starts sending IGMPv1 host membership reports. 2338189592Sbms * IGMPv2 starts sending IGMPv2 host membership reports. 2339189592Sbms * IGMPv3 will schedule an IGMPv3 state-change report containing the 2340189592Sbms * initial state of the membership. 2341189592Sbms */ 2342189592Sbmsstatic int 2343189592Sbmsigmp_initial_join(struct in_multi *inm, struct igmp_ifinfo *igi) 2344189592Sbms{ 2345189592Sbms struct ifnet *ifp; 2346189592Sbms struct ifqueue *ifq; 2347189592Sbms int error, retval, syncstates; 2348189592Sbms 2349189592Sbms CTR4(KTR_IGMPV3, "%s: initial join %s on ifp %p(%s)", 2350189592Sbms __func__, inet_ntoa(inm->inm_addr), inm->inm_ifp, 2351189592Sbms inm->inm_ifp->if_xname); 2352189592Sbms 2353189592Sbms error = 0; 2354189592Sbms syncstates = 1; 2355189592Sbms 2356189592Sbms ifp = inm->inm_ifp; 2357189592Sbms 2358189592Sbms IN_MULTI_LOCK_ASSERT(); 2359189592Sbms IGMP_LOCK_ASSERT(); 2360189592Sbms 2361189592Sbms KASSERT(igi && igi->igi_ifp == ifp, ("%s: inconsistent ifp", __func__)); 2362189592Sbms 2363189592Sbms /* 2364189592Sbms * Groups joined on loopback or marked as 'not reported', 2365189592Sbms * e.g. 224.0.0.1, enter the IGMP_SILENT_MEMBER state and 2366189592Sbms * are never reported in any IGMP protocol exchanges. 2367189592Sbms * All other groups enter the appropriate IGMP state machine 2368189592Sbms * for the version in use on this link. 2369189592Sbms * A link marked as IGIF_SILENT causes IGMP to be completely 2370189592Sbms * disabled for the link. 2371189592Sbms */ 2372189592Sbms if ((ifp->if_flags & IFF_LOOPBACK) || 2373189592Sbms (igi->igi_flags & IGIF_SILENT) || 2374189592Sbms !igmp_isgroupreported(inm->inm_addr)) { 2375189592Sbms CTR1(KTR_IGMPV3, 2376189592Sbms"%s: not kicking state machine for silent group", __func__); 2377189592Sbms inm->inm_state = IGMP_SILENT_MEMBER; 2378189592Sbms inm->inm_timer = 0; 2379189592Sbms } else { 2380189592Sbms /* 2381189592Sbms * Deal with overlapping in_multi lifecycle. 2382189592Sbms * If this group was LEAVING, then make sure 2383189592Sbms * we drop the reference we picked up to keep the 2384189592Sbms * group around for the final INCLUDE {} enqueue. 2385189592Sbms */ 2386189592Sbms if (igi->igi_version == IGMP_VERSION_3 && 2387189592Sbms inm->inm_state == IGMP_LEAVING_MEMBER) 2388189592Sbms inm_release_locked(inm); 2389189592Sbms 2390189592Sbms inm->inm_state = IGMP_REPORTING_MEMBER; 2391189592Sbms 2392189592Sbms switch (igi->igi_version) { 2393189592Sbms case IGMP_VERSION_1: 2394189592Sbms case IGMP_VERSION_2: 2395189592Sbms inm->inm_state = IGMP_IDLE_MEMBER; 2396189592Sbms error = igmp_v1v2_queue_report(inm, 2397189592Sbms (igi->igi_version == IGMP_VERSION_2) ? 2398189592Sbms IGMP_v2_HOST_MEMBERSHIP_REPORT : 2399189592Sbms IGMP_v1_HOST_MEMBERSHIP_REPORT); 2400189592Sbms if (error == 0) { 2401189592Sbms inm->inm_timer = IGMP_RANDOM_DELAY( 2402189592Sbms IGMP_V1V2_MAX_RI * PR_FASTHZ); 2403189592Sbms V_current_state_timers_running = 1; 2404189592Sbms } 2405189592Sbms break; 2406189592Sbms 2407189592Sbms case IGMP_VERSION_3: 2408189592Sbms /* 2409189592Sbms * Defer update of T0 to T1, until the first copy 2410189592Sbms * of the state change has been transmitted. 2411189592Sbms */ 2412189592Sbms syncstates = 0; 2413189592Sbms 2414189592Sbms /* 2415189592Sbms * Immediately enqueue a State-Change Report for 2416189592Sbms * this interface, freeing any previous reports. 2417189592Sbms * Don't kick the timers if there is nothing to do, 2418189592Sbms * or if an error occurred. 2419189592Sbms */ 2420189592Sbms ifq = &inm->inm_scq; 2421189592Sbms _IF_DRAIN(ifq); 2422189592Sbms retval = igmp_v3_enqueue_group_record(ifq, inm, 1, 2423189592Sbms 0, 0); 2424189592Sbms CTR2(KTR_IGMPV3, "%s: enqueue record = %d", 2425189592Sbms __func__, retval); 2426189592Sbms if (retval <= 0) { 2427189592Sbms error = retval * -1; 2428189592Sbms break; 2429189592Sbms } 2430189592Sbms 2431189592Sbms /* 2432189592Sbms * Schedule transmission of pending state-change 2433189592Sbms * report up to RV times for this link. The timer 2434189592Sbms * will fire at the next igmp_fasttimo (~200ms), 2435189592Sbms * giving us an opportunity to merge the reports. 2436189592Sbms */ 2437189592Sbms if (igi->igi_flags & IGIF_LOOPBACK) { 2438189592Sbms inm->inm_scrv = 1; 2439189592Sbms } else { 2440189592Sbms KASSERT(igi->igi_rv > 1, 2441189592Sbms ("%s: invalid robustness %d", __func__, 2442189592Sbms igi->igi_rv)); 2443189592Sbms inm->inm_scrv = igi->igi_rv; 2444189592Sbms } 2445189592Sbms inm->inm_sctimer = 1; 2446189592Sbms V_state_change_timers_running = 1; 2447189592Sbms 2448189592Sbms error = 0; 2449189592Sbms break; 2450189592Sbms } 2451189592Sbms } 2452189592Sbms 2453189592Sbms /* 2454189592Sbms * Only update the T0 state if state change is atomic, 2455189592Sbms * i.e. we don't need to wait for a timer to fire before we 2456189592Sbms * can consider the state change to have been communicated. 2457189592Sbms */ 2458189592Sbms if (syncstates) { 2459189592Sbms inm_commit(inm); 2460189592Sbms CTR3(KTR_IGMPV3, "%s: T1 -> T0 for %s/%s", __func__, 2461189592Sbms inet_ntoa(inm->inm_addr), inm->inm_ifp->if_xname); 2462189592Sbms } 2463189592Sbms 2464189592Sbms return (error); 2465189592Sbms} 2466189592Sbms 2467189592Sbms/* 2468189592Sbms * Issue an intermediate state change during the IGMP life-cycle. 2469189592Sbms */ 2470189592Sbmsstatic int 2471189592Sbmsigmp_handle_state_change(struct in_multi *inm, struct igmp_ifinfo *igi) 2472189592Sbms{ 2473189592Sbms struct ifnet *ifp; 2474189592Sbms int retval; 2475189592Sbms 2476189592Sbms CTR4(KTR_IGMPV3, "%s: state change for %s on ifp %p(%s)", 2477189592Sbms __func__, inet_ntoa(inm->inm_addr), inm->inm_ifp, 2478189592Sbms inm->inm_ifp->if_xname); 2479189592Sbms 2480189592Sbms ifp = inm->inm_ifp; 2481189592Sbms 2482189592Sbms IN_MULTI_LOCK_ASSERT(); 2483189592Sbms IGMP_LOCK_ASSERT(); 2484189592Sbms 2485189592Sbms KASSERT(igi && igi->igi_ifp == ifp, ("%s: inconsistent ifp", __func__)); 2486189592Sbms 2487189592Sbms if ((ifp->if_flags & IFF_LOOPBACK) || 2488189592Sbms (igi->igi_flags & IGIF_SILENT) || 2489189592Sbms !igmp_isgroupreported(inm->inm_addr) || 2490189592Sbms (igi->igi_version != IGMP_VERSION_3)) { 2491189592Sbms if (!igmp_isgroupreported(inm->inm_addr)) { 2492189592Sbms CTR1(KTR_IGMPV3, 2493189592Sbms"%s: not kicking state machine for silent group", __func__); 2494189592Sbms } 2495189592Sbms CTR1(KTR_IGMPV3, "%s: nothing to do", __func__); 2496189592Sbms inm_commit(inm); 2497189592Sbms CTR3(KTR_IGMPV3, "%s: T1 -> T0 for %s/%s", __func__, 2498189592Sbms inet_ntoa(inm->inm_addr), inm->inm_ifp->if_xname); 2499189592Sbms return (0); 2500189592Sbms } 2501189592Sbms 2502189592Sbms _IF_DRAIN(&inm->inm_scq); 2503189592Sbms 2504189592Sbms retval = igmp_v3_enqueue_group_record(&inm->inm_scq, inm, 1, 0, 0); 2505189592Sbms CTR2(KTR_IGMPV3, "%s: enqueue record = %d", __func__, retval); 2506189592Sbms if (retval <= 0) 2507189592Sbms return (-retval); 2508189592Sbms 2509189592Sbms /* 2510189592Sbms * If record(s) were enqueued, start the state-change 2511189592Sbms * report timer for this group. 2512189592Sbms */ 2513189592Sbms inm->inm_scrv = ((igi->igi_flags & IGIF_LOOPBACK) ? 1 : igi->igi_rv); 2514189592Sbms inm->inm_sctimer = 1; 2515189592Sbms V_state_change_timers_running = 1; 2516189592Sbms 2517189592Sbms return (0); 2518189592Sbms} 2519189592Sbms 2520189592Sbms/* 2521189592Sbms * Perform the final leave for an IGMP group. 2522189592Sbms * 2523189592Sbms * When leaving a group: 2524189592Sbms * IGMPv1 does nothing. 2525189592Sbms * IGMPv2 sends a host leave message, if and only if we are the reporter. 2526189592Sbms * IGMPv3 enqueues a state-change report containing a transition 2527189592Sbms * to INCLUDE {} for immediate transmission. 2528189592Sbms */ 2529189592Sbmsstatic void 2530189592Sbmsigmp_final_leave(struct in_multi *inm, struct igmp_ifinfo *igi) 2531189592Sbms{ 2532189592Sbms int syncstates; 2533189592Sbms 2534189592Sbms syncstates = 1; 2535189592Sbms 2536189592Sbms CTR4(KTR_IGMPV3, "%s: final leave %s on ifp %p(%s)", 2537189592Sbms __func__, inet_ntoa(inm->inm_addr), inm->inm_ifp, 2538189592Sbms inm->inm_ifp->if_xname); 2539189592Sbms 2540189592Sbms IN_MULTI_LOCK_ASSERT(); 2541189592Sbms IGMP_LOCK_ASSERT(); 2542189592Sbms 2543189592Sbms switch (inm->inm_state) { 2544189592Sbms case IGMP_NOT_MEMBER: 2545189592Sbms case IGMP_SILENT_MEMBER: 2546189592Sbms case IGMP_LEAVING_MEMBER: 2547189592Sbms /* Already leaving or left; do nothing. */ 2548189592Sbms CTR1(KTR_IGMPV3, 2549189592Sbms"%s: not kicking state machine for silent group", __func__); 2550189592Sbms break; 2551189592Sbms case IGMP_REPORTING_MEMBER: 2552189592Sbms case IGMP_IDLE_MEMBER: 2553189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 2554189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 2555189592Sbms if (igi->igi_version == IGMP_VERSION_2) { 2556189592Sbms#ifdef INVARIANTS 2557189592Sbms if (inm->inm_state == IGMP_G_QUERY_PENDING_MEMBER || 2558189592Sbms inm->inm_state == IGMP_SG_QUERY_PENDING_MEMBER) 2559189592Sbms panic("%s: IGMPv3 state reached, not IGMPv3 mode", 2560189592Sbms __func__); 2561189592Sbms#endif 2562189592Sbms igmp_v1v2_queue_report(inm, IGMP_HOST_LEAVE_MESSAGE); 2563189592Sbms inm->inm_state = IGMP_NOT_MEMBER; 2564189592Sbms } else if (igi->igi_version == IGMP_VERSION_3) { 2565189592Sbms /* 2566189592Sbms * Stop group timer and all pending reports. 2567189592Sbms * Immediately enqueue a state-change report 2568189592Sbms * TO_IN {} to be sent on the next fast timeout, 2569189592Sbms * giving us an opportunity to merge reports. 2570189592Sbms */ 2571189592Sbms _IF_DRAIN(&inm->inm_scq); 2572189592Sbms inm->inm_timer = 0; 2573189592Sbms if (igi->igi_flags & IGIF_LOOPBACK) { 2574189592Sbms inm->inm_scrv = 1; 2575189592Sbms } else { 2576189592Sbms inm->inm_scrv = igi->igi_rv; 2577189592Sbms } 2578189592Sbms CTR4(KTR_IGMPV3, "%s: Leaving %s/%s with %d " 2579189592Sbms "pending retransmissions.", __func__, 2580189592Sbms inet_ntoa(inm->inm_addr), 2581189592Sbms inm->inm_ifp->if_xname, inm->inm_scrv); 2582189592Sbms if (inm->inm_scrv == 0) { 2583189592Sbms inm->inm_state = IGMP_NOT_MEMBER; 2584189592Sbms inm->inm_sctimer = 0; 2585189592Sbms } else { 2586189592Sbms int retval; 2587189592Sbms 2588189592Sbms inm_acquire_locked(inm); 2589189592Sbms 2590189592Sbms retval = igmp_v3_enqueue_group_record( 2591189592Sbms &inm->inm_scq, inm, 1, 0, 0); 2592189592Sbms KASSERT(retval != 0, 2593189592Sbms ("%s: enqueue record = %d", __func__, 2594189592Sbms retval)); 2595189592Sbms 2596189592Sbms inm->inm_state = IGMP_LEAVING_MEMBER; 2597189592Sbms inm->inm_sctimer = 1; 2598189592Sbms V_state_change_timers_running = 1; 2599189592Sbms syncstates = 0; 2600189592Sbms } 2601189592Sbms break; 2602189592Sbms } 2603189592Sbms break; 2604189592Sbms case IGMP_LAZY_MEMBER: 2605189592Sbms case IGMP_SLEEPING_MEMBER: 2606189592Sbms case IGMP_AWAKENING_MEMBER: 2607189592Sbms /* Our reports are suppressed; do nothing. */ 2608189592Sbms break; 2609189592Sbms } 2610189592Sbms 2611189592Sbms if (syncstates) { 2612189592Sbms inm_commit(inm); 2613189592Sbms CTR3(KTR_IGMPV3, "%s: T1 -> T0 for %s/%s", __func__, 2614189592Sbms inet_ntoa(inm->inm_addr), inm->inm_ifp->if_xname); 2615189592Sbms inm->inm_st[1].iss_fmode = MCAST_UNDEFINED; 2616189592Sbms CTR3(KTR_IGMPV3, "%s: T1 now MCAST_UNDEFINED for %s/%s", 2617189592Sbms __func__, inet_ntoa(inm->inm_addr), inm->inm_ifp->if_xname); 2618189592Sbms } 2619189592Sbms} 2620189592Sbms 2621189592Sbms/* 2622189592Sbms * Enqueue an IGMPv3 group record to the given output queue. 2623189592Sbms * 2624189592Sbms * XXX This function could do with having the allocation code 2625189592Sbms * split out, and the multiple-tree-walks coalesced into a single 2626189592Sbms * routine as has been done in igmp_v3_enqueue_filter_change(). 2627189592Sbms * 2628189592Sbms * If is_state_change is zero, a current-state record is appended. 2629189592Sbms * If is_state_change is non-zero, a state-change report is appended. 2630189592Sbms * 2631189592Sbms * If is_group_query is non-zero, an mbuf packet chain is allocated. 2632189592Sbms * If is_group_query is zero, and if there is a packet with free space 2633189592Sbms * at the tail of the queue, it will be appended to providing there 2634189592Sbms * is enough free space. 2635189592Sbms * Otherwise a new mbuf packet chain is allocated. 2636189592Sbms * 2637189592Sbms * If is_source_query is non-zero, each source is checked to see if 2638189592Sbms * it was recorded for a Group-Source query, and will be omitted if 2639189592Sbms * it is not both in-mode and recorded. 2640189592Sbms * 2641189592Sbms * The function will attempt to allocate leading space in the packet 2642189592Sbms * for the IP/IGMP header to be prepended without fragmenting the chain. 2643189592Sbms * 2644189592Sbms * If successful the size of all data appended to the queue is returned, 2645189592Sbms * otherwise an error code less than zero is returned, or zero if 2646189592Sbms * no record(s) were appended. 2647189592Sbms */ 2648189592Sbmsstatic int 2649189592Sbmsigmp_v3_enqueue_group_record(struct ifqueue *ifq, struct in_multi *inm, 2650189592Sbms const int is_state_change, const int is_group_query, 2651189592Sbms const int is_source_query) 2652189592Sbms{ 2653189592Sbms struct igmp_grouprec ig; 2654189592Sbms struct igmp_grouprec *pig; 2655189592Sbms struct ifnet *ifp; 2656189592Sbms struct ip_msource *ims, *nims; 2657189592Sbms struct mbuf *m0, *m, *md; 2658189592Sbms int error, is_filter_list_change; 2659189592Sbms int minrec0len, m0srcs, msrcs, nbytes, off; 2660189592Sbms int record_has_sources; 2661189592Sbms int now; 2662189592Sbms int type; 2663189592Sbms in_addr_t naddr; 2664189592Sbms uint8_t mode; 2665189592Sbms 2666189592Sbms IN_MULTI_LOCK_ASSERT(); 2667189592Sbms 2668189592Sbms error = 0; 2669189592Sbms ifp = inm->inm_ifp; 2670189592Sbms is_filter_list_change = 0; 2671189592Sbms m = NULL; 2672189592Sbms m0 = NULL; 2673189592Sbms m0srcs = 0; 2674189592Sbms msrcs = 0; 2675189592Sbms nbytes = 0; 2676189592Sbms nims = NULL; 2677189592Sbms record_has_sources = 1; 2678189592Sbms pig = NULL; 2679189592Sbms type = IGMP_DO_NOTHING; 2680189592Sbms mode = inm->inm_st[1].iss_fmode; 2681189592Sbms 2682189592Sbms /* 2683189592Sbms * If we did not transition out of ASM mode during t0->t1, 2684189592Sbms * and there are no source nodes to process, we can skip 2685189592Sbms * the generation of source records. 2686189592Sbms */ 2687189592Sbms if (inm->inm_st[0].iss_asm > 0 && inm->inm_st[1].iss_asm > 0 && 2688189592Sbms inm->inm_nsrc == 0) 2689189592Sbms record_has_sources = 0; 2690189592Sbms 2691189592Sbms if (is_state_change) { 2692189592Sbms /* 2693189592Sbms * Queue a state change record. 2694189592Sbms * If the mode did not change, and there are non-ASM 2695189592Sbms * listeners or source filters present, 2696189592Sbms * we potentially need to issue two records for the group. 2697189592Sbms * If we are transitioning to MCAST_UNDEFINED, we need 2698189592Sbms * not send any sources. 2699189592Sbms * If there are ASM listeners, and there was no filter 2700189592Sbms * mode transition of any kind, do nothing. 2701189592Sbms */ 2702189592Sbms if (mode != inm->inm_st[0].iss_fmode) { 2703189592Sbms if (mode == MCAST_EXCLUDE) { 2704189592Sbms CTR1(KTR_IGMPV3, "%s: change to EXCLUDE", 2705189592Sbms __func__); 2706189592Sbms type = IGMP_CHANGE_TO_EXCLUDE_MODE; 2707189592Sbms } else { 2708189592Sbms CTR1(KTR_IGMPV3, "%s: change to INCLUDE", 2709189592Sbms __func__); 2710189592Sbms type = IGMP_CHANGE_TO_INCLUDE_MODE; 2711189592Sbms if (mode == MCAST_UNDEFINED) 2712189592Sbms record_has_sources = 0; 2713189592Sbms } 2714189592Sbms } else { 2715189592Sbms if (record_has_sources) { 2716189592Sbms is_filter_list_change = 1; 2717189592Sbms } else { 2718189592Sbms type = IGMP_DO_NOTHING; 2719189592Sbms } 2720189592Sbms } 2721189592Sbms } else { 2722189592Sbms /* 2723189592Sbms * Queue a current state record. 2724189592Sbms */ 2725189592Sbms if (mode == MCAST_EXCLUDE) { 2726189592Sbms type = IGMP_MODE_IS_EXCLUDE; 2727189592Sbms } else if (mode == MCAST_INCLUDE) { 2728189592Sbms type = IGMP_MODE_IS_INCLUDE; 2729189592Sbms KASSERT(inm->inm_st[1].iss_asm == 0, 2730189592Sbms ("%s: inm %p is INCLUDE but ASM count is %d", 2731189592Sbms __func__, inm, inm->inm_st[1].iss_asm)); 2732189592Sbms } 2733189592Sbms } 2734189592Sbms 2735189592Sbms /* 2736189592Sbms * Generate the filter list changes using a separate function. 2737189592Sbms */ 2738189592Sbms if (is_filter_list_change) 2739189592Sbms return (igmp_v3_enqueue_filter_change(ifq, inm)); 2740189592Sbms 2741189592Sbms if (type == IGMP_DO_NOTHING) { 2742189592Sbms CTR3(KTR_IGMPV3, "%s: nothing to do for %s/%s", 2743189592Sbms __func__, inet_ntoa(inm->inm_addr), 2744189592Sbms inm->inm_ifp->if_xname); 2745189592Sbms return (0); 2746189592Sbms } 2747189592Sbms 2748189592Sbms /* 2749189592Sbms * If any sources are present, we must be able to fit at least 2750189592Sbms * one in the trailing space of the tail packet's mbuf, 2751189592Sbms * ideally more. 2752189592Sbms */ 2753189592Sbms minrec0len = sizeof(struct igmp_grouprec); 2754189592Sbms if (record_has_sources) 2755189592Sbms minrec0len += sizeof(in_addr_t); 2756189592Sbms 2757189592Sbms CTR4(KTR_IGMPV3, "%s: queueing %s for %s/%s", __func__, 2758189592Sbms igmp_rec_type_to_str(type), inet_ntoa(inm->inm_addr), 2759189592Sbms inm->inm_ifp->if_xname); 2760189592Sbms 2761189592Sbms /* 2762189592Sbms * Check if we have a packet in the tail of the queue for this 2763189592Sbms * group into which the first group record for this group will fit. 2764189592Sbms * Otherwise allocate a new packet. 2765189592Sbms * Always allocate leading space for IP+RA_OPT+IGMP+REPORT. 2766189592Sbms * Note: Group records for G/GSR query responses MUST be sent 2767189592Sbms * in their own packet. 2768189592Sbms */ 2769189592Sbms m0 = ifq->ifq_tail; 2770189592Sbms if (!is_group_query && 2771189592Sbms m0 != NULL && 2772189592Sbms (m0->m_pkthdr.PH_vt.vt_nrecs + 1 <= IGMP_V3_REPORT_MAXRECS) && 2773189592Sbms (m0->m_pkthdr.len + minrec0len) < 2774189592Sbms (ifp->if_mtu - IGMP_LEADINGSPACE)) { 2775189592Sbms m0srcs = (ifp->if_mtu - m0->m_pkthdr.len - 2776189592Sbms sizeof(struct igmp_grouprec)) / sizeof(in_addr_t); 2777189592Sbms m = m0; 2778189592Sbms CTR1(KTR_IGMPV3, "%s: use existing packet", __func__); 2779189592Sbms } else { 2780189592Sbms if (_IF_QFULL(ifq)) { 2781189592Sbms CTR1(KTR_IGMPV3, "%s: outbound queue full", __func__); 2782189592Sbms return (-ENOMEM); 2783189592Sbms } 2784189592Sbms m = NULL; 2785189592Sbms m0srcs = (ifp->if_mtu - IGMP_LEADINGSPACE - 2786189592Sbms sizeof(struct igmp_grouprec)) / sizeof(in_addr_t); 2787189592Sbms if (!is_state_change && !is_group_query) 2788189592Sbms m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 2789189592Sbms if (m == NULL) { 2790189592Sbms m = m_gethdr(M_DONTWAIT, MT_DATA); 2791189592Sbms if (m) 2792189592Sbms MH_ALIGN(m, IGMP_LEADINGSPACE); 2793189592Sbms } 2794189592Sbms if (m == NULL) 2795189592Sbms return (-ENOMEM); 2796189592Sbms m->m_data += IGMP_LEADINGSPACE; 2797189592Sbms 2798189592Sbms igmp_save_context(m, ifp); 2799189592Sbms 2800189592Sbms CTR1(KTR_IGMPV3, "%s: allocated first packet", __func__); 2801189592Sbms } 2802189592Sbms 2803189592Sbms /* 2804189592Sbms * Append group record. 2805189592Sbms * If we have sources, we don't know how many yet. 2806189592Sbms */ 2807189592Sbms ig.ig_type = type; 2808189592Sbms ig.ig_datalen = 0; 2809189592Sbms ig.ig_numsrc = 0; 2810189592Sbms ig.ig_group = inm->inm_addr; 2811189592Sbms if (!m_append(m, sizeof(struct igmp_grouprec), (void *)&ig)) { 2812189592Sbms if (m != m0) 2813189592Sbms m_freem(m); 2814189592Sbms CTR1(KTR_IGMPV3, "%s: m_append() failed.", __func__); 2815189592Sbms return (-ENOMEM); 2816189592Sbms } 2817189592Sbms nbytes += sizeof(struct igmp_grouprec); 2818189592Sbms 2819189592Sbms /* 2820189592Sbms * Append as many sources as will fit in the first packet. 2821189592Sbms * If we are appending to a new packet, the chain allocation 2822189592Sbms * may potentially use clusters; use m_getptr() in this case. 2823189592Sbms * If we are appending to an existing packet, we need to obtain 2824189592Sbms * a pointer to the group record after m_append(), in case a new 2825189592Sbms * mbuf was allocated. 2826189592Sbms * Only append sources which are in-mode at t1. If we are 2827189592Sbms * transitioning to MCAST_UNDEFINED state on the group, do not 2828189592Sbms * include source entries. 2829189592Sbms * Only report recorded sources in our filter set when responding 2830189592Sbms * to a group-source query. 2831189592Sbms */ 2832189592Sbms if (record_has_sources) { 2833189592Sbms if (m == m0) { 2834189592Sbms md = m_last(m); 2835189592Sbms pig = (struct igmp_grouprec *)(mtod(md, uint8_t *) + 2836189592Sbms md->m_len - nbytes); 2837189592Sbms } else { 2838189592Sbms md = m_getptr(m, 0, &off); 2839189592Sbms pig = (struct igmp_grouprec *)(mtod(md, uint8_t *) + 2840189592Sbms off); 2841189592Sbms } 2842189592Sbms msrcs = 0; 2843189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, nims) { 2844189592Sbms CTR2(KTR_IGMPV3, "%s: visit node %s", __func__, 2845189592Sbms inet_ntoa_haddr(ims->ims_haddr)); 2846189592Sbms now = ims_get_mode(inm, ims, 1); 2847189592Sbms CTR2(KTR_IGMPV3, "%s: node is %d", __func__, now); 2848189592Sbms if ((now != mode) || 2849189592Sbms (now == mode && mode == MCAST_UNDEFINED)) { 2850189592Sbms CTR1(KTR_IGMPV3, "%s: skip node", __func__); 2851189592Sbms continue; 2852189592Sbms } 2853189592Sbms if (is_source_query && ims->ims_stp == 0) { 2854189592Sbms CTR1(KTR_IGMPV3, "%s: skip unrecorded node", 2855189592Sbms __func__); 2856189592Sbms continue; 2857189592Sbms } 2858189592Sbms CTR1(KTR_IGMPV3, "%s: append node", __func__); 2859189592Sbms naddr = htonl(ims->ims_haddr); 2860189592Sbms if (!m_append(m, sizeof(in_addr_t), (void *)&naddr)) { 2861189592Sbms if (m != m0) 2862189592Sbms m_freem(m); 2863189592Sbms CTR1(KTR_IGMPV3, "%s: m_append() failed.", 2864189592Sbms __func__); 2865189592Sbms return (-ENOMEM); 2866189592Sbms } 2867189592Sbms nbytes += sizeof(in_addr_t); 2868189592Sbms ++msrcs; 2869189592Sbms if (msrcs == m0srcs) 2870189592Sbms break; 2871189592Sbms } 2872189592Sbms CTR2(KTR_IGMPV3, "%s: msrcs is %d this packet", __func__, 2873189592Sbms msrcs); 2874189592Sbms pig->ig_numsrc = htons(msrcs); 2875189592Sbms nbytes += (msrcs * sizeof(in_addr_t)); 2876189592Sbms } 2877189592Sbms 2878189592Sbms if (is_source_query && msrcs == 0) { 2879189592Sbms CTR1(KTR_IGMPV3, "%s: no recorded sources to report", __func__); 2880189592Sbms if (m != m0) 2881189592Sbms m_freem(m); 2882189592Sbms return (0); 2883189592Sbms } 2884189592Sbms 2885189592Sbms /* 2886189592Sbms * We are good to go with first packet. 2887189592Sbms */ 2888189592Sbms if (m != m0) { 2889189592Sbms CTR1(KTR_IGMPV3, "%s: enqueueing first packet", __func__); 2890189592Sbms m->m_pkthdr.PH_vt.vt_nrecs = 1; 2891189592Sbms _IF_ENQUEUE(ifq, m); 2892189592Sbms } else 2893189592Sbms m->m_pkthdr.PH_vt.vt_nrecs++; 2894189592Sbms 2895189592Sbms /* 2896189592Sbms * No further work needed if no source list in packet(s). 2897189592Sbms */ 2898189592Sbms if (!record_has_sources) 2899189592Sbms return (nbytes); 2900189592Sbms 2901189592Sbms /* 2902189592Sbms * Whilst sources remain to be announced, we need to allocate 2903189592Sbms * a new packet and fill out as many sources as will fit. 2904189592Sbms * Always try for a cluster first. 2905189592Sbms */ 2906189592Sbms while (nims != NULL) { 2907189592Sbms if (_IF_QFULL(ifq)) { 2908189592Sbms CTR1(KTR_IGMPV3, "%s: outbound queue full", __func__); 2909189592Sbms return (-ENOMEM); 2910189592Sbms } 2911189592Sbms m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 2912189592Sbms if (m == NULL) { 2913189592Sbms m = m_gethdr(M_DONTWAIT, MT_DATA); 2914189592Sbms if (m) 2915189592Sbms MH_ALIGN(m, IGMP_LEADINGSPACE); 2916189592Sbms } 2917189592Sbms if (m == NULL) 2918189592Sbms return (-ENOMEM); 2919189592Sbms igmp_save_context(m, ifp); 2920189592Sbms m->m_data += IGMP_LEADINGSPACE; 2921189592Sbms md = m_getptr(m, 0, &off); 2922189592Sbms pig = (struct igmp_grouprec *)(mtod(md, uint8_t *) + off); 2923189592Sbms CTR1(KTR_IGMPV3, "%s: allocated next packet", __func__); 2924189592Sbms 2925189592Sbms if (!m_append(m, sizeof(struct igmp_grouprec), (void *)&ig)) { 2926189592Sbms if (m != m0) 2927189592Sbms m_freem(m); 2928189592Sbms CTR1(KTR_IGMPV3, "%s: m_append() failed.", __func__); 2929189592Sbms return (-ENOMEM); 2930189592Sbms } 2931189592Sbms m->m_pkthdr.PH_vt.vt_nrecs = 1; 2932189592Sbms nbytes += sizeof(struct igmp_grouprec); 2933189592Sbms 2934189592Sbms m0srcs = (ifp->if_mtu - IGMP_LEADINGSPACE - 2935189592Sbms sizeof(struct igmp_grouprec)) / sizeof(in_addr_t); 2936189592Sbms 2937189592Sbms msrcs = 0; 2938189592Sbms RB_FOREACH_FROM(ims, ip_msource_tree, nims) { 2939189592Sbms CTR2(KTR_IGMPV3, "%s: visit node %s", __func__, 2940189592Sbms inet_ntoa_haddr(ims->ims_haddr)); 2941189592Sbms now = ims_get_mode(inm, ims, 1); 2942189592Sbms if ((now != mode) || 2943189592Sbms (now == mode && mode == MCAST_UNDEFINED)) { 2944189592Sbms CTR1(KTR_IGMPV3, "%s: skip node", __func__); 2945189592Sbms continue; 2946189592Sbms } 2947189592Sbms if (is_source_query && ims->ims_stp == 0) { 2948189592Sbms CTR1(KTR_IGMPV3, "%s: skip unrecorded node", 2949189592Sbms __func__); 2950189592Sbms continue; 2951189592Sbms } 2952189592Sbms CTR1(KTR_IGMPV3, "%s: append node", __func__); 2953189592Sbms naddr = htonl(ims->ims_haddr); 2954189592Sbms if (!m_append(m, sizeof(in_addr_t), (void *)&naddr)) { 2955189592Sbms if (m != m0) 2956189592Sbms m_freem(m); 2957189592Sbms CTR1(KTR_IGMPV3, "%s: m_append() failed.", 2958189592Sbms __func__); 2959189592Sbms return (-ENOMEM); 2960189592Sbms } 2961189592Sbms ++msrcs; 2962189592Sbms if (msrcs == m0srcs) 2963189592Sbms break; 2964189592Sbms } 2965189592Sbms pig->ig_numsrc = htons(msrcs); 2966189592Sbms nbytes += (msrcs * sizeof(in_addr_t)); 2967189592Sbms 2968189592Sbms CTR1(KTR_IGMPV3, "%s: enqueueing next packet", __func__); 2969189592Sbms _IF_ENQUEUE(ifq, m); 2970189592Sbms } 2971189592Sbms 2972189592Sbms return (nbytes); 2973189592Sbms} 2974189592Sbms 2975189592Sbms/* 2976189592Sbms * Type used to mark record pass completion. 2977189592Sbms * We exploit the fact we can cast to this easily from the 2978189592Sbms * current filter modes on each ip_msource node. 2979189592Sbms */ 2980189592Sbmstypedef enum { 2981189592Sbms REC_NONE = 0x00, /* MCAST_UNDEFINED */ 2982189592Sbms REC_ALLOW = 0x01, /* MCAST_INCLUDE */ 2983189592Sbms REC_BLOCK = 0x02, /* MCAST_EXCLUDE */ 2984189592Sbms REC_FULL = REC_ALLOW | REC_BLOCK 2985189592Sbms} rectype_t; 2986189592Sbms 2987189592Sbms/* 2988189592Sbms * Enqueue an IGMPv3 filter list change to the given output queue. 2989189592Sbms * 2990189592Sbms * Source list filter state is held in an RB-tree. When the filter list 2991189592Sbms * for a group is changed without changing its mode, we need to compute 2992189592Sbms * the deltas between T0 and T1 for each source in the filter set, 2993189592Sbms * and enqueue the appropriate ALLOW_NEW/BLOCK_OLD records. 2994189592Sbms * 2995189592Sbms * As we may potentially queue two record types, and the entire R-B tree 2996189592Sbms * needs to be walked at once, we break this out into its own function 2997189592Sbms * so we can generate a tightly packed queue of packets. 2998189592Sbms * 2999189592Sbms * XXX This could be written to only use one tree walk, although that makes 3000189592Sbms * serializing into the mbuf chains a bit harder. For now we do two walks 3001189592Sbms * which makes things easier on us, and it may or may not be harder on 3002189592Sbms * the L2 cache. 3003189592Sbms * 3004189592Sbms * If successful the size of all data appended to the queue is returned, 3005189592Sbms * otherwise an error code less than zero is returned, or zero if 3006189592Sbms * no record(s) were appended. 3007189592Sbms */ 3008189592Sbmsstatic int 3009189592Sbmsigmp_v3_enqueue_filter_change(struct ifqueue *ifq, struct in_multi *inm) 3010189592Sbms{ 3011189592Sbms static const int MINRECLEN = 3012189592Sbms sizeof(struct igmp_grouprec) + sizeof(in_addr_t); 3013189592Sbms struct ifnet *ifp; 3014189592Sbms struct igmp_grouprec ig; 3015189592Sbms struct igmp_grouprec *pig; 3016189592Sbms struct ip_msource *ims, *nims; 3017189592Sbms struct mbuf *m, *m0, *md; 3018189592Sbms in_addr_t naddr; 3019189592Sbms int m0srcs, nbytes, off, rsrcs, schanged; 3020189592Sbms int nallow, nblock; 3021189592Sbms uint8_t mode, now, then; 3022189592Sbms rectype_t crt, drt, nrt; 3023189592Sbms 3024189592Sbms IN_MULTI_LOCK_ASSERT(); 3025189592Sbms 3026189592Sbms if (inm->inm_nsrc == 0 || 3027189592Sbms (inm->inm_st[0].iss_asm > 0 && inm->inm_st[1].iss_asm > 0)) 3028189592Sbms return (0); 3029189592Sbms 3030189592Sbms ifp = inm->inm_ifp; /* interface */ 3031189592Sbms mode = inm->inm_st[1].iss_fmode; /* filter mode at t1 */ 3032189592Sbms crt = REC_NONE; /* current group record type */ 3033189592Sbms drt = REC_NONE; /* mask of completed group record types */ 3034189592Sbms nrt = REC_NONE; /* record type for current node */ 3035189592Sbms m0srcs = 0; /* # source which will fit in current mbuf chain */ 3036189592Sbms nbytes = 0; /* # of bytes appended to group's state-change queue */ 3037189592Sbms rsrcs = 0; /* # sources encoded in current record */ 3038189592Sbms schanged = 0; /* # nodes encoded in overall filter change */ 3039189592Sbms nallow = 0; /* # of source entries in ALLOW_NEW */ 3040189592Sbms nblock = 0; /* # of source entries in BLOCK_OLD */ 3041189592Sbms nims = NULL; /* next tree node pointer */ 3042189592Sbms 3043189592Sbms /* 3044189592Sbms * For each possible filter record mode. 3045189592Sbms * The first kind of source we encounter tells us which 3046189592Sbms * is the first kind of record we start appending. 3047189592Sbms * If a node transitioned to UNDEFINED at t1, its mode is treated 3048189592Sbms * as the inverse of the group's filter mode. 3049189592Sbms */ 3050189592Sbms while (drt != REC_FULL) { 3051189592Sbms do { 3052189592Sbms m0 = ifq->ifq_tail; 3053189592Sbms if (m0 != NULL && 3054189592Sbms (m0->m_pkthdr.PH_vt.vt_nrecs + 1 <= 3055189592Sbms IGMP_V3_REPORT_MAXRECS) && 3056189592Sbms (m0->m_pkthdr.len + MINRECLEN) < 3057189592Sbms (ifp->if_mtu - IGMP_LEADINGSPACE)) { 3058189592Sbms m = m0; 3059189592Sbms m0srcs = (ifp->if_mtu - m0->m_pkthdr.len - 3060189592Sbms sizeof(struct igmp_grouprec)) / 3061189592Sbms sizeof(in_addr_t); 3062189592Sbms CTR1(KTR_IGMPV3, 3063189592Sbms "%s: use previous packet", __func__); 3064189592Sbms } else { 3065189592Sbms m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 3066189592Sbms if (m == NULL) { 3067189592Sbms m = m_gethdr(M_DONTWAIT, MT_DATA); 3068189592Sbms if (m) 3069189592Sbms MH_ALIGN(m, IGMP_LEADINGSPACE); 3070189592Sbms } 3071189592Sbms if (m == NULL) { 3072189592Sbms CTR1(KTR_IGMPV3, 3073189592Sbms "%s: m_get*() failed", __func__); 3074189592Sbms return (-ENOMEM); 3075189592Sbms } 3076189592Sbms m->m_pkthdr.PH_vt.vt_nrecs = 0; 3077189592Sbms igmp_save_context(m, ifp); 3078189592Sbms m->m_data += IGMP_LEADINGSPACE; 3079189592Sbms m0srcs = (ifp->if_mtu - IGMP_LEADINGSPACE - 3080189592Sbms sizeof(struct igmp_grouprec)) / 3081189592Sbms sizeof(in_addr_t); 3082189592Sbms CTR1(KTR_IGMPV3, 3083189592Sbms "%s: allocated new packet", __func__); 3084189592Sbms } 3085189592Sbms /* 3086189592Sbms * Append the IGMP group record header to the 3087189592Sbms * current packet's data area. 3088189592Sbms * Recalculate pointer to free space for next 3089189592Sbms * group record, in case m_append() allocated 3090189592Sbms * a new mbuf or cluster. 3091189592Sbms */ 3092189592Sbms memset(&ig, 0, sizeof(ig)); 3093189592Sbms ig.ig_group = inm->inm_addr; 3094189592Sbms if (!m_append(m, sizeof(ig), (void *)&ig)) { 3095189592Sbms if (m != m0) 3096189592Sbms m_freem(m); 3097189592Sbms CTR1(KTR_IGMPV3, 3098189592Sbms "%s: m_append() failed", __func__); 3099189592Sbms return (-ENOMEM); 3100189592Sbms } 3101189592Sbms nbytes += sizeof(struct igmp_grouprec); 3102189592Sbms if (m == m0) { 3103189592Sbms md = m_last(m); 3104189592Sbms pig = (struct igmp_grouprec *)(mtod(md, 3105189592Sbms uint8_t *) + md->m_len - nbytes); 3106189592Sbms } else { 3107189592Sbms md = m_getptr(m, 0, &off); 3108189592Sbms pig = (struct igmp_grouprec *)(mtod(md, 3109189592Sbms uint8_t *) + off); 3110189592Sbms } 3111189592Sbms /* 3112189592Sbms * Begin walking the tree for this record type 3113189592Sbms * pass, or continue from where we left off 3114189592Sbms * previously if we had to allocate a new packet. 3115189592Sbms * Only report deltas in-mode at t1. 3116189592Sbms * We need not report included sources as allowed 3117189592Sbms * if we are in inclusive mode on the group, 3118189592Sbms * however the converse is not true. 3119189592Sbms */ 3120189592Sbms rsrcs = 0; 3121189592Sbms if (nims == NULL) 3122189592Sbms nims = RB_MIN(ip_msource_tree, &inm->inm_srcs); 3123189592Sbms RB_FOREACH_FROM(ims, ip_msource_tree, nims) { 3124189592Sbms CTR2(KTR_IGMPV3, "%s: visit node %s", 3125189592Sbms __func__, inet_ntoa_haddr(ims->ims_haddr)); 3126189592Sbms now = ims_get_mode(inm, ims, 1); 3127189592Sbms then = ims_get_mode(inm, ims, 0); 3128189592Sbms CTR3(KTR_IGMPV3, "%s: mode: t0 %d, t1 %d", 3129189592Sbms __func__, then, now); 3130189592Sbms if (now == then) { 3131189592Sbms CTR1(KTR_IGMPV3, 3132189592Sbms "%s: skip unchanged", __func__); 3133189592Sbms continue; 3134189592Sbms } 3135189592Sbms if (mode == MCAST_EXCLUDE && 3136189592Sbms now == MCAST_INCLUDE) { 3137189592Sbms CTR1(KTR_IGMPV3, 3138189592Sbms "%s: skip IN src on EX group", 3139189592Sbms __func__); 3140189592Sbms continue; 3141189592Sbms } 3142189592Sbms nrt = (rectype_t)now; 3143189592Sbms if (nrt == REC_NONE) 3144189592Sbms nrt = (rectype_t)(~mode & REC_FULL); 3145189592Sbms if (schanged++ == 0) { 3146189592Sbms crt = nrt; 3147189592Sbms } else if (crt != nrt) 3148189592Sbms continue; 3149189592Sbms naddr = htonl(ims->ims_haddr); 3150189592Sbms if (!m_append(m, sizeof(in_addr_t), 3151189592Sbms (void *)&naddr)) { 3152189592Sbms if (m != m0) 3153189592Sbms m_freem(m); 3154189592Sbms CTR1(KTR_IGMPV3, 3155189592Sbms "%s: m_append() failed", __func__); 3156189592Sbms return (-ENOMEM); 3157189592Sbms } 3158189592Sbms nallow += !!(crt == REC_ALLOW); 3159189592Sbms nblock += !!(crt == REC_BLOCK); 3160189592Sbms if (++rsrcs == m0srcs) 3161189592Sbms break; 3162189592Sbms } 3163189592Sbms /* 3164189592Sbms * If we did not append any tree nodes on this 3165189592Sbms * pass, back out of allocations. 3166189592Sbms */ 3167189592Sbms if (rsrcs == 0) { 3168189592Sbms nbytes -= sizeof(struct igmp_grouprec); 3169189592Sbms if (m != m0) { 3170189592Sbms CTR1(KTR_IGMPV3, 3171189592Sbms "%s: m_free(m)", __func__); 3172189592Sbms m_freem(m); 3173189592Sbms } else { 3174189592Sbms CTR1(KTR_IGMPV3, 3175189592Sbms "%s: m_adj(m, -ig)", __func__); 3176189592Sbms m_adj(m, -((int)sizeof( 3177189592Sbms struct igmp_grouprec))); 3178189592Sbms } 3179189592Sbms continue; 3180189592Sbms } 3181189592Sbms nbytes += (rsrcs * sizeof(in_addr_t)); 3182189592Sbms if (crt == REC_ALLOW) 3183189592Sbms pig->ig_type = IGMP_ALLOW_NEW_SOURCES; 3184189592Sbms else if (crt == REC_BLOCK) 3185189592Sbms pig->ig_type = IGMP_BLOCK_OLD_SOURCES; 3186189592Sbms pig->ig_numsrc = htons(rsrcs); 3187189592Sbms /* 3188189592Sbms * Count the new group record, and enqueue this 3189189592Sbms * packet if it wasn't already queued. 3190189592Sbms */ 3191189592Sbms m->m_pkthdr.PH_vt.vt_nrecs++; 3192189592Sbms if (m != m0) 3193189592Sbms _IF_ENQUEUE(ifq, m); 3194189592Sbms } while (nims != NULL); 3195189592Sbms drt |= crt; 3196189592Sbms crt = (~crt & REC_FULL); 3197189592Sbms } 3198189592Sbms 3199189592Sbms CTR3(KTR_IGMPV3, "%s: queued %d ALLOW_NEW, %d BLOCK_OLD", __func__, 3200189592Sbms nallow, nblock); 3201189592Sbms 3202189592Sbms return (nbytes); 3203189592Sbms} 3204189592Sbms 3205189592Sbmsstatic int 3206189592Sbmsigmp_v3_merge_state_changes(struct in_multi *inm, struct ifqueue *ifscq) 3207189592Sbms{ 3208189592Sbms struct ifqueue *gq; 3209189592Sbms struct mbuf *m; /* pending state-change */ 3210189592Sbms struct mbuf *m0; /* copy of pending state-change */ 3211189592Sbms struct mbuf *mt; /* last state-change in packet */ 3212189592Sbms int docopy, domerge; 3213189592Sbms u_int recslen; 3214189592Sbms 3215189592Sbms docopy = 0; 3216189592Sbms domerge = 0; 3217189592Sbms recslen = 0; 3218189592Sbms 3219189592Sbms IN_MULTI_LOCK_ASSERT(); 3220189592Sbms IGMP_LOCK_ASSERT(); 3221189592Sbms 3222189592Sbms /* 3223189592Sbms * If there are further pending retransmissions, make a writable 3224189592Sbms * copy of each queued state-change message before merging. 3225189592Sbms */ 3226189592Sbms if (inm->inm_scrv > 0) 3227189592Sbms docopy = 1; 3228189592Sbms 3229189592Sbms gq = &inm->inm_scq; 3230189592Sbms#ifdef KTR 3231189592Sbms if (gq->ifq_head == NULL) { 3232189592Sbms CTR2(KTR_IGMPV3, "%s: WARNING: queue for inm %p is empty", 3233189592Sbms __func__, inm); 3234189592Sbms } 3235189592Sbms#endif 3236189592Sbms 3237189592Sbms m = gq->ifq_head; 3238189592Sbms while (m != NULL) { 3239189592Sbms /* 3240189592Sbms * Only merge the report into the current packet if 3241189592Sbms * there is sufficient space to do so; an IGMPv3 report 3242189592Sbms * packet may only contain 65,535 group records. 3243189592Sbms * Always use a simple mbuf chain concatentation to do this, 3244189592Sbms * as large state changes for single groups may have 3245189592Sbms * allocated clusters. 3246189592Sbms */ 3247189592Sbms domerge = 0; 3248189592Sbms mt = ifscq->ifq_tail; 3249189592Sbms if (mt != NULL) { 3250189592Sbms recslen = m_length(m, NULL); 3251189592Sbms 3252189592Sbms if ((mt->m_pkthdr.PH_vt.vt_nrecs + 3253189592Sbms m->m_pkthdr.PH_vt.vt_nrecs <= 3254189592Sbms IGMP_V3_REPORT_MAXRECS) && 3255189592Sbms (mt->m_pkthdr.len + recslen <= 3256189592Sbms (inm->inm_ifp->if_mtu - IGMP_LEADINGSPACE))) 3257189592Sbms domerge = 1; 3258189592Sbms } 3259189592Sbms 3260189592Sbms if (!domerge && _IF_QFULL(gq)) { 3261189592Sbms CTR2(KTR_IGMPV3, 3262189592Sbms "%s: outbound queue full, skipping whole packet %p", 3263189592Sbms __func__, m); 3264189592Sbms mt = m->m_nextpkt; 3265189592Sbms if (!docopy) 3266189592Sbms m_freem(m); 3267189592Sbms m = mt; 3268189592Sbms continue; 3269189592Sbms } 3270189592Sbms 3271189592Sbms if (!docopy) { 3272189592Sbms CTR2(KTR_IGMPV3, "%s: dequeueing %p", __func__, m); 3273189592Sbms _IF_DEQUEUE(gq, m0); 3274189592Sbms m = m0->m_nextpkt; 3275189592Sbms } else { 3276189592Sbms CTR2(KTR_IGMPV3, "%s: copying %p", __func__, m); 3277189592Sbms m0 = m_dup(m, M_NOWAIT); 3278189592Sbms if (m0 == NULL) 3279189592Sbms return (ENOMEM); 3280189592Sbms m0->m_nextpkt = NULL; 3281189592Sbms m = m->m_nextpkt; 3282189592Sbms } 3283189592Sbms 3284189592Sbms if (!domerge) { 3285189592Sbms CTR3(KTR_IGMPV3, "%s: queueing %p to ifscq %p)", 3286189592Sbms __func__, m0, ifscq); 3287189592Sbms _IF_ENQUEUE(ifscq, m0); 3288189592Sbms } else { 3289189592Sbms struct mbuf *mtl; /* last mbuf of packet mt */ 3290189592Sbms 3291189592Sbms CTR3(KTR_IGMPV3, "%s: merging %p with ifscq tail %p)", 3292189592Sbms __func__, m0, mt); 3293189592Sbms 3294189592Sbms mtl = m_last(mt); 3295189592Sbms m0->m_flags &= ~M_PKTHDR; 3296189592Sbms mt->m_pkthdr.len += recslen; 3297189592Sbms mt->m_pkthdr.PH_vt.vt_nrecs += 3298189592Sbms m0->m_pkthdr.PH_vt.vt_nrecs; 3299189592Sbms 3300189592Sbms mtl->m_next = m0; 3301189592Sbms } 3302189592Sbms } 3303189592Sbms 3304189592Sbms return (0); 3305189592Sbms} 3306189592Sbms 3307189592Sbms/* 3308189592Sbms * Respond to a pending IGMPv3 General Query. 3309189592Sbms */ 3310189592Sbmsstatic void 3311189592Sbmsigmp_v3_dispatch_general_query(struct igmp_ifinfo *igi) 3312189592Sbms{ 3313189592Sbms struct ifmultiaddr *ifma, *tifma; 3314189592Sbms struct ifnet *ifp; 3315189592Sbms struct in_multi *inm; 3316189592Sbms int retval, loop; 3317189592Sbms 3318189592Sbms IN_MULTI_LOCK_ASSERT(); 3319189592Sbms IGMP_LOCK_ASSERT(); 3320189592Sbms 3321189592Sbms KASSERT(igi->igi_version == IGMP_VERSION_3, 3322189592Sbms ("%s: called when version %d", __func__, igi->igi_version)); 3323189592Sbms 3324189592Sbms ifp = igi->igi_ifp; 3325189592Sbms 3326189592Sbms IF_ADDR_LOCK(ifp); 3327189592Sbms TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, tifma) { 3328189931Sbms if (ifma->ifma_addr->sa_family != AF_INET || 3329189931Sbms ifma->ifma_protospec == NULL) 3330189592Sbms continue; 3331189592Sbms 3332189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 3333189592Sbms KASSERT(ifp == inm->inm_ifp, 3334189592Sbms ("%s: inconsistent ifp", __func__)); 3335189592Sbms 3336189592Sbms switch (inm->inm_state) { 3337189592Sbms case IGMP_NOT_MEMBER: 3338189592Sbms case IGMP_SILENT_MEMBER: 3339189592Sbms break; 3340189592Sbms case IGMP_REPORTING_MEMBER: 3341189592Sbms case IGMP_IDLE_MEMBER: 3342189592Sbms case IGMP_LAZY_MEMBER: 3343189592Sbms case IGMP_SLEEPING_MEMBER: 3344189592Sbms case IGMP_AWAKENING_MEMBER: 3345189592Sbms inm->inm_state = IGMP_REPORTING_MEMBER; 3346189592Sbms retval = igmp_v3_enqueue_group_record(&igi->igi_gq, 3347189592Sbms inm, 0, 0, 0); 3348189592Sbms CTR2(KTR_IGMPV3, "%s: enqueue record = %d", 3349189592Sbms __func__, retval); 3350189592Sbms break; 3351189592Sbms case IGMP_G_QUERY_PENDING_MEMBER: 3352189592Sbms case IGMP_SG_QUERY_PENDING_MEMBER: 3353189592Sbms case IGMP_LEAVING_MEMBER: 3354189592Sbms break; 3355189592Sbms } 3356189592Sbms } 3357189592Sbms IF_ADDR_UNLOCK(ifp); 3358189592Sbms 3359189592Sbms loop = (igi->igi_flags & IGIF_LOOPBACK) ? 1 : 0; 3360189592Sbms igmp_dispatch_queue(&igi->igi_gq, IGMP_MAX_RESPONSE_BURST, loop); 3361189592Sbms 3362189592Sbms /* 3363189592Sbms * Slew transmission of bursts over 500ms intervals. 3364189592Sbms */ 3365189592Sbms if (igi->igi_gq.ifq_head != NULL) { 3366189592Sbms igi->igi_v3_timer = 1 + IGMP_RANDOM_DELAY( 3367189592Sbms IGMP_RESPONSE_BURST_INTERVAL); 3368189592Sbms V_interface_timers_running = 1; 3369189592Sbms } 3370189592Sbms} 3371189592Sbms 3372189592Sbms/* 3373189592Sbms * Transmit the next pending IGMP message in the output queue. 3374189592Sbms * 3375189592Sbms * We get called from netisr_processqueue(). A mutex private to igmpoq 3376189592Sbms * will be acquired and released around this routine. 3377189592Sbms * 3378189592Sbms * VIMAGE: Needs to store/restore vnet pointer on a per-mbuf-chain basis. 3379189592Sbms * MRT: Nothing needs to be done, as IGMP traffic is always local to 3380189592Sbms * a link and uses a link-scope multicast address. 3381189592Sbms */ 3382189592Sbmsstatic void 3383189592Sbmsigmp_intr(struct mbuf *m) 3384189592Sbms{ 3385189592Sbms struct ip_moptions imo; 3386189592Sbms struct ifnet *ifp; 3387189592Sbms struct mbuf *ipopts, *m0; 3388189592Sbms int error; 3389189592Sbms uint32_t ifindex; 3390189592Sbms 3391189592Sbms CTR2(KTR_IGMPV3, "%s: transmit %p", __func__, m); 3392189592Sbms 3393189592Sbms /* 3394189592Sbms * Restore VNET image pointer from enqueued mbuf chain 3395189592Sbms * before doing anything else. Whilst we use interface 3396189592Sbms * indexes to guard against interface detach, they are 3397189592Sbms * unique to each VIMAGE and must be retrieved. 3398189592Sbms */ 3399189592Sbms CURVNET_SET(m->m_pkthdr.header); 3400189592Sbms ifindex = igmp_restore_context(m); 3401189592Sbms 3402189592Sbms /* 3403189592Sbms * Check if the ifnet still exists. This limits the scope of 3404189592Sbms * any race in the absence of a global ifp lock for low cost 3405189592Sbms * (an array lookup). 3406189592Sbms */ 3407189592Sbms ifp = ifnet_byindex(ifindex); 3408189592Sbms if (ifp == NULL) { 3409189592Sbms CTR3(KTR_IGMPV3, "%s: dropped %p as ifindex %u went away.", 3410189592Sbms __func__, m, ifindex); 3411189592Sbms m_freem(m); 3412189592Sbms V_ipstat.ips_noroute++; 3413189592Sbms goto out; 3414189592Sbms } 3415189592Sbms 3416189592Sbms ipopts = V_igmp_sendra ? m_raopt : NULL; 3417189592Sbms 3418119181Srwatson imo.imo_multicast_ttl = 1; 341915292Swollman imo.imo_multicast_vif = -1; 3420181803Sbz imo.imo_multicast_loop = (V_ip_mrouter != NULL); 34211541Srgrimes 342215292Swollman /* 3423189592Sbms * If the user requested that IGMP traffic be explicitly 3424189592Sbms * redirected to the loopback interface (e.g. they are running a 3425189592Sbms * MANET interface and the routing protocol needs to see the 3426189592Sbms * updates), handle this now. 342715292Swollman */ 3428189592Sbms if (m->m_flags & M_IGMP_LOOP) 3429189592Sbms imo.imo_multicast_ifp = V_loif; 3430189592Sbms else 3431189592Sbms imo.imo_multicast_ifp = ifp; 34322531Swollman 3433189592Sbms if (m->m_flags & M_IGMPV2) { 3434189592Sbms m0 = m; 3435189592Sbms } else { 3436189592Sbms m0 = igmp_v3_encap_report(ifp, m); 3437189592Sbms if (m0 == NULL) { 3438189592Sbms CTR2(KTR_IGMPV3, "%s: dropped %p", __func__, m); 3439189592Sbms m_freem(m); 3440189592Sbms V_ipstat.ips_odropped++; 3441189592Sbms goto out; 3442189592Sbms } 3443189592Sbms } 3444189592Sbms 3445189592Sbms igmp_scrub_context(m0); 3446189592Sbms m->m_flags &= ~(M_PROTOFLAGS); 3447189592Sbms m0->m_pkthdr.rcvif = V_loif; 3448189592Sbms#ifdef MAC 3449189592Sbms mac_netinet_igmp_send(ifp, m0); 3450189592Sbms#endif 3451189592Sbms error = ip_output(m0, ipopts, NULL, 0, &imo, NULL); 3452189592Sbms if (error) { 3453189592Sbms CTR3(KTR_IGMPV3, "%s: ip_output(%p) = %d", __func__, m0, error); 3454189592Sbms m_freem(m0); 3455189592Sbms goto out; 3456189592Sbms } 3457189592Sbms 3458181803Sbz ++V_igmpstat.igps_snd_reports; 3459189592Sbms 3460189592Sbmsout: 3461189592Sbms /* 3462189592Sbms * We must restore the existing vnet pointer before 3463189592Sbms * continuing as we are run from netisr context. 3464189592Sbms */ 3465189592Sbms CURVNET_RESTORE(); 34661541Srgrimes} 3467189592Sbms 3468189592Sbms/* 3469189592Sbms * Encapsulate an IGMPv3 report. 3470189592Sbms * 3471189592Sbms * The internal mbuf flag M_IGMPV3_HDR is used to indicate that the mbuf 3472189592Sbms * chain has already had its IP/IGMPv3 header prepended. In this case 3473189592Sbms * the function will not attempt to prepend; the lengths and checksums 3474189592Sbms * will however be re-computed. 3475189592Sbms * 3476189592Sbms * Returns a pointer to the new mbuf chain head, or NULL if the 3477189592Sbms * allocation failed. 3478189592Sbms */ 3479189592Sbmsstatic struct mbuf * 3480189592Sbmsigmp_v3_encap_report(struct ifnet *ifp, struct mbuf *m) 3481189592Sbms{ 3482189592Sbms INIT_VNET_NET(curvnet); 3483189592Sbms INIT_VNET_INET(curvnet); 3484189592Sbms struct igmp_report *igmp; 3485189592Sbms struct ip *ip; 3486189592Sbms int hdrlen, igmpreclen; 3487189592Sbms 3488189592Sbms KASSERT((m->m_flags & M_PKTHDR), 3489189592Sbms ("%s: mbuf chain %p is !M_PKTHDR", __func__, m)); 3490189592Sbms 3491189592Sbms igmpreclen = m_length(m, NULL); 3492189592Sbms hdrlen = sizeof(struct ip) + sizeof(struct igmp_report); 3493189592Sbms 3494189592Sbms if (m->m_flags & M_IGMPV3_HDR) { 3495189592Sbms igmpreclen -= hdrlen; 3496189592Sbms } else { 3497189592Sbms M_PREPEND(m, hdrlen, M_DONTWAIT); 3498189592Sbms if (m == NULL) 3499189592Sbms return (NULL); 3500189592Sbms m->m_flags |= M_IGMPV3_HDR; 3501189592Sbms } 3502189592Sbms 3503189592Sbms CTR2(KTR_IGMPV3, "%s: igmpreclen is %d", __func__, igmpreclen); 3504189592Sbms 3505189592Sbms m->m_data += sizeof(struct ip); 3506189592Sbms m->m_len -= sizeof(struct ip); 3507189592Sbms 3508189592Sbms igmp = mtod(m, struct igmp_report *); 3509189592Sbms igmp->ir_type = IGMP_v3_HOST_MEMBERSHIP_REPORT; 3510189592Sbms igmp->ir_rsv1 = 0; 3511189592Sbms igmp->ir_rsv2 = 0; 3512189592Sbms igmp->ir_numgrps = htons(m->m_pkthdr.PH_vt.vt_nrecs); 3513189592Sbms igmp->ir_cksum = 0; 3514189592Sbms igmp->ir_cksum = in_cksum(m, sizeof(struct igmp_report) + igmpreclen); 3515189592Sbms m->m_pkthdr.PH_vt.vt_nrecs = 0; 3516189592Sbms 3517189592Sbms m->m_data -= sizeof(struct ip); 3518189592Sbms m->m_len += sizeof(struct ip); 3519189592Sbms 3520189592Sbms ip = mtod(m, struct ip *); 3521189592Sbms ip->ip_tos = IPTOS_PREC_INTERNETCONTROL; 3522189592Sbms ip->ip_len = hdrlen + igmpreclen; 3523189592Sbms ip->ip_off = IP_DF; 3524189592Sbms ip->ip_p = IPPROTO_IGMP; 3525189592Sbms ip->ip_sum = 0; 3526189592Sbms 3527189592Sbms ip->ip_src.s_addr = INADDR_ANY; 3528189592Sbms 3529189592Sbms if (m->m_flags & M_IGMP_LOOP) { 3530189592Sbms struct in_ifaddr *ia; 3531189592Sbms 3532189592Sbms IFP_TO_IA(ifp, ia); 3533189592Sbms if (ia != NULL) 3534189592Sbms ip->ip_src = ia->ia_addr.sin_addr; 3535189592Sbms } 3536189592Sbms 3537189592Sbms ip->ip_dst.s_addr = htonl(INADDR_ALLRPTS_GROUP); 3538189592Sbms 3539189592Sbms return (m); 3540189592Sbms} 3541189592Sbms 3542189592Sbms#ifdef KTR 3543189592Sbmsstatic char * 3544189592Sbmsigmp_rec_type_to_str(const int type) 3545189592Sbms{ 3546189592Sbms 3547189592Sbms switch (type) { 3548189592Sbms case IGMP_CHANGE_TO_EXCLUDE_MODE: 3549189592Sbms return "TO_EX"; 3550189592Sbms break; 3551189592Sbms case IGMP_CHANGE_TO_INCLUDE_MODE: 3552189592Sbms return "TO_IN"; 3553189592Sbms break; 3554189592Sbms case IGMP_MODE_IS_EXCLUDE: 3555189592Sbms return "MODE_EX"; 3556189592Sbms break; 3557189592Sbms case IGMP_MODE_IS_INCLUDE: 3558189592Sbms return "MODE_IN"; 3559189592Sbms break; 3560189592Sbms case IGMP_ALLOW_NEW_SOURCES: 3561189592Sbms return "ALLOW_NEW"; 3562189592Sbms break; 3563189592Sbms case IGMP_BLOCK_OLD_SOURCES: 3564189592Sbms return "BLOCK_OLD"; 3565189592Sbms break; 3566189592Sbms default: 3567189592Sbms break; 3568189592Sbms } 3569189592Sbms return "unknown"; 3570189592Sbms} 3571189592Sbms#endif 3572189592Sbms 3573189592Sbmsstatic void 3574189592Sbmsigmp_sysinit(void) 3575189592Sbms{ 3576189592Sbms 3577189592Sbms CTR1(KTR_IGMPV3, "%s: initializing", __func__); 3578189592Sbms 3579189592Sbms IGMP_LOCK_INIT(); 3580189592Sbms TUNABLE_INT_FETCH("debug.mpsafeigmp", &mpsafe_igmp); 3581189592Sbms 3582189592Sbms mtx_init(&igmpoq.ifq_mtx, "igmpoq_mtx", NULL, MTX_DEF); 3583189592Sbms IFQ_SET_MAXLEN(&igmpoq, IFQ_MAXLEN); 3584189592Sbms 3585189592Sbms m_raopt = igmp_ra_alloc(); 3586189592Sbms 3587189592Sbms#if __FreeBSD_version < 800000 3588189592Sbms netisr_register(NETISR_IGMP, igmp_intr, &igmpoq, 3589189592Sbms mpsafe_igmp ? NETISR_MPSAFE : 0); 3590189592Sbms#else 3591189592Sbms netisr_register(NETISR_IGMP, igmp_intr, &igmpoq, 3592189592Sbms mpsafe_igmp ? 0 : NETISR_FORCEQUEUE); 3593189592Sbms#endif 3594189592Sbms} 3595189592Sbms 3596189592Sbmsstatic void 3597189592Sbmsigmp_sysuninit(void) 3598189592Sbms{ 3599189592Sbms 3600189592Sbms CTR1(KTR_IGMPV3, "%s: tearing down", __func__); 3601189592Sbms 3602189592Sbms netisr_unregister(NETISR_IGMP); 3603189592Sbms mtx_destroy(&igmpoq.ifq_mtx); 3604189592Sbms 3605189592Sbms m_free(m_raopt); 3606189592Sbms m_raopt = NULL; 3607189592Sbms 3608189592Sbms IGMP_LOCK_DESTROY(); 3609189592Sbms} 3610189592Sbms 3611189592Sbms/* 3612189592Sbms * Initialize an IGMPv3 instance. 3613189592Sbms * VIMAGE: Assumes curvnet set by caller and called per vimage. 3614189592Sbms */ 3615189592Sbmsstatic int 3616189592Sbmsvnet_igmp_iattach(const void *unused __unused) 3617189592Sbms{ 3618189592Sbms INIT_VNET_INET(curvnet); 3619189592Sbms 3620189592Sbms CTR1(KTR_IGMPV3, "%s: initializing", __func__); 3621189592Sbms 3622189592Sbms LIST_INIT(&V_igi_head); 3623189592Sbms 3624189592Sbms V_current_state_timers_running = 0; 3625189592Sbms V_state_change_timers_running = 0; 3626189592Sbms V_interface_timers_running = 0; 3627189592Sbms 3628189592Sbms /* 3629189592Sbms * Initialize sysctls to default values. 3630189592Sbms */ 3631189592Sbms V_igmp_recvifkludge = 1; 3632189592Sbms V_igmp_sendra = 1; 3633189592Sbms V_igmp_sendlocal = 1; 3634189592Sbms V_igmp_v1enable = 1; 3635189592Sbms V_igmp_v2enable = 1; 3636189592Sbms V_igmp_legacysupp = 0; 3637189592Sbms V_igmp_default_version = IGMP_VERSION_3; 3638189592Sbms V_igmp_gsrdelay.tv_sec = 10; 3639189592Sbms V_igmp_gsrdelay.tv_usec = 0; 3640189592Sbms 3641189592Sbms memset(&V_igmpstat, 0, sizeof(struct igmpstat)); 3642189592Sbms V_igmpstat.igps_version = IGPS_VERSION_3; 3643189592Sbms V_igmpstat.igps_len = sizeof(struct igmpstat); 3644189592Sbms 3645189592Sbms return (0); 3646189592Sbms} 3647189592Sbms 3648189592Sbmsstatic int 3649189592Sbmsvnet_igmp_idetach(const void *unused __unused) 3650189592Sbms{ 3651189592Sbms INIT_VNET_INET(curvnet); 3652189592Sbms 3653189592Sbms CTR1(KTR_IGMPV3, "%s: tearing down", __func__); 3654189592Sbms 3655189592Sbms KASSERT(LIST_EMPTY(&V_igi_head), 3656189592Sbms ("%s: igi list not empty; ifnets not detached?", __func__)); 3657189592Sbms 3658189592Sbms return (0); 3659189592Sbms} 3660189592Sbms 3661189592Sbms#ifdef VIMAGE 3662189592Sbmsstatic struct vnet_symmap vnet_igmp_symmap[] = { 3663189592Sbms VNET_SYMMAP(igmp, igi_head), 3664189592Sbms VNET_SYMMAP(igmp, igmpstat), 3665189592Sbms VNET_SYMMAP_END 3666189592Sbms}; 3667189592SbmsVNET_MOD_DECLARE(IGMP, igmp, vnet_igmp_iattach, vnet_igmp_idetach, 3668189592Sbms vnet_igmp_symmap); 3669189592Sbms#endif /* VIMAGE */ 3670189592Sbms 3671189592Sbmsstatic int 3672189592Sbmsigmp_modevent(module_t mod, int type, void *unused __unused) 3673189592Sbms{ 3674189592Sbms 3675189592Sbms switch (type) { 3676189592Sbms case MOD_LOAD: 3677189592Sbms igmp_sysinit(); 3678189592Sbms#ifdef VIMAGE 3679189592Sbms vnet_mod_register(&vnet_igmp_modinfo); 3680189592Sbms#else 3681189592Sbms (void)vnet_igmp_iattach(NULL); 3682189592Sbms#endif /* VIMAGE */ 3683189592Sbms break; 3684189592Sbms case MOD_UNLOAD: 3685189592Sbms#ifdef VIMAGE 3686189592Sbms /* 3687189592Sbms * TODO: Allow module unload if any VIMAGE instances 3688189592Sbms * are using this module. 3689189592Sbms */ 3690189592Sbms return (EBUSY); 3691189592Sbms#else 3692189592Sbms (void)vnet_igmp_idetach(NULL); 3693189592Sbms#endif /* VIMAGE */ 3694189592Sbms igmp_sysuninit(); 3695189592Sbms break; 3696189592Sbms default: 3697189592Sbms return (EOPNOTSUPP); 3698189592Sbms } 3699189592Sbms return (0); 3700189592Sbms} 3701189592Sbms 3702189592Sbmsstatic moduledata_t igmp_mod = { 3703189592Sbms "igmp", 3704189592Sbms igmp_modevent, 3705189592Sbms 0 3706189592Sbms}; 3707189592SbmsDECLARE_MODULE(igmp, igmp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 3708