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