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