154819Ssklower /* 254819Ssklower * Copyright (c) 1988 Stephen Deering. 354819Ssklower * Copyright (c) 1992 Regents of the University of California. 454819Ssklower * All rights reserved. 554819Ssklower * 654819Ssklower * This code is derived from software contributed to Berkeley by 754819Ssklower * Stephen Deering of Stanford University. 854819Ssklower * 954819Ssklower * %sccs.include.redist.c% 1054819Ssklower * 11*57946Ssklower * @(#)igmp.c 7.4 (Berkeley) 02/12/93 1254819Ssklower */ 1354819Ssklower 1454819Ssklower /* Internet Group Management Protocol (IGMP) routines. */ 1554819Ssklower 1654819Ssklower 1756531Sbostic #include <sys/param.h> 1856531Sbostic #include <sys/mbuf.h> 1956531Sbostic #include <sys/socket.h> 2056531Sbostic #include <sys/protosw.h> 2154819Ssklower 2256531Sbostic #include <net/if.h> 2356531Sbostic #include <net/route.h> 2454819Ssklower 2556531Sbostic #include <netinet/in.h> 2656531Sbostic #include <netinet/in_var.h> 2756531Sbostic #include <netinet/in_systm.h> 2856531Sbostic #include <netinet/ip.h> 2956531Sbostic #include <netinet/ip_var.h> 3056531Sbostic #include <netinet/igmp.h> 3156531Sbostic #include <netinet/igmp_var.h> 3254819Ssklower 3354819Ssklower extern struct ifnet loif; 3454819Ssklower 3554819Ssklower static int igmp_timers_are_running = 0; 3654819Ssklower static u_long igmp_all_hosts_group; 3754819Ssklower 3854819Ssklower static void igmp_sendreport __P((struct in_multi *)); 3954819Ssklower 4054819Ssklower void 4154819Ssklower igmp_init() 4254819Ssklower { 4354819Ssklower /* 4454819Ssklower * To avoid byte-swapping the same value over and over again. 4554819Ssklower */ 4654819Ssklower igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 4754819Ssklower } 4854819Ssklower 4954819Ssklower void 5054819Ssklower igmp_input(m, ifp) 5154819Ssklower register struct mbuf *m; 5254819Ssklower register struct ifnet *ifp; 5354819Ssklower { 5454819Ssklower register struct igmp *igmp; 5554819Ssklower register struct ip *ip; 5654819Ssklower register int igmplen; 5754819Ssklower register int iphlen; 5854819Ssklower register int minlen; 5954819Ssklower register struct in_multi *inm; 6054819Ssklower register struct in_ifaddr *ia; 6154819Ssklower struct in_multistep step; 6254819Ssklower 6354819Ssklower ++igmpstat.igps_rcv_total; 6454819Ssklower 6554819Ssklower ip = mtod(m, struct ip *); 6654819Ssklower iphlen = ip->ip_hl << 2; 6754819Ssklower igmplen = ip->ip_len; 6854819Ssklower 6954819Ssklower /* 7054819Ssklower * Validate lengths 7154819Ssklower */ 7254819Ssklower if (igmplen < IGMP_MINLEN) { 7354819Ssklower ++igmpstat.igps_rcv_tooshort; 7454819Ssklower m_freem(m); 7554819Ssklower return; 7654819Ssklower } 7754819Ssklower minlen = iphlen + IGMP_MINLEN; 7854819Ssklower if ((m->m_flags & M_EXT || m->m_len < minlen) && 7954819Ssklower (m = m_pullup(m, minlen)) == 0) { 8054819Ssklower ++igmpstat.igps_rcv_tooshort; 8154819Ssklower return; 8254819Ssklower } 8354819Ssklower 8454819Ssklower /* 8554819Ssklower * Validate checksum 8654819Ssklower */ 8754819Ssklower m->m_data += iphlen; 8854819Ssklower m->m_len -= iphlen; 8954819Ssklower igmp = mtod(m, struct igmp *); 9054819Ssklower if (in_cksum(m, igmplen)) { 9154819Ssklower ++igmpstat.igps_rcv_badsum; 9254819Ssklower m_freem(m); 9354819Ssklower return; 9454819Ssklower } 9554819Ssklower m->m_data -= iphlen; 9654819Ssklower m->m_len += iphlen; 9754819Ssklower ip = mtod(m, struct ip *); 9854819Ssklower 9954819Ssklower switch (igmp->igmp_type) { 10054819Ssklower 10154819Ssklower case IGMP_HOST_MEMBERSHIP_QUERY: 10254819Ssklower ++igmpstat.igps_rcv_queries; 10354819Ssklower 10454819Ssklower if (ifp == &loif) 10554819Ssklower break; 10654819Ssklower 10754819Ssklower if (ip->ip_dst.s_addr != igmp_all_hosts_group) { 10854819Ssklower ++igmpstat.igps_rcv_badqueries; 10954819Ssklower m_freem(m); 11054819Ssklower return; 11154819Ssklower } 11254819Ssklower 11354819Ssklower /* 11454819Ssklower * Start the timers in all of our membership records for 11554819Ssklower * the interface on which the query arrived, except those 11654819Ssklower * that are already running and those that belong to the 11754819Ssklower * "all-hosts" group. 11854819Ssklower */ 11954819Ssklower IN_FIRST_MULTI(step, inm); 12054819Ssklower while (inm != NULL) { 12154819Ssklower if (inm->inm_ifp == ifp && inm->inm_timer == 0 && 12254819Ssklower inm->inm_addr.s_addr != igmp_all_hosts_group) { 12354819Ssklower inm->inm_timer = 12454819Ssklower IGMP_RANDOM_DELAY(inm->inm_addr); 12554819Ssklower igmp_timers_are_running = 1; 12654819Ssklower } 12754819Ssklower IN_NEXT_MULTI(step, inm); 12854819Ssklower } 12954819Ssklower 13054819Ssklower break; 13154819Ssklower 13254819Ssklower case IGMP_HOST_MEMBERSHIP_REPORT: 13354819Ssklower ++igmpstat.igps_rcv_reports; 13454819Ssklower 13554819Ssklower if (ifp == &loif) 13654819Ssklower break; 13754819Ssklower 13854819Ssklower if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 13954819Ssklower igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 14054819Ssklower ++igmpstat.igps_rcv_badreports; 14154819Ssklower m_freem(m); 14254819Ssklower return; 14354819Ssklower } 14454819Ssklower 14554819Ssklower /* 14654819Ssklower * KLUDGE: if the IP source address of the report has an 14754819Ssklower * unspecified (i.e., zero) subnet number, as is allowed for 14854819Ssklower * a booting host, replace it with the correct subnet number 14954819Ssklower * so that a process-level multicast routing demon can 15054819Ssklower * determine which subnet it arrived from. This is necessary 15154819Ssklower * to compensate for the lack of any way for a process to 15254819Ssklower * determine the arrival interface of an incoming packet. 15354819Ssklower */ 15454819Ssklower if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { 15554819Ssklower IFP_TO_IA(ifp, ia); 15654819Ssklower if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 15754819Ssklower } 15854819Ssklower 15954819Ssklower /* 16054819Ssklower * If we belong to the group being reported, stop 16154819Ssklower * our timer for that group. 16254819Ssklower */ 16354819Ssklower IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 16454819Ssklower if (inm != NULL) { 16554819Ssklower inm->inm_timer = 0; 16654819Ssklower ++igmpstat.igps_rcv_ourreports; 16754819Ssklower } 16854819Ssklower 16954819Ssklower break; 17054819Ssklower } 17154819Ssklower 17254819Ssklower /* 17354819Ssklower * Pass all valid IGMP packets up to any process(es) listening 17454819Ssklower * on a raw IGMP socket. 17554819Ssklower */ 17654819Ssklower rip_input(m); 17754819Ssklower } 17854819Ssklower 17954819Ssklower void 18054819Ssklower igmp_joingroup(inm) 18154819Ssklower struct in_multi *inm; 18254819Ssklower { 18354819Ssklower register int s = splnet(); 18454819Ssklower 18554819Ssklower if (inm->inm_addr.s_addr == igmp_all_hosts_group || 18654819Ssklower inm->inm_ifp == &loif) 18754819Ssklower inm->inm_timer = 0; 18854819Ssklower else { 18954819Ssklower igmp_sendreport(inm); 19054819Ssklower inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr); 19154819Ssklower igmp_timers_are_running = 1; 19254819Ssklower } 19354819Ssklower splx(s); 19454819Ssklower } 19554819Ssklower 19654819Ssklower void 19754819Ssklower igmp_leavegroup(inm) 19854819Ssklower struct in_multi *inm; 19954819Ssklower { 20054819Ssklower /* 20154819Ssklower * No action required on leaving a group. 20254819Ssklower */ 20354819Ssklower } 20454819Ssklower 20554819Ssklower void 20654819Ssklower igmp_fasttimo() 20754819Ssklower { 20854819Ssklower register struct in_multi *inm; 20954819Ssklower register int s; 21054819Ssklower struct in_multistep step; 21154819Ssklower 21254819Ssklower /* 21354819Ssklower * Quick check to see if any work needs to be done, in order 21454819Ssklower * to minimize the overhead of fasttimo processing. 21554819Ssklower */ 21654819Ssklower if (!igmp_timers_are_running) 21754819Ssklower return; 21854819Ssklower 21954819Ssklower s = splnet(); 22054819Ssklower igmp_timers_are_running = 0; 22154819Ssklower IN_FIRST_MULTI(step, inm); 22254819Ssklower while (inm != NULL) { 22354819Ssklower if (inm->inm_timer == 0) { 22454819Ssklower /* do nothing */ 22554819Ssklower } else if (--inm->inm_timer == 0) { 22654819Ssklower igmp_sendreport(inm); 22754819Ssklower } else { 22854819Ssklower igmp_timers_are_running = 1; 22954819Ssklower } 23054819Ssklower IN_NEXT_MULTI(step, inm); 23154819Ssklower } 23254819Ssklower splx(s); 23354819Ssklower } 23454819Ssklower 23554819Ssklower static void 23654819Ssklower igmp_sendreport(inm) 23754819Ssklower register struct in_multi *inm; 23854819Ssklower { 23954819Ssklower register struct mbuf *m; 24054819Ssklower register struct igmp *igmp; 24154819Ssklower register struct ip *ip; 24254819Ssklower register struct ip_moptions *imo; 24354819Ssklower struct ip_moptions simo; 24454819Ssklower 24554819Ssklower MGETHDR(m, M_DONTWAIT, MT_HEADER); 24654819Ssklower if (m == NULL) 24754819Ssklower return; 24854819Ssklower m->m_data += max_linkhdr; 24954819Ssklower m->m_len = sizeof(struct ip) + IGMP_MINLEN; 25054819Ssklower 25154819Ssklower ip = mtod(m, struct ip *); 25254819Ssklower ip->ip_tos = 0; 25354819Ssklower ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 25454819Ssklower ip->ip_off = 0; 25554819Ssklower ip->ip_p = IPPROTO_IGMP; 25654819Ssklower ip->ip_src.s_addr = INADDR_ANY; 25754819Ssklower ip->ip_dst = inm->inm_addr; 25854819Ssklower 25954819Ssklower igmp = (struct igmp *)(ip + 1); 26054819Ssklower igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT; 26154819Ssklower igmp->igmp_code = 0; 26254819Ssklower igmp->igmp_group = inm->inm_addr; 26354819Ssklower igmp->igmp_cksum = 0; 26454819Ssklower igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 26554819Ssklower 26654819Ssklower imo = &simo; 26754819Ssklower bzero((caddr_t)imo, sizeof(*imo)); 26854819Ssklower imo->imo_multicast_ifp = inm->inm_ifp; 26954819Ssklower imo->imo_multicast_ttl = 1; 27054819Ssklower /* 27154819Ssklower * Request loopback of the report if we are acting as a multicast 27254819Ssklower * router, so that the process-level routing demon can hear it. 27354819Ssklower */ 274*57946Ssklower #ifdef MROUTING 275*57946Ssklower { 276*57946Ssklower extern struct socket *ip_mrouter; 27754819Ssklower imo->imo_multicast_loop = (ip_mrouter != NULL); 278*57946Ssklower } 279*57946Ssklower #endif 28054819Ssklower ip_output(m, NULL, NULL, 0, imo); 28154819Ssklower 28254819Ssklower ++igmpstat.igps_snd_reports; 28354819Ssklower } 284