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