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