* Copyright (c) 1988 Stephen Deering.
* Copyright (c) 1992 Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Stephen Deering of Stanford University.
* %sccs.include.redist.c%
* @(#)igmp.c 7.4 (Berkeley) %G%
/* 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 ifnet
*ifp
;
register struct igmp
*igmp
;
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
;
MGETHDR(m
, M_DONTWAIT
, MT_HEADER
);
m
->m_data
+= max_linkhdr
;
m
->m_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
;
igmp
= (struct igmp
*)(ip
+ 1);
igmp
->igmp_type
= IGMP_HOST_MEMBERSHIP_REPORT
;
igmp
->igmp_group
= inm
->inm_addr
;
igmp
->igmp_cksum
= in_cksum(m
, IGMP_MINLEN
);
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.
extern struct socket
*ip_mrouter
;
imo
->imo_multicast_loop
= (ip_mrouter
!= NULL
);
ip_output(m
, NULL
, NULL
, 0, imo
);
++igmpstat
.igps_snd_reports
;