1 1.70 maxv /* $NetBSD: igmp.c,v 1.70 2020/05/15 06:34:34 maxv Exp $ */ 2 1.21 itojun 3 1.21 itojun /* 4 1.21 itojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 1.21 itojun * All rights reserved. 6 1.29 itojun * 7 1.21 itojun * Redistribution and use in source and binary forms, with or without 8 1.21 itojun * modification, are permitted provided that the following conditions 9 1.21 itojun * are met: 10 1.21 itojun * 1. Redistributions of source code must retain the above copyright 11 1.21 itojun * notice, this list of conditions and the following disclaimer. 12 1.21 itojun * 2. Redistributions in binary form must reproduce the above copyright 13 1.21 itojun * notice, this list of conditions and the following disclaimer in the 14 1.21 itojun * documentation and/or other materials provided with the distribution. 15 1.21 itojun * 3. Neither the name of the project nor the names of its contributors 16 1.21 itojun * may be used to endorse or promote products derived from this software 17 1.21 itojun * without specific prior written permission. 18 1.29 itojun * 19 1.21 itojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 1.21 itojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.21 itojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.21 itojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 1.21 itojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.21 itojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.21 itojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.21 itojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.21 itojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.21 itojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.21 itojun * SUCH DAMAGE. 30 1.21 itojun */ 31 1.8 cgd 32 1.1 hpeyerl /* 33 1.10 mycroft * Internet Group Management Protocol (IGMP) routines. 34 1.1 hpeyerl * 35 1.10 mycroft * Written by Steve Deering, Stanford, May 1988. 36 1.10 mycroft * Modified by Rosen Sharma, Stanford, Aug 1994. 37 1.10 mycroft * Modified by Bill Fenner, Xerox PARC, Feb 1995. 38 1.1 hpeyerl * 39 1.10 mycroft * MULTICAST Revision: 1.3 40 1.1 hpeyerl */ 41 1.27 lukem 42 1.27 lukem #include <sys/cdefs.h> 43 1.70 maxv __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.70 2020/05/15 06:34:34 maxv Exp $"); 44 1.17 scottr 45 1.56 pooka #ifdef _KERNEL_OPT 46 1.17 scottr #include "opt_mrouting.h" 47 1.64 ozaki #include "opt_net_mpsafe.h" 48 1.56 pooka #endif 49 1.1 hpeyerl 50 1.1 hpeyerl #include <sys/param.h> 51 1.1 hpeyerl #include <sys/mbuf.h> 52 1.1 hpeyerl #include <sys/socket.h> 53 1.48 ad #include <sys/socketvar.h> 54 1.15 christos #include <sys/systm.h> 55 1.55 rmind #include <sys/cprng.h> 56 1.46 thorpej #include <sys/sysctl.h> 57 1.1 hpeyerl 58 1.1 hpeyerl #include <net/if.h> 59 1.47 thorpej #include <net/net_stats.h> 60 1.1 hpeyerl 61 1.1 hpeyerl #include <netinet/in.h> 62 1.1 hpeyerl #include <netinet/in_var.h> 63 1.1 hpeyerl #include <netinet/in_systm.h> 64 1.1 hpeyerl #include <netinet/ip.h> 65 1.1 hpeyerl #include <netinet/ip_var.h> 66 1.1 hpeyerl #include <netinet/igmp.h> 67 1.1 hpeyerl #include <netinet/igmp_var.h> 68 1.1 hpeyerl 69 1.55 rmind /* 70 1.55 rmind * Per-interface router version information. 71 1.55 rmind */ 72 1.55 rmind typedef struct router_info { 73 1.55 rmind LIST_ENTRY(router_info) rti_link; 74 1.55 rmind ifnet_t * rti_ifp; 75 1.55 rmind int rti_type; /* type of router on this interface */ 76 1.55 rmind int rti_age; /* time since last v1 query */ 77 1.55 rmind } router_info_t; 78 1.10 mycroft 79 1.55 rmind /* 80 1.55 rmind * The router-info list and the timer flag are protected by in_multilock. 81 1.55 rmind * 82 1.55 rmind * Lock order: 83 1.55 rmind * 84 1.55 rmind * softnet_lock -> 85 1.55 rmind * in_multilock 86 1.55 rmind */ 87 1.55 rmind static struct pool igmp_rti_pool __cacheline_aligned; 88 1.55 rmind static LIST_HEAD(, router_info) rti_head __cacheline_aligned; 89 1.55 rmind static int igmp_timers_on __cacheline_aligned; 90 1.55 rmind static percpu_t * igmpstat_percpu __read_mostly; 91 1.46 thorpej 92 1.47 thorpej #define IGMP_STATINC(x) _NET_STATINC(igmpstat_percpu, x) 93 1.46 thorpej 94 1.55 rmind static void igmp_sendpkt(struct in_multi *, int); 95 1.55 rmind static int rti_fill(struct in_multi *); 96 1.55 rmind static router_info_t * rti_find(struct ifnet *); 97 1.55 rmind static void rti_delete(struct ifnet *); 98 1.55 rmind static void sysctl_net_inet_igmp_setup(struct sysctllog **); 99 1.51 pooka 100 1.55 rmind /* 101 1.55 rmind * rti_fill: associate router information with the given multicast group; 102 1.55 rmind * if there is no router information for the interface, then create it. 103 1.55 rmind */ 104 1.10 mycroft static int 105 1.41 perry rti_fill(struct in_multi *inm) 106 1.10 mycroft { 107 1.55 rmind router_info_t *rti; 108 1.55 rmind 109 1.55 rmind KASSERT(in_multi_lock_held()); 110 1.10 mycroft 111 1.33 matt LIST_FOREACH(rti, &rti_head, rti_link) { 112 1.10 mycroft if (rti->rti_ifp == inm->inm_ifp) { 113 1.10 mycroft inm->inm_rti = rti; 114 1.55 rmind return rti->rti_type == IGMP_v1_ROUTER ? 115 1.55 rmind IGMP_v1_HOST_MEMBERSHIP_REPORT : 116 1.55 rmind IGMP_v2_HOST_MEMBERSHIP_REPORT; 117 1.10 mycroft } 118 1.10 mycroft } 119 1.33 matt rti = pool_get(&igmp_rti_pool, PR_NOWAIT); 120 1.55 rmind if (rti == NULL) { 121 1.33 matt return 0; 122 1.55 rmind } 123 1.10 mycroft rti->rti_ifp = inm->inm_ifp; 124 1.10 mycroft rti->rti_type = IGMP_v2_ROUTER; 125 1.33 matt LIST_INSERT_HEAD(&rti_head, rti, rti_link); 126 1.10 mycroft inm->inm_rti = rti; 127 1.55 rmind return IGMP_v2_HOST_MEMBERSHIP_REPORT; 128 1.10 mycroft } 129 1.10 mycroft 130 1.55 rmind /* 131 1.55 rmind * rti_find: lookup or create router information for the given interface. 132 1.55 rmind */ 133 1.55 rmind static router_info_t * 134 1.55 rmind rti_find(ifnet_t *ifp) 135 1.10 mycroft { 136 1.55 rmind router_info_t *rti; 137 1.55 rmind 138 1.55 rmind KASSERT(in_multi_lock_held()); 139 1.10 mycroft 140 1.33 matt LIST_FOREACH(rti, &rti_head, rti_link) { 141 1.10 mycroft if (rti->rti_ifp == ifp) 142 1.55 rmind return rti; 143 1.10 mycroft } 144 1.33 matt rti = pool_get(&igmp_rti_pool, PR_NOWAIT); 145 1.43 tls if (rti == NULL) { 146 1.33 matt return NULL; 147 1.43 tls } 148 1.10 mycroft rti->rti_ifp = ifp; 149 1.10 mycroft rti->rti_type = IGMP_v2_ROUTER; 150 1.33 matt LIST_INSERT_HEAD(&rti_head, rti, rti_link); 151 1.55 rmind return rti; 152 1.1 hpeyerl } 153 1.1 hpeyerl 154 1.55 rmind /* 155 1.55 rmind * rti_delete: remove and free the router information entry for the 156 1.55 rmind * given interface. 157 1.55 rmind */ 158 1.34 itojun static void 159 1.55 rmind rti_delete(ifnet_t *ifp) 160 1.34 itojun { 161 1.55 rmind router_info_t *rti; 162 1.55 rmind 163 1.55 rmind KASSERT(in_multi_lock_held()); 164 1.34 itojun 165 1.34 itojun LIST_FOREACH(rti, &rti_head, rti_link) { 166 1.34 itojun if (rti->rti_ifp == ifp) { 167 1.34 itojun LIST_REMOVE(rti, rti_link); 168 1.34 itojun pool_put(&igmp_rti_pool, rti); 169 1.55 rmind break; 170 1.34 itojun } 171 1.34 itojun } 172 1.34 itojun } 173 1.34 itojun 174 1.1 hpeyerl void 175 1.46 thorpej igmp_init(void) 176 1.46 thorpej { 177 1.55 rmind pool_init(&igmp_rti_pool, sizeof(router_info_t), 0, 0, 0, 178 1.50 pooka "igmppl", NULL, IPL_SOFTNET); 179 1.46 thorpej igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS); 180 1.55 rmind sysctl_net_inet_igmp_setup(NULL); 181 1.55 rmind LIST_INIT(&rti_head); 182 1.46 thorpej } 183 1.46 thorpej 184 1.46 thorpej void 185 1.69 maxv igmp_input(struct mbuf *m, int off, int proto) 186 1.15 christos { 187 1.58 ozaki ifnet_t *ifp; 188 1.24 augustss struct ip *ip = mtod(m, struct ip *); 189 1.24 augustss struct igmp *igmp; 190 1.55 rmind u_int minlen, timer; 191 1.10 mycroft struct in_multi *inm; 192 1.24 augustss struct in_ifaddr *ia; 193 1.69 maxv int ip_len, iphlen; 194 1.58 ozaki struct psref psref; 195 1.15 christos 196 1.69 maxv iphlen = off; 197 1.1 hpeyerl 198 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_TOTAL); 199 1.1 hpeyerl 200 1.1 hpeyerl /* 201 1.1 hpeyerl * Validate lengths 202 1.1 hpeyerl */ 203 1.19 mycroft minlen = iphlen + IGMP_MINLEN; 204 1.31 itojun ip_len = ntohs(ip->ip_len); 205 1.31 itojun if (ip_len < minlen) { 206 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); 207 1.1 hpeyerl m_freem(m); 208 1.1 hpeyerl return; 209 1.1 hpeyerl } 210 1.25 matt if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0) 211 1.25 matt || m->m_len < minlen) { 212 1.53 liamjfoy if ((m = m_pullup(m, minlen)) == NULL) { 213 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); 214 1.25 matt return; 215 1.25 matt } 216 1.25 matt ip = mtod(m, struct ip *); 217 1.1 hpeyerl } 218 1.1 hpeyerl 219 1.1 hpeyerl /* 220 1.1 hpeyerl * Validate checksum 221 1.1 hpeyerl */ 222 1.1 hpeyerl m->m_data += iphlen; 223 1.1 hpeyerl m->m_len -= iphlen; 224 1.1 hpeyerl igmp = mtod(m, struct igmp *); 225 1.30 thorpej /* No need to assert alignment here. */ 226 1.31 itojun if (in_cksum(m, ip_len - iphlen)) { 227 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_BADSUM); 228 1.1 hpeyerl m_freem(m); 229 1.1 hpeyerl return; 230 1.1 hpeyerl } 231 1.1 hpeyerl m->m_data -= iphlen; 232 1.1 hpeyerl m->m_len += iphlen; 233 1.1 hpeyerl 234 1.58 ozaki ifp = m_get_rcvif_psref(m, &psref); 235 1.60 ozaki if (__predict_false(ifp == NULL)) 236 1.60 ozaki goto drop; 237 1.60 ozaki 238 1.1 hpeyerl switch (igmp->igmp_type) { 239 1.1 hpeyerl 240 1.1 hpeyerl case IGMP_HOST_MEMBERSHIP_QUERY: 241 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_QUERIES); 242 1.1 hpeyerl 243 1.6 mycroft if (ifp->if_flags & IFF_LOOPBACK) 244 1.1 hpeyerl break; 245 1.1 hpeyerl 246 1.10 mycroft if (igmp->igmp_code == 0) { 247 1.55 rmind struct in_multistep step; 248 1.55 rmind router_info_t *rti; 249 1.10 mycroft 250 1.12 mycroft if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) { 251 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES); 252 1.58 ozaki goto drop; 253 1.10 mycroft } 254 1.10 mycroft 255 1.55 rmind in_multi_lock(RW_WRITER); 256 1.55 rmind rti = rti_find(ifp); 257 1.55 rmind if (rti == NULL) { 258 1.55 rmind in_multi_unlock(); 259 1.55 rmind break; 260 1.55 rmind } 261 1.55 rmind rti->rti_type = IGMP_v1_ROUTER; 262 1.55 rmind rti->rti_age = 0; 263 1.55 rmind 264 1.10 mycroft /* 265 1.10 mycroft * Start the timers in all of our membership records 266 1.10 mycroft * for the interface on which the query arrived, 267 1.10 mycroft * except those that are already running and those 268 1.10 mycroft * that belong to a "local" group (224.0.0.X). 269 1.10 mycroft */ 270 1.55 rmind 271 1.55 rmind inm = in_first_multi(&step); 272 1.10 mycroft while (inm != NULL) { 273 1.10 mycroft if (inm->inm_ifp == ifp && 274 1.10 mycroft inm->inm_timer == 0 && 275 1.10 mycroft !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { 276 1.10 mycroft inm->inm_state = IGMP_DELAYING_MEMBER; 277 1.10 mycroft inm->inm_timer = IGMP_RANDOM_DELAY( 278 1.10 mycroft IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 279 1.55 rmind igmp_timers_on = true; 280 1.10 mycroft } 281 1.55 rmind inm = in_next_multi(&step); 282 1.10 mycroft } 283 1.55 rmind in_multi_unlock(); 284 1.10 mycroft } else { 285 1.55 rmind struct in_multistep step; 286 1.55 rmind 287 1.12 mycroft if (!IN_MULTICAST(ip->ip_dst.s_addr)) { 288 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES); 289 1.58 ozaki goto drop; 290 1.10 mycroft } 291 1.10 mycroft 292 1.10 mycroft timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 293 1.20 hwr if (timer == 0) 294 1.55 rmind timer = 1; 295 1.10 mycroft 296 1.10 mycroft /* 297 1.10 mycroft * Start the timers in all of our membership records 298 1.10 mycroft * for the interface on which the query arrived, 299 1.10 mycroft * except those that are already running and those 300 1.10 mycroft * that belong to a "local" group (224.0.0.X). For 301 1.10 mycroft * timers already running, check if they need to be 302 1.10 mycroft * reset. 303 1.10 mycroft */ 304 1.55 rmind in_multi_lock(RW_WRITER); 305 1.55 rmind inm = in_first_multi(&step); 306 1.10 mycroft while (inm != NULL) { 307 1.10 mycroft if (inm->inm_ifp == ifp && 308 1.10 mycroft !IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 309 1.12 mycroft (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || 310 1.16 mycroft in_hosteq(ip->ip_dst, inm->inm_addr))) { 311 1.10 mycroft switch (inm->inm_state) { 312 1.10 mycroft case IGMP_DELAYING_MEMBER: 313 1.10 mycroft if (inm->inm_timer <= timer) 314 1.10 mycroft break; 315 1.10 mycroft /* FALLTHROUGH */ 316 1.10 mycroft case IGMP_IDLE_MEMBER: 317 1.10 mycroft case IGMP_LAZY_MEMBER: 318 1.10 mycroft case IGMP_AWAKENING_MEMBER: 319 1.10 mycroft inm->inm_state = 320 1.10 mycroft IGMP_DELAYING_MEMBER; 321 1.10 mycroft inm->inm_timer = 322 1.10 mycroft IGMP_RANDOM_DELAY(timer); 323 1.55 rmind igmp_timers_on = true; 324 1.10 mycroft break; 325 1.10 mycroft case IGMP_SLEEPING_MEMBER: 326 1.10 mycroft inm->inm_state = 327 1.10 mycroft IGMP_AWAKENING_MEMBER; 328 1.10 mycroft break; 329 1.10 mycroft } 330 1.10 mycroft } 331 1.55 rmind inm = in_next_multi(&step); 332 1.10 mycroft } 333 1.55 rmind in_multi_unlock(); 334 1.10 mycroft } 335 1.10 mycroft 336 1.10 mycroft break; 337 1.10 mycroft 338 1.10 mycroft case IGMP_v1_HOST_MEMBERSHIP_REPORT: 339 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_REPORTS); 340 1.10 mycroft 341 1.10 mycroft if (ifp->if_flags & IFF_LOOPBACK) 342 1.10 mycroft break; 343 1.10 mycroft 344 1.12 mycroft if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 345 1.16 mycroft !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 346 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); 347 1.58 ozaki goto drop; 348 1.1 hpeyerl } 349 1.1 hpeyerl 350 1.1 hpeyerl /* 351 1.10 mycroft * KLUDGE: if the IP source address of the report has an 352 1.10 mycroft * unspecified (i.e., zero) subnet number, as is allowed for 353 1.10 mycroft * a booting host, replace it with the correct subnet number 354 1.10 mycroft * so that a process-level multicast routing daemon can 355 1.10 mycroft * determine which subnet it arrived from. This is necessary 356 1.10 mycroft * to compensate for the lack of any way for a process to 357 1.10 mycroft * determine the arrival interface of an incoming packet. 358 1.1 hpeyerl */ 359 1.12 mycroft if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 360 1.62 ozaki int s = pserialize_read_enter(); 361 1.62 ozaki ia = in_get_ia_from_ifp(ifp); /* XXX */ 362 1.10 mycroft if (ia) 363 1.12 mycroft ip->ip_src.s_addr = ia->ia_subnet; 364 1.62 ozaki pserialize_read_exit(s); 365 1.10 mycroft } 366 1.10 mycroft 367 1.10 mycroft /* 368 1.10 mycroft * If we belong to the group being reported, stop 369 1.10 mycroft * our timer for that group. 370 1.10 mycroft */ 371 1.55 rmind in_multi_lock(RW_WRITER); 372 1.55 rmind inm = in_lookup_multi(igmp->igmp_group, ifp); 373 1.10 mycroft if (inm != NULL) { 374 1.10 mycroft inm->inm_timer = 0; 375 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); 376 1.10 mycroft 377 1.10 mycroft switch (inm->inm_state) { 378 1.10 mycroft case IGMP_IDLE_MEMBER: 379 1.10 mycroft case IGMP_LAZY_MEMBER: 380 1.10 mycroft case IGMP_AWAKENING_MEMBER: 381 1.10 mycroft case IGMP_SLEEPING_MEMBER: 382 1.10 mycroft inm->inm_state = IGMP_SLEEPING_MEMBER; 383 1.10 mycroft break; 384 1.10 mycroft case IGMP_DELAYING_MEMBER: 385 1.10 mycroft if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 386 1.10 mycroft inm->inm_state = IGMP_LAZY_MEMBER; 387 1.10 mycroft else 388 1.10 mycroft inm->inm_state = IGMP_SLEEPING_MEMBER; 389 1.10 mycroft break; 390 1.1 hpeyerl } 391 1.1 hpeyerl } 392 1.55 rmind in_multi_unlock(); 393 1.1 hpeyerl break; 394 1.1 hpeyerl 395 1.62 ozaki case IGMP_v2_HOST_MEMBERSHIP_REPORT: { 396 1.62 ozaki int s = pserialize_read_enter(); 397 1.10 mycroft #ifdef MROUTING 398 1.10 mycroft /* 399 1.10 mycroft * Make sure we don't hear our own membership report. Fast 400 1.10 mycroft * leave requires knowing that we are the only member of a 401 1.10 mycroft * group. 402 1.10 mycroft */ 403 1.62 ozaki ia = in_get_ia_from_ifp(ifp); /* XXX */ 404 1.62 ozaki if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) { 405 1.62 ozaki pserialize_read_exit(s); 406 1.10 mycroft break; 407 1.62 ozaki } 408 1.10 mycroft #endif 409 1.10 mycroft 410 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_REPORTS); 411 1.1 hpeyerl 412 1.62 ozaki if (ifp->if_flags & IFF_LOOPBACK) { 413 1.62 ozaki pserialize_read_exit(s); 414 1.1 hpeyerl break; 415 1.62 ozaki } 416 1.1 hpeyerl 417 1.12 mycroft if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 418 1.16 mycroft !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 419 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); 420 1.62 ozaki pserialize_read_exit(s); 421 1.58 ozaki goto drop; 422 1.1 hpeyerl } 423 1.1 hpeyerl 424 1.1 hpeyerl /* 425 1.1 hpeyerl * KLUDGE: if the IP source address of the report has an 426 1.1 hpeyerl * unspecified (i.e., zero) subnet number, as is allowed for 427 1.1 hpeyerl * a booting host, replace it with the correct subnet number 428 1.10 mycroft * so that a process-level multicast routing daemon can 429 1.1 hpeyerl * determine which subnet it arrived from. This is necessary 430 1.1 hpeyerl * to compensate for the lack of any way for a process to 431 1.1 hpeyerl * determine the arrival interface of an incoming packet. 432 1.1 hpeyerl */ 433 1.12 mycroft if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 434 1.10 mycroft #ifndef MROUTING 435 1.62 ozaki ia = in_get_ia_from_ifp(ifp); /* XXX */ 436 1.10 mycroft #endif 437 1.10 mycroft if (ia) 438 1.12 mycroft ip->ip_src.s_addr = ia->ia_subnet; 439 1.1 hpeyerl } 440 1.62 ozaki pserialize_read_exit(s); 441 1.1 hpeyerl 442 1.1 hpeyerl /* 443 1.1 hpeyerl * If we belong to the group being reported, stop 444 1.1 hpeyerl * our timer for that group. 445 1.1 hpeyerl */ 446 1.55 rmind in_multi_lock(RW_WRITER); 447 1.55 rmind inm = in_lookup_multi(igmp->igmp_group, ifp); 448 1.1 hpeyerl if (inm != NULL) { 449 1.1 hpeyerl inm->inm_timer = 0; 450 1.46 thorpej IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); 451 1.10 mycroft 452 1.10 mycroft switch (inm->inm_state) { 453 1.10 mycroft case IGMP_DELAYING_MEMBER: 454 1.10 mycroft case IGMP_IDLE_MEMBER: 455 1.10 mycroft case IGMP_AWAKENING_MEMBER: 456 1.10 mycroft inm->inm_state = IGMP_LAZY_MEMBER; 457 1.10 mycroft break; 458 1.10 mycroft case IGMP_LAZY_MEMBER: 459 1.10 mycroft case IGMP_SLEEPING_MEMBER: 460 1.10 mycroft break; 461 1.10 mycroft } 462 1.1 hpeyerl } 463 1.55 rmind in_multi_unlock(); 464 1.1 hpeyerl break; 465 1.62 ozaki } 466 1.1 hpeyerl } 467 1.58 ozaki m_put_rcvif_psref(ifp, &psref); 468 1.1 hpeyerl 469 1.1 hpeyerl /* 470 1.1 hpeyerl * Pass all valid IGMP packets up to any process(es) listening 471 1.1 hpeyerl * on a raw IGMP socket. 472 1.1 hpeyerl */ 473 1.68 knakahar /* 474 1.68 knakahar * Currently, igmp_input() is always called holding softnet_lock 475 1.68 knakahar * by ipintr()(!NET_MPSAFE) or PR_INPUT_WRAP()(NET_MPSAFE). 476 1.68 knakahar */ 477 1.68 knakahar KASSERT(mutex_owned(softnet_lock)); 478 1.21 itojun rip_input(m, iphlen, proto); 479 1.21 itojun return; 480 1.58 ozaki 481 1.58 ozaki drop: 482 1.58 ozaki m_put_rcvif_psref(ifp, &psref); 483 1.58 ozaki m_freem(m); 484 1.58 ozaki return; 485 1.1 hpeyerl } 486 1.1 hpeyerl 487 1.33 matt int 488 1.41 perry igmp_joingroup(struct in_multi *inm) 489 1.1 hpeyerl { 490 1.55 rmind KASSERT(in_multi_lock_held()); 491 1.10 mycroft inm->inm_state = IGMP_IDLE_MEMBER; 492 1.1 hpeyerl 493 1.10 mycroft if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 494 1.10 mycroft (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) { 495 1.55 rmind int report_type; 496 1.55 rmind 497 1.33 matt report_type = rti_fill(inm); 498 1.39 christos if (report_type == 0) { 499 1.33 matt return ENOMEM; 500 1.39 christos } 501 1.33 matt igmp_sendpkt(inm, report_type); 502 1.10 mycroft inm->inm_state = IGMP_DELAYING_MEMBER; 503 1.10 mycroft inm->inm_timer = IGMP_RANDOM_DELAY( 504 1.10 mycroft IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 505 1.55 rmind igmp_timers_on = true; 506 1.10 mycroft } else 507 1.1 hpeyerl inm->inm_timer = 0; 508 1.55 rmind 509 1.33 matt return 0; 510 1.1 hpeyerl } 511 1.1 hpeyerl 512 1.1 hpeyerl void 513 1.41 perry igmp_leavegroup(struct in_multi *inm) 514 1.1 hpeyerl { 515 1.55 rmind KASSERT(in_multi_lock_held()); 516 1.10 mycroft 517 1.10 mycroft switch (inm->inm_state) { 518 1.10 mycroft case IGMP_DELAYING_MEMBER: 519 1.10 mycroft case IGMP_IDLE_MEMBER: 520 1.10 mycroft if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 521 1.10 mycroft (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) 522 1.10 mycroft if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 523 1.10 mycroft igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 524 1.10 mycroft break; 525 1.10 mycroft case IGMP_LAZY_MEMBER: 526 1.10 mycroft case IGMP_AWAKENING_MEMBER: 527 1.10 mycroft case IGMP_SLEEPING_MEMBER: 528 1.10 mycroft break; 529 1.10 mycroft } 530 1.1 hpeyerl } 531 1.1 hpeyerl 532 1.1 hpeyerl void 533 1.41 perry igmp_fasttimo(void) 534 1.1 hpeyerl { 535 1.24 augustss struct in_multi *inm; 536 1.1 hpeyerl struct in_multistep step; 537 1.1 hpeyerl 538 1.1 hpeyerl /* 539 1.1 hpeyerl * Quick check to see if any work needs to be done, in order 540 1.1 hpeyerl * to minimize the overhead of fasttimo processing. 541 1.1 hpeyerl */ 542 1.55 rmind if (!igmp_timers_on) { 543 1.1 hpeyerl return; 544 1.55 rmind } 545 1.1 hpeyerl 546 1.55 rmind /* XXX: Needed for ip_output(). */ 547 1.65 ozaki SOFTNET_LOCK_UNLESS_NET_MPSAFE(); 548 1.48 ad 549 1.55 rmind in_multi_lock(RW_WRITER); 550 1.55 rmind igmp_timers_on = false; 551 1.55 rmind inm = in_first_multi(&step); 552 1.1 hpeyerl while (inm != NULL) { 553 1.1 hpeyerl if (inm->inm_timer == 0) { 554 1.1 hpeyerl /* do nothing */ 555 1.1 hpeyerl } else if (--inm->inm_timer == 0) { 556 1.10 mycroft if (inm->inm_state == IGMP_DELAYING_MEMBER) { 557 1.10 mycroft if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 558 1.10 mycroft igmp_sendpkt(inm, 559 1.10 mycroft IGMP_v1_HOST_MEMBERSHIP_REPORT); 560 1.10 mycroft else 561 1.10 mycroft igmp_sendpkt(inm, 562 1.10 mycroft IGMP_v2_HOST_MEMBERSHIP_REPORT); 563 1.10 mycroft inm->inm_state = IGMP_IDLE_MEMBER; 564 1.10 mycroft } 565 1.1 hpeyerl } else { 566 1.55 rmind igmp_timers_on = true; 567 1.1 hpeyerl } 568 1.55 rmind inm = in_next_multi(&step); 569 1.1 hpeyerl } 570 1.55 rmind in_multi_unlock(); 571 1.65 ozaki SOFTNET_UNLOCK_UNLESS_NET_MPSAFE(); 572 1.1 hpeyerl } 573 1.1 hpeyerl 574 1.10 mycroft void 575 1.41 perry igmp_slowtimo(void) 576 1.10 mycroft { 577 1.55 rmind router_info_t *rti; 578 1.10 mycroft 579 1.55 rmind in_multi_lock(RW_WRITER); 580 1.33 matt LIST_FOREACH(rti, &rti_head, rti_link) { 581 1.10 mycroft if (rti->rti_type == IGMP_v1_ROUTER && 582 1.10 mycroft ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 583 1.10 mycroft rti->rti_type = IGMP_v2_ROUTER; 584 1.10 mycroft } 585 1.10 mycroft } 586 1.55 rmind in_multi_unlock(); 587 1.10 mycroft } 588 1.10 mycroft 589 1.55 rmind /* 590 1.55 rmind * igmp_sendpkt: construct an IGMP packet, given the multicast structure 591 1.55 rmind * and the type, and send the datagram. 592 1.55 rmind */ 593 1.55 rmind static void 594 1.41 perry igmp_sendpkt(struct in_multi *inm, int type) 595 1.1 hpeyerl { 596 1.10 mycroft struct mbuf *m; 597 1.10 mycroft struct igmp *igmp; 598 1.10 mycroft struct ip *ip; 599 1.10 mycroft struct ip_moptions imo; 600 1.55 rmind 601 1.55 rmind KASSERT(in_multi_lock_held()); 602 1.1 hpeyerl 603 1.1 hpeyerl MGETHDR(m, M_DONTWAIT, MT_HEADER); 604 1.1 hpeyerl if (m == NULL) 605 1.1 hpeyerl return; 606 1.67 maxv KASSERT(max_linkhdr + sizeof(struct ip) + IGMP_MINLEN <= MHLEN); 607 1.55 rmind 608 1.1 hpeyerl m->m_data += max_linkhdr; 609 1.1 hpeyerl m->m_len = sizeof(struct ip) + IGMP_MINLEN; 610 1.1 hpeyerl m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 611 1.1 hpeyerl 612 1.1 hpeyerl ip = mtod(m, struct ip *); 613 1.1 hpeyerl ip->ip_tos = 0; 614 1.31 itojun ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 615 1.31 itojun ip->ip_off = htons(0); 616 1.70 maxv ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; 617 1.1 hpeyerl ip->ip_p = IPPROTO_IGMP; 618 1.16 mycroft ip->ip_src = zeroin_addr; 619 1.1 hpeyerl ip->ip_dst = inm->inm_addr; 620 1.1 hpeyerl 621 1.7 brezak m->m_data += sizeof(struct ip); 622 1.7 brezak m->m_len -= sizeof(struct ip); 623 1.7 brezak igmp = mtod(m, struct igmp *); 624 1.10 mycroft igmp->igmp_type = type; 625 1.1 hpeyerl igmp->igmp_code = 0; 626 1.1 hpeyerl igmp->igmp_group = inm->inm_addr; 627 1.1 hpeyerl igmp->igmp_cksum = 0; 628 1.1 hpeyerl igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 629 1.7 brezak m->m_data -= sizeof(struct ip); 630 1.7 brezak m->m_len += sizeof(struct ip); 631 1.1 hpeyerl 632 1.59 ozaki imo.imo_multicast_if_index = if_get_index(inm->inm_ifp); 633 1.10 mycroft imo.imo_multicast_ttl = 1; 634 1.66 maxv 635 1.1 hpeyerl /* 636 1.1 hpeyerl * Request loopback of the report if we are acting as a multicast 637 1.1 hpeyerl * router, so that the process-level routing demon can hear it. 638 1.1 hpeyerl */ 639 1.1 hpeyerl #ifdef MROUTING 640 1.55 rmind extern struct socket *ip_mrouter; 641 1.10 mycroft imo.imo_multicast_loop = (ip_mrouter != NULL); 642 1.10 mycroft #else 643 1.10 mycroft imo.imo_multicast_loop = 0; 644 1.55 rmind #endif 645 1.1 hpeyerl 646 1.55 rmind /* 647 1.55 rmind * Note: IP_IGMP_MCAST indicates that in_multilock is held. 648 1.55 rmind * The caller must still acquire softnet_lock for ip_output(). 649 1.55 rmind */ 650 1.64 ozaki #ifndef NET_MPSAFE 651 1.55 rmind KASSERT(mutex_owned(softnet_lock)); 652 1.64 ozaki #endif 653 1.55 rmind ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL); 654 1.46 thorpej IGMP_STATINC(IGMP_STAT_SND_REPORTS); 655 1.34 itojun } 656 1.34 itojun 657 1.34 itojun void 658 1.55 rmind igmp_purgeif(ifnet_t *ifp) 659 1.34 itojun { 660 1.55 rmind in_multi_lock(RW_WRITER); 661 1.55 rmind rti_delete(ifp); 662 1.55 rmind in_multi_unlock(); 663 1.1 hpeyerl } 664 1.46 thorpej 665 1.46 thorpej static int 666 1.46 thorpej sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS) 667 1.46 thorpej { 668 1.55 rmind return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS); 669 1.46 thorpej } 670 1.46 thorpej 671 1.51 pooka static void 672 1.51 pooka sysctl_net_inet_igmp_setup(struct sysctllog **clog) 673 1.46 thorpej { 674 1.46 thorpej sysctl_createv(clog, 0, NULL, NULL, 675 1.46 thorpej CTLFLAG_PERMANENT, 676 1.46 thorpej CTLTYPE_NODE, "inet", NULL, 677 1.46 thorpej NULL, 0, NULL, 0, 678 1.46 thorpej CTL_NET, PF_INET, CTL_EOL); 679 1.46 thorpej sysctl_createv(clog, 0, NULL, NULL, 680 1.46 thorpej CTLFLAG_PERMANENT, 681 1.46 thorpej CTLTYPE_NODE, "igmp", 682 1.46 thorpej SYSCTL_DESCR("Internet Group Management Protocol"), 683 1.46 thorpej NULL, 0, NULL, 0, 684 1.46 thorpej CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL); 685 1.46 thorpej sysctl_createv(clog, 0, NULL, NULL, 686 1.46 thorpej CTLFLAG_PERMANENT, 687 1.46 thorpej CTLTYPE_STRUCT, "stats", 688 1.46 thorpej SYSCTL_DESCR("IGMP statistics"), 689 1.46 thorpej sysctl_net_inet_igmp_stats, 0, NULL, 0, 690 1.46 thorpej CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL); 691 1.46 thorpej } 692