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