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 */ 39 40 /* Internet Group Management Protocol (IGMP) routines. */ 41 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/mbuf.h> 46 #include <sys/socket.h> 47 #include <sys/protosw.h> 48 49 #include <net/if.h> 50 #include <net/route.h> 51 52 #include <netinet/in.h> 53 #include <netinet/in_var.h> 54 #include <netinet/in_systm.h> 55 #include <netinet/ip.h> 56 #include <netinet/ip_var.h> 57 #include <netinet/igmp.h> 58 #include <netinet/igmp_var.h> 59 60 extern struct ifnet loif; 61 62 static int igmp_timers_are_running = 0; 63 static u_long igmp_all_hosts_group; 64 65 static void igmp_sendreport __P((struct in_multi *)); 66 67 void 68 igmp_init() 69 { 70 /* 71 * To avoid byte-swapping the same value over and over again. 72 */ 73 igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 74 } 75 76 void 77 igmp_input(m, iphlen) 78 register struct mbuf *m; 79 register int iphlen; 80 { 81 register struct igmp *igmp; 82 register struct ip *ip; 83 register int igmplen; 84 register struct ifnet *ifp = m->m_pkthdr.rcvif; 85 register int minlen; 86 register struct in_multi *inm; 87 register struct in_ifaddr *ia; 88 struct in_multistep step; 89 90 ++igmpstat.igps_rcv_total; 91 92 ip = mtod(m, struct ip *); 93 igmplen = ip->ip_len; 94 95 /* 96 * Validate lengths 97 */ 98 if (igmplen < IGMP_MINLEN) { 99 ++igmpstat.igps_rcv_tooshort; 100 m_freem(m); 101 return; 102 } 103 minlen = iphlen + IGMP_MINLEN; 104 if ((m->m_flags & M_EXT || m->m_len < minlen) && 105 (m = m_pullup(m, minlen)) == 0) { 106 ++igmpstat.igps_rcv_tooshort; 107 return; 108 } 109 110 /* 111 * Validate checksum 112 */ 113 m->m_data += iphlen; 114 m->m_len -= iphlen; 115 igmp = mtod(m, struct igmp *); 116 if (in_cksum(m, igmplen)) { 117 ++igmpstat.igps_rcv_badsum; 118 m_freem(m); 119 return; 120 } 121 m->m_data -= iphlen; 122 m->m_len += iphlen; 123 ip = mtod(m, struct ip *); 124 125 switch (igmp->igmp_type) { 126 127 case IGMP_HOST_MEMBERSHIP_QUERY: 128 ++igmpstat.igps_rcv_queries; 129 130 if (ifp == &loif) 131 break; 132 133 if (ip->ip_dst.s_addr != igmp_all_hosts_group) { 134 ++igmpstat.igps_rcv_badqueries; 135 m_freem(m); 136 return; 137 } 138 139 /* 140 * Start the timers in all of our membership records for 141 * the interface on which the query arrived, except those 142 * that are already running and those that belong to the 143 * "all-hosts" group. 144 */ 145 IN_FIRST_MULTI(step, inm); 146 while (inm != NULL) { 147 if (inm->inm_ifp == ifp && inm->inm_timer == 0 && 148 inm->inm_addr.s_addr != igmp_all_hosts_group) { 149 inm->inm_timer = 150 IGMP_RANDOM_DELAY(inm->inm_addr); 151 igmp_timers_are_running = 1; 152 } 153 IN_NEXT_MULTI(step, inm); 154 } 155 156 break; 157 158 case IGMP_HOST_MEMBERSHIP_REPORT: 159 ++igmpstat.igps_rcv_reports; 160 161 if (ifp == &loif) 162 break; 163 164 if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 165 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 166 ++igmpstat.igps_rcv_badreports; 167 m_freem(m); 168 return; 169 } 170 171 /* 172 * KLUDGE: if the IP source address of the report has an 173 * unspecified (i.e., zero) subnet number, as is allowed for 174 * a booting host, replace it with the correct subnet number 175 * so that a process-level multicast routing demon can 176 * determine which subnet it arrived from. This is necessary 177 * to compensate for the lack of any way for a process to 178 * determine the arrival interface of an incoming packet. 179 */ 180 if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { 181 IFP_TO_IA(ifp, ia); 182 if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 183 } 184 185 /* 186 * If we belong to the group being reported, stop 187 * our timer for that group. 188 */ 189 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 190 if (inm != NULL) { 191 inm->inm_timer = 0; 192 ++igmpstat.igps_rcv_ourreports; 193 } 194 195 break; 196 } 197 198 /* 199 * Pass all valid IGMP packets up to any process(es) listening 200 * on a raw IGMP socket. 201 */ 202 rip_input(m); 203 } 204 205 void 206 igmp_joingroup(inm) 207 struct in_multi *inm; 208 { 209 register int s = splnet(); 210 211 if (inm->inm_addr.s_addr == igmp_all_hosts_group || 212 inm->inm_ifp == &loif) 213 inm->inm_timer = 0; 214 else { 215 igmp_sendreport(inm); 216 inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr); 217 igmp_timers_are_running = 1; 218 } 219 splx(s); 220 } 221 222 void 223 igmp_leavegroup(inm) 224 struct in_multi *inm; 225 { 226 /* 227 * No action required on leaving a group. 228 */ 229 } 230 231 void 232 igmp_fasttimo() 233 { 234 register struct in_multi *inm; 235 register int s; 236 struct in_multistep step; 237 238 /* 239 * Quick check to see if any work needs to be done, in order 240 * to minimize the overhead of fasttimo processing. 241 */ 242 if (!igmp_timers_are_running) 243 return; 244 245 s = splnet(); 246 igmp_timers_are_running = 0; 247 IN_FIRST_MULTI(step, inm); 248 while (inm != NULL) { 249 if (inm->inm_timer == 0) { 250 /* do nothing */ 251 } else if (--inm->inm_timer == 0) { 252 igmp_sendreport(inm); 253 } else { 254 igmp_timers_are_running = 1; 255 } 256 IN_NEXT_MULTI(step, inm); 257 } 258 splx(s); 259 } 260 261 static void 262 igmp_sendreport(inm) 263 register struct in_multi *inm; 264 { 265 register struct mbuf *m; 266 register struct igmp *igmp; 267 register struct ip *ip; 268 register struct ip_moptions *imo; 269 struct ip_moptions simo; 270 271 MGETHDR(m, M_DONTWAIT, MT_HEADER); 272 if (m == NULL) 273 return; 274 /* 275 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 276 * is smaller than mbuf size returned by MGETHDR. 277 */ 278 m->m_data += max_linkhdr; 279 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 280 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 281 282 ip = mtod(m, struct ip *); 283 ip->ip_tos = 0; 284 ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 285 ip->ip_off = 0; 286 ip->ip_p = IPPROTO_IGMP; 287 ip->ip_src.s_addr = INADDR_ANY; 288 ip->ip_dst = inm->inm_addr; 289 290 igmp = (struct igmp *)(ip + 1); 291 igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT; 292 igmp->igmp_code = 0; 293 igmp->igmp_group = inm->inm_addr; 294 igmp->igmp_cksum = 0; 295 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 296 297 imo = &simo; 298 bzero((caddr_t)imo, sizeof(*imo)); 299 imo->imo_multicast_ifp = inm->inm_ifp; 300 imo->imo_multicast_ttl = 1; 301 /* 302 * Request loopback of the report if we are acting as a multicast 303 * router, so that the process-level routing demon can hear it. 304 */ 305 #ifdef MROUTING 306 { 307 extern struct socket *ip_mrouter; 308 imo->imo_multicast_loop = (ip_mrouter != NULL); 309 } 310 #endif 311 ip_output(m, NULL, NULL, 0, imo); 312 313 ++igmpstat.igps_snd_reports; 314 } 315