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