* Copyright (c) 1988 Stephen Deering.
* Copyright (c) 1992, 1993 Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Stephen Deering of Stanford University.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)igmp.c 7.2 (Berkeley) 10/11/92
/* Internet Group Management Protocol (IGMP) routines. */
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip_var.h>
#include <netinet/igmp.h>
#include <netinet/igmp_var.h>
extern struct ifnet loif
;
static int igmp_timers_are_running
= 0;
static u_long igmp_all_hosts_group
;
static void igmp_sendreport
__P((struct in_multi
*));
* To avoid byte-swapping the same value over and over again.
igmp_all_hosts_group
= htonl(INADDR_ALLHOSTS_GROUP
);
register struct igmp
*igmp
;
register struct ifnet
*ifp
= m
->m_pkthdr
.rcvif
;
register struct in_multi
*inm
;
register struct in_ifaddr
*ia
;
struct in_multistep step
;
++igmpstat
.igps_rcv_total
;
ip
= mtod(m
, struct ip
*);
if (igmplen
< IGMP_MINLEN
) {
++igmpstat
.igps_rcv_tooshort
;
minlen
= iphlen
+ IGMP_MINLEN
;
if ((m
->m_flags
& M_EXT
|| m
->m_len
< minlen
) &&
(m
= m_pullup(m
, minlen
)) == 0) {
++igmpstat
.igps_rcv_tooshort
;
igmp
= mtod(m
, struct igmp
*);
if (in_cksum(m
, igmplen
)) {
++igmpstat
.igps_rcv_badsum
;
ip
= mtod(m
, struct ip
*);
switch (igmp
->igmp_type
) {
case IGMP_HOST_MEMBERSHIP_QUERY
:
++igmpstat
.igps_rcv_queries
;
if (ip
->ip_dst
.s_addr
!= igmp_all_hosts_group
) {
++igmpstat
.igps_rcv_badqueries
;
* Start the timers in all of our membership records for
* the interface on which the query arrived, except those
* that are already running and those that belong to the
IN_FIRST_MULTI(step
, inm
);
if (inm
->inm_ifp
== ifp
&& inm
->inm_timer
== 0 &&
inm
->inm_addr
.s_addr
!= igmp_all_hosts_group
) {
IGMP_RANDOM_DELAY(inm
->inm_addr
);
igmp_timers_are_running
= 1;
IN_NEXT_MULTI(step
, inm
);
case IGMP_HOST_MEMBERSHIP_REPORT
:
++igmpstat
.igps_rcv_reports
;
if (!IN_MULTICAST(ntohl(igmp
->igmp_group
.s_addr
)) ||
igmp
->igmp_group
.s_addr
!= ip
->ip_dst
.s_addr
) {
++igmpstat
.igps_rcv_badreports
;
* KLUDGE: if the IP source address of the report has an
* unspecified (i.e., zero) subnet number, as is allowed for
* a booting host, replace it with the correct subnet number
* so that a process-level multicast routing demon can
* determine which subnet it arrived from. This is necessary
* to compensate for the lack of any way for a process to
* determine the arrival interface of an incoming packet.
if ((ntohl(ip
->ip_src
.s_addr
) & IN_CLASSA_NET
) == 0) {
if (ia
) ip
->ip_src
.s_addr
= htonl(ia
->ia_subnet
);
* If we belong to the group being reported, stop
* our timer for that group.
IN_LOOKUP_MULTI(igmp
->igmp_group
, ifp
, inm
);
++igmpstat
.igps_rcv_ourreports
;
* Pass all valid IGMP packets up to any process(es) listening
register int s
= splnet();
if (inm
->inm_addr
.s_addr
== igmp_all_hosts_group
||
inm
->inm_timer
= IGMP_RANDOM_DELAY(inm
->inm_addr
);
igmp_timers_are_running
= 1;
* No action required on leaving a group.
register struct in_multi
*inm
;
struct in_multistep step
;
* Quick check to see if any work needs to be done, in order
* to minimize the overhead of fasttimo processing.
if (!igmp_timers_are_running
)
igmp_timers_are_running
= 0;
IN_FIRST_MULTI(step
, inm
);
if (inm
->inm_timer
== 0) {
} else if (--inm
->inm_timer
== 0) {
igmp_timers_are_running
= 1;
IN_NEXT_MULTI(step
, inm
);
register struct in_multi
*inm
;
register struct igmp
*igmp
;
register struct ip_moptions
*imo
;
extern struct socket
*ip_mrouter
;
MGETHDR(m
, M_DONTWAIT
, MT_HEADER
);
* Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
* is smaller than mbuf size returned by MGETHDR.
m
->m_data
+= max_linkhdr
;
m
->m_len
= sizeof(struct ip
) + IGMP_MINLEN
;
m
->m_pkthdr
.len
= sizeof(struct ip
) + IGMP_MINLEN
;
ip
= mtod(m
, struct ip
*);
ip
->ip_len
= sizeof(struct ip
) + IGMP_MINLEN
;
ip
->ip_src
.s_addr
= INADDR_ANY
;
ip
->ip_dst
= inm
->inm_addr
;
m
->m_data
+= sizeof(struct ip
);
m
->m_len
-= sizeof(struct ip
);
igmp
= mtod(m
, struct igmp
*);
igmp
->igmp_type
= IGMP_HOST_MEMBERSHIP_REPORT
;
igmp
->igmp_group
= inm
->inm_addr
;
igmp
->igmp_cksum
= in_cksum(m
, IGMP_MINLEN
);
m
->m_data
-= sizeof(struct ip
);
m
->m_len
+= sizeof(struct ip
);
bzero((caddr_t
)imo
, sizeof(*imo
));
imo
->imo_multicast_ifp
= inm
->inm_ifp
;
imo
->imo_multicast_ttl
= 1;
* Request loopback of the report if we are acting as a multicast
* router, so that the process-level routing demon can hear it.
imo
->imo_multicast_loop
= (ip_mrouter
!= NULL
);
imo
->imo_multicast_loop
= 0;
ip_output(m
, NULL
, NULL
, IP_MULTICASTOPTS
, imo
);
++igmpstat
.igps_snd_reports
;