1 /* $NetBSD: igmp.c,v 1.9 1995/04/13 06:26:19 cgd Exp $ */ 2 3 /* 4 * Copyright (c) 1988 Stephen Deering. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Stephen Deering of Stanford University. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * @(#)igmp.c 8.1 (Berkeley) 7/19/93 40 */ 41 42 /* Internet Group Management Protocol (IGMP) routines. */ 43 44 45 #include <sys/param.h> 46 #include <sys/mbuf.h> 47 #include <sys/socket.h> 48 #include <sys/protosw.h> 49 50 #include <net/if.h> 51 #include <net/route.h> 52 53 #include <netinet/in.h> 54 #include <netinet/in_var.h> 55 #include <netinet/in_systm.h> 56 #include <netinet/ip.h> 57 #include <netinet/ip_var.h> 58 #include <netinet/igmp.h> 59 #include <netinet/igmp_var.h> 60 61 static int igmp_timers_are_running = 0; 62 static u_int32_t igmp_all_hosts_group; 63 64 static void igmp_sendreport __P((struct in_multi *)); 65 66 void 67 igmp_init() 68 { 69 /* 70 * To avoid byte-swapping the same value over and over again. 71 */ 72 igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 73 } 74 75 void 76 igmp_input(m, iphlen) 77 register struct mbuf *m; 78 register int iphlen; 79 { 80 register struct igmp *igmp; 81 register struct ip *ip; 82 register int igmplen; 83 register struct ifnet *ifp = m->m_pkthdr.rcvif; 84 register int minlen; 85 register struct in_multi *inm; 86 register struct in_ifaddr *ia; 87 struct in_multistep step; 88 89 ++igmpstat.igps_rcv_total; 90 91 ip = mtod(m, struct ip *); 92 igmplen = ip->ip_len; 93 94 /* 95 * Validate lengths 96 */ 97 if (igmplen < IGMP_MINLEN) { 98 ++igmpstat.igps_rcv_tooshort; 99 m_freem(m); 100 return; 101 } 102 minlen = iphlen + IGMP_MINLEN; 103 if ((m->m_flags & M_EXT || m->m_len < minlen) && 104 (m = m_pullup(m, minlen)) == 0) { 105 ++igmpstat.igps_rcv_tooshort; 106 return; 107 } 108 109 /* 110 * Validate checksum 111 */ 112 m->m_data += iphlen; 113 m->m_len -= iphlen; 114 igmp = mtod(m, struct igmp *); 115 if (in_cksum(m, igmplen)) { 116 ++igmpstat.igps_rcv_badsum; 117 m_freem(m); 118 return; 119 } 120 m->m_data -= iphlen; 121 m->m_len += iphlen; 122 ip = mtod(m, struct ip *); 123 124 switch (igmp->igmp_type) { 125 126 case IGMP_HOST_MEMBERSHIP_QUERY: 127 ++igmpstat.igps_rcv_queries; 128 129 if (ifp->if_flags & IFF_LOOPBACK) 130 break; 131 132 if (ip->ip_dst.s_addr != igmp_all_hosts_group) { 133 ++igmpstat.igps_rcv_badqueries; 134 m_freem(m); 135 return; 136 } 137 138 /* 139 * Start the timers in all of our membership records for 140 * the interface on which the query arrived, except those 141 * that are already running and those that belong to the 142 * "all-hosts" group. 143 */ 144 IN_FIRST_MULTI(step, inm); 145 while (inm != NULL) { 146 if (inm->inm_ifp == ifp && inm->inm_timer == 0 && 147 inm->inm_addr.s_addr != igmp_all_hosts_group) { 148 inm->inm_timer = 149 IGMP_RANDOM_DELAY(inm->inm_addr); 150 igmp_timers_are_running = 1; 151 } 152 IN_NEXT_MULTI(step, inm); 153 } 154 155 break; 156 157 case IGMP_HOST_MEMBERSHIP_REPORT: 158 ++igmpstat.igps_rcv_reports; 159 160 if (ifp->if_flags & IFF_LOOPBACK) 161 break; 162 163 if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 164 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 165 ++igmpstat.igps_rcv_badreports; 166 m_freem(m); 167 return; 168 } 169 170 /* 171 * KLUDGE: if the IP source address of the report has an 172 * unspecified (i.e., zero) subnet number, as is allowed for 173 * a booting host, replace it with the correct subnet number 174 * so that a process-level multicast routing demon can 175 * determine which subnet it arrived from. This is necessary 176 * to compensate for the lack of any way for a process to 177 * determine the arrival interface of an incoming packet. 178 */ 179 if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { 180 IFP_TO_IA(ifp, ia); 181 if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 182 } 183 184 /* 185 * If we belong to the group being reported, stop 186 * our timer for that group. 187 */ 188 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 189 if (inm != NULL) { 190 inm->inm_timer = 0; 191 ++igmpstat.igps_rcv_ourreports; 192 } 193 194 break; 195 } 196 197 /* 198 * Pass all valid IGMP packets up to any process(es) listening 199 * on a raw IGMP socket. 200 */ 201 rip_input(m); 202 } 203 204 void 205 igmp_joingroup(inm) 206 struct in_multi *inm; 207 { 208 register int s = splnet(); 209 210 if (inm->inm_addr.s_addr == igmp_all_hosts_group || 211 (inm->inm_ifp->if_flags & IFF_LOOPBACK)) 212 inm->inm_timer = 0; 213 else { 214 igmp_sendreport(inm); 215 inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr); 216 igmp_timers_are_running = 1; 217 } 218 splx(s); 219 } 220 221 void 222 igmp_leavegroup(inm) 223 struct in_multi *inm; 224 { 225 /* 226 * No action required on leaving a group. 227 */ 228 } 229 230 void 231 igmp_fasttimo() 232 { 233 register struct in_multi *inm; 234 register int s; 235 struct in_multistep step; 236 237 /* 238 * Quick check to see if any work needs to be done, in order 239 * to minimize the overhead of fasttimo processing. 240 */ 241 if (!igmp_timers_are_running) 242 return; 243 244 s = splnet(); 245 igmp_timers_are_running = 0; 246 IN_FIRST_MULTI(step, inm); 247 while (inm != NULL) { 248 if (inm->inm_timer == 0) { 249 /* do nothing */ 250 } else if (--inm->inm_timer == 0) { 251 igmp_sendreport(inm); 252 } else { 253 igmp_timers_are_running = 1; 254 } 255 IN_NEXT_MULTI(step, inm); 256 } 257 splx(s); 258 } 259 260 static void 261 igmp_sendreport(inm) 262 register struct in_multi *inm; 263 { 264 register struct mbuf *m; 265 register struct igmp *igmp; 266 register struct ip *ip; 267 register struct ip_moptions *imo; 268 struct ip_moptions simo; 269 270 MGETHDR(m, M_DONTWAIT, MT_HEADER); 271 if (m == NULL) 272 return; 273 /* 274 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 275 * is smaller than mbuf size returned by MGETHDR. 276 */ 277 m->m_data += max_linkhdr; 278 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 279 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 280 281 ip = mtod(m, struct ip *); 282 ip->ip_tos = 0; 283 ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 284 ip->ip_off = 0; 285 ip->ip_p = IPPROTO_IGMP; 286 ip->ip_src.s_addr = INADDR_ANY; 287 ip->ip_dst = inm->inm_addr; 288 289 m->m_data += sizeof(struct ip); 290 m->m_len -= sizeof(struct ip); 291 igmp = mtod(m, struct igmp *); 292 igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT; 293 igmp->igmp_code = 0; 294 igmp->igmp_group = inm->inm_addr; 295 igmp->igmp_cksum = 0; 296 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 297 m->m_data -= sizeof(struct ip); 298 m->m_len += sizeof(struct ip); 299 300 imo = &simo; 301 bzero((caddr_t)imo, sizeof(*imo)); 302 imo->imo_multicast_ifp = inm->inm_ifp; 303 imo->imo_multicast_ttl = 1; 304 /* 305 * Request loopback of the report if we are acting as a multicast 306 * router, so that the process-level routing demon can hear it. 307 */ 308 #ifdef MROUTING 309 { 310 extern struct socket *ip_mrouter; 311 imo->imo_multicast_loop = (ip_mrouter != NULL); 312 } 313 #endif 314 ip_output(m, NULL, NULL, 0, imo); 315 316 ++igmpstat.igps_snd_reports; 317 } 318