1 /* $NetBSD: igmp.c,v 1.50 2009/09/13 18:45:11 pooka Exp $ */
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * Internet Group Management Protocol (IGMP) routines.
35 * Written by Steve Deering, Stanford, May 1988.
36 * Modified by Rosen Sharma, Stanford, Aug 1994.
37 * Modified by Bill Fenner, Xerox PARC, Feb 1995.
39 * MULTICAST Revision: 1.3
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.50 2009/09/13 18:45:11 pooka Exp $");
45 #include "opt_mrouting.h"
47 #include <sys/param.h>
49 #include <sys/socket.h>
50 #include <sys/socketvar.h>
51 #include <sys/protosw.h>
52 #include <sys/systm.h>
53 #include <sys/sysctl.h>
56 #include <net/route.h>
57 #include <net/net_stats.h>
59 #include <netinet/in.h>
60 #include <netinet/in_var.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63 #include <netinet/ip_var.h>
64 #include <netinet/igmp.h>
65 #include <netinet/igmp_var.h>
67 #include <machine/stdarg.h>
69 #define IP_MULTICASTOPTS 0
71 static struct pool igmp_rti_pool
;
73 static percpu_t
*igmpstat_percpu
;
75 #define IGMP_STATINC(x) _NET_STATINC(igmpstat_percpu, x)
77 int igmp_timers_are_running
;
78 static LIST_HEAD(, router_info
) rti_head
= LIST_HEAD_INITIALIZER(rti_head
);
80 void igmp_sendpkt(struct in_multi
*, int);
81 static int rti_fill(struct in_multi
*);
82 static struct router_info
*rti_find(struct ifnet
*);
83 static void rti_delete(struct ifnet
*);
85 static void sysctl_net_inet_igmp_setup(struct sysctllog
**);
88 rti_fill(struct in_multi
*inm
)
90 struct router_info
*rti
;
92 /* this function is called at splsoftnet() */
93 LIST_FOREACH(rti
, &rti_head
, rti_link
) {
94 if (rti
->rti_ifp
== inm
->inm_ifp
) {
96 if (rti
->rti_type
== IGMP_v1_ROUTER
)
97 return (IGMP_v1_HOST_MEMBERSHIP_REPORT
);
99 return (IGMP_v2_HOST_MEMBERSHIP_REPORT
);
103 rti
= pool_get(&igmp_rti_pool
, PR_NOWAIT
);
106 rti
->rti_ifp
= inm
->inm_ifp
;
107 rti
->rti_type
= IGMP_v2_ROUTER
;
108 LIST_INSERT_HEAD(&rti_head
, rti
, rti_link
);
110 return (IGMP_v2_HOST_MEMBERSHIP_REPORT
);
113 static struct router_info
*
114 rti_find(struct ifnet
*ifp
)
116 struct router_info
*rti
;
117 int s
= splsoftnet();
119 LIST_FOREACH(rti
, &rti_head
, rti_link
) {
120 if (rti
->rti_ifp
== ifp
)
124 rti
= pool_get(&igmp_rti_pool
, PR_NOWAIT
);
130 rti
->rti_type
= IGMP_v2_ROUTER
;
131 LIST_INSERT_HEAD(&rti_head
, rti
, rti_link
);
137 rti_delete(struct ifnet
*ifp
) /* MUST be called at splsoftnet */
139 struct router_info
*rti
;
141 LIST_FOREACH(rti
, &rti_head
, rti_link
) {
142 if (rti
->rti_ifp
== ifp
) {
143 LIST_REMOVE(rti
, rti_link
);
144 pool_put(&igmp_rti_pool
, rti
);
154 sysctl_net_inet_igmp_setup(NULL
);
155 pool_init(&igmp_rti_pool
, sizeof(struct router_info
), 0, 0, 0,
156 "igmppl", NULL
, IPL_SOFTNET
);
157 igmpstat_percpu
= percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS
);
161 igmp_input(struct mbuf
*m
, ...)
165 struct ifnet
*ifp
= m
->m_pkthdr
.rcvif
;
166 struct ip
*ip
= mtod(m
, struct ip
*);
169 struct in_multi
*inm
;
170 struct in_multistep step
;
171 struct router_info
*rti
;
172 struct in_ifaddr
*ia
;
178 iphlen
= va_arg(ap
, int);
179 proto
= va_arg(ap
, int);
182 IGMP_STATINC(IGMP_STAT_RCV_TOTAL
);
187 minlen
= iphlen
+ IGMP_MINLEN
;
188 ip_len
= ntohs(ip
->ip_len
);
189 if (ip_len
< minlen
) {
190 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT
);
194 if (((m
->m_flags
& M_EXT
) && (ip
->ip_src
.s_addr
& IN_CLASSA_NET
) == 0)
195 || m
->m_len
< minlen
) {
196 if ((m
= m_pullup(m
, minlen
)) == 0) {
197 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT
);
200 ip
= mtod(m
, struct ip
*);
208 igmp
= mtod(m
, struct igmp
*);
209 /* No need to assert alignment here. */
210 if (in_cksum(m
, ip_len
- iphlen
)) {
211 IGMP_STATINC(IGMP_STAT_RCV_BADSUM
);
218 switch (igmp
->igmp_type
) {
220 case IGMP_HOST_MEMBERSHIP_QUERY
:
221 IGMP_STATINC(IGMP_STAT_RCV_QUERIES
);
223 if (ifp
->if_flags
& IFF_LOOPBACK
)
226 if (igmp
->igmp_code
== 0) {
230 rti
->rti_type
= IGMP_v1_ROUTER
;
233 if (ip
->ip_dst
.s_addr
!= INADDR_ALLHOSTS_GROUP
) {
234 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES
);
240 * Start the timers in all of our membership records
241 * for the interface on which the query arrived,
242 * except those that are already running and those
243 * that belong to a "local" group (224.0.0.X).
245 IN_FIRST_MULTI(step
, inm
);
246 while (inm
!= NULL
) {
247 if (inm
->inm_ifp
== ifp
&&
248 inm
->inm_timer
== 0 &&
249 !IN_LOCAL_GROUP(inm
->inm_addr
.s_addr
)) {
250 inm
->inm_state
= IGMP_DELAYING_MEMBER
;
251 inm
->inm_timer
= IGMP_RANDOM_DELAY(
252 IGMP_MAX_HOST_REPORT_DELAY
* PR_FASTHZ
);
253 igmp_timers_are_running
= 1;
255 IN_NEXT_MULTI(step
, inm
);
258 if (!IN_MULTICAST(ip
->ip_dst
.s_addr
)) {
259 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES
);
264 timer
= igmp
->igmp_code
* PR_FASTHZ
/ IGMP_TIMER_SCALE
;
269 * Start the timers in all of our membership records
270 * for the interface on which the query arrived,
271 * except those that are already running and those
272 * that belong to a "local" group (224.0.0.X). For
273 * timers already running, check if they need to be
276 IN_FIRST_MULTI(step
, inm
);
277 while (inm
!= NULL
) {
278 if (inm
->inm_ifp
== ifp
&&
279 !IN_LOCAL_GROUP(inm
->inm_addr
.s_addr
) &&
280 (ip
->ip_dst
.s_addr
== INADDR_ALLHOSTS_GROUP
||
281 in_hosteq(ip
->ip_dst
, inm
->inm_addr
))) {
282 switch (inm
->inm_state
) {
283 case IGMP_DELAYING_MEMBER
:
284 if (inm
->inm_timer
<= timer
)
287 case IGMP_IDLE_MEMBER
:
288 case IGMP_LAZY_MEMBER
:
289 case IGMP_AWAKENING_MEMBER
:
291 IGMP_DELAYING_MEMBER
;
293 IGMP_RANDOM_DELAY(timer
);
294 igmp_timers_are_running
= 1;
296 case IGMP_SLEEPING_MEMBER
:
298 IGMP_AWAKENING_MEMBER
;
302 IN_NEXT_MULTI(step
, inm
);
308 case IGMP_v1_HOST_MEMBERSHIP_REPORT
:
309 IGMP_STATINC(IGMP_STAT_RCV_REPORTS
);
311 if (ifp
->if_flags
& IFF_LOOPBACK
)
314 if (!IN_MULTICAST(igmp
->igmp_group
.s_addr
) ||
315 !in_hosteq(igmp
->igmp_group
, ip
->ip_dst
)) {
316 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS
);
322 * KLUDGE: if the IP source address of the report has an
323 * unspecified (i.e., zero) subnet number, as is allowed for
324 * a booting host, replace it with the correct subnet number
325 * so that a process-level multicast routing daemon can
326 * determine which subnet it arrived from. This is necessary
327 * to compensate for the lack of any way for a process to
328 * determine the arrival interface of an incoming packet.
330 if ((ip
->ip_src
.s_addr
& IN_CLASSA_NET
) == 0) {
331 IFP_TO_IA(ifp
, ia
); /* XXX */
333 ip
->ip_src
.s_addr
= ia
->ia_subnet
;
337 * If we belong to the group being reported, stop
338 * our timer for that group.
340 IN_LOOKUP_MULTI(igmp
->igmp_group
, ifp
, inm
);
343 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS
);
345 switch (inm
->inm_state
) {
346 case IGMP_IDLE_MEMBER
:
347 case IGMP_LAZY_MEMBER
:
348 case IGMP_AWAKENING_MEMBER
:
349 case IGMP_SLEEPING_MEMBER
:
350 inm
->inm_state
= IGMP_SLEEPING_MEMBER
;
352 case IGMP_DELAYING_MEMBER
:
353 if (inm
->inm_rti
->rti_type
== IGMP_v1_ROUTER
)
354 inm
->inm_state
= IGMP_LAZY_MEMBER
;
356 inm
->inm_state
= IGMP_SLEEPING_MEMBER
;
363 case IGMP_v2_HOST_MEMBERSHIP_REPORT
:
366 * Make sure we don't hear our own membership report. Fast
367 * leave requires knowing that we are the only member of a
370 IFP_TO_IA(ifp
, ia
); /* XXX */
371 if (ia
&& in_hosteq(ip
->ip_src
, ia
->ia_addr
.sin_addr
))
375 IGMP_STATINC(IGMP_STAT_RCV_REPORTS
);
377 if (ifp
->if_flags
& IFF_LOOPBACK
)
380 if (!IN_MULTICAST(igmp
->igmp_group
.s_addr
) ||
381 !in_hosteq(igmp
->igmp_group
, ip
->ip_dst
)) {
382 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS
);
388 * KLUDGE: if the IP source address of the report has an
389 * unspecified (i.e., zero) subnet number, as is allowed for
390 * a booting host, replace it with the correct subnet number
391 * so that a process-level multicast routing daemon can
392 * determine which subnet it arrived from. This is necessary
393 * to compensate for the lack of any way for a process to
394 * determine the arrival interface of an incoming packet.
396 if ((ip
->ip_src
.s_addr
& IN_CLASSA_NET
) == 0) {
398 IFP_TO_IA(ifp
, ia
); /* XXX */
401 ip
->ip_src
.s_addr
= ia
->ia_subnet
;
405 * If we belong to the group being reported, stop
406 * our timer for that group.
408 IN_LOOKUP_MULTI(igmp
->igmp_group
, ifp
, inm
);
411 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS
);
413 switch (inm
->inm_state
) {
414 case IGMP_DELAYING_MEMBER
:
415 case IGMP_IDLE_MEMBER
:
416 case IGMP_AWAKENING_MEMBER
:
417 inm
->inm_state
= IGMP_LAZY_MEMBER
;
419 case IGMP_LAZY_MEMBER
:
420 case IGMP_SLEEPING_MEMBER
:
430 * Pass all valid IGMP packets up to any process(es) listening
431 * on a raw IGMP socket.
433 rip_input(m
, iphlen
, proto
);
438 igmp_joingroup(struct in_multi
*inm
)
441 int s
= splsoftnet();
443 inm
->inm_state
= IGMP_IDLE_MEMBER
;
445 if (!IN_LOCAL_GROUP(inm
->inm_addr
.s_addr
) &&
446 (inm
->inm_ifp
->if_flags
& IFF_LOOPBACK
) == 0) {
447 report_type
= rti_fill(inm
);
448 if (report_type
== 0) {
452 igmp_sendpkt(inm
, report_type
);
453 inm
->inm_state
= IGMP_DELAYING_MEMBER
;
454 inm
->inm_timer
= IGMP_RANDOM_DELAY(
455 IGMP_MAX_HOST_REPORT_DELAY
* PR_FASTHZ
);
456 igmp_timers_are_running
= 1;
464 igmp_leavegroup(struct in_multi
*inm
)
467 switch (inm
->inm_state
) {
468 case IGMP_DELAYING_MEMBER
:
469 case IGMP_IDLE_MEMBER
:
470 if (!IN_LOCAL_GROUP(inm
->inm_addr
.s_addr
) &&
471 (inm
->inm_ifp
->if_flags
& IFF_LOOPBACK
) == 0)
472 if (inm
->inm_rti
->rti_type
!= IGMP_v1_ROUTER
)
473 igmp_sendpkt(inm
, IGMP_HOST_LEAVE_MESSAGE
);
475 case IGMP_LAZY_MEMBER
:
476 case IGMP_AWAKENING_MEMBER
:
477 case IGMP_SLEEPING_MEMBER
:
485 struct in_multi
*inm
;
486 struct in_multistep step
;
489 * Quick check to see if any work needs to be done, in order
490 * to minimize the overhead of fasttimo processing.
492 if (!igmp_timers_are_running
)
495 mutex_enter(softnet_lock
);
496 KERNEL_LOCK(1, NULL
);
498 igmp_timers_are_running
= 0;
499 IN_FIRST_MULTI(step
, inm
);
500 while (inm
!= NULL
) {
501 if (inm
->inm_timer
== 0) {
503 } else if (--inm
->inm_timer
== 0) {
504 if (inm
->inm_state
== IGMP_DELAYING_MEMBER
) {
505 if (inm
->inm_rti
->rti_type
== IGMP_v1_ROUTER
)
507 IGMP_v1_HOST_MEMBERSHIP_REPORT
);
510 IGMP_v2_HOST_MEMBERSHIP_REPORT
);
511 inm
->inm_state
= IGMP_IDLE_MEMBER
;
514 igmp_timers_are_running
= 1;
516 IN_NEXT_MULTI(step
, inm
);
519 KERNEL_UNLOCK_ONE(NULL
);
520 mutex_exit(softnet_lock
);
526 struct router_info
*rti
;
528 mutex_enter(softnet_lock
);
529 KERNEL_LOCK(1, NULL
);
530 LIST_FOREACH(rti
, &rti_head
, rti_link
) {
531 if (rti
->rti_type
== IGMP_v1_ROUTER
&&
532 ++rti
->rti_age
>= IGMP_AGE_THRESHOLD
) {
533 rti
->rti_type
= IGMP_v2_ROUTER
;
536 KERNEL_UNLOCK_ONE(NULL
);
537 mutex_exit(softnet_lock
);
541 igmp_sendpkt(struct in_multi
*inm
, int type
)
546 struct ip_moptions imo
;
548 extern struct socket
*ip_mrouter
;
549 #endif /* MROUTING */
551 MGETHDR(m
, M_DONTWAIT
, MT_HEADER
);
555 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
556 * is smaller than mbuf size returned by MGETHDR.
558 m
->m_data
+= max_linkhdr
;
559 m
->m_len
= sizeof(struct ip
) + IGMP_MINLEN
;
560 m
->m_pkthdr
.len
= sizeof(struct ip
) + IGMP_MINLEN
;
562 ip
= mtod(m
, struct ip
*);
564 ip
->ip_len
= htons(sizeof(struct ip
) + IGMP_MINLEN
);
565 ip
->ip_off
= htons(0);
566 ip
->ip_p
= IPPROTO_IGMP
;
567 ip
->ip_src
= zeroin_addr
;
568 ip
->ip_dst
= inm
->inm_addr
;
570 m
->m_data
+= sizeof(struct ip
);
571 m
->m_len
-= sizeof(struct ip
);
572 igmp
= mtod(m
, struct igmp
*);
573 igmp
->igmp_type
= type
;
575 igmp
->igmp_group
= inm
->inm_addr
;
576 igmp
->igmp_cksum
= 0;
577 igmp
->igmp_cksum
= in_cksum(m
, IGMP_MINLEN
);
578 m
->m_data
-= sizeof(struct ip
);
579 m
->m_len
+= sizeof(struct ip
);
581 imo
.imo_multicast_ifp
= inm
->inm_ifp
;
582 imo
.imo_multicast_ttl
= 1;
584 imo
.imo_multicast_vif
= -1;
587 * Request loopback of the report if we are acting as a multicast
588 * router, so that the process-level routing demon can hear it.
591 imo
.imo_multicast_loop
= (ip_mrouter
!= NULL
);
593 imo
.imo_multicast_loop
= 0;
594 #endif /* MROUTING */
596 ip_output(m
, NULL
, NULL
, IP_MULTICASTOPTS
, &imo
, NULL
);
598 IGMP_STATINC(IGMP_STAT_SND_REPORTS
);
602 igmp_purgeif(struct ifnet
*ifp
) /* MUST be called at splsoftnet() */
604 rti_delete(ifp
); /* manipulates pools */
608 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS
)
611 return (NETSTAT_SYSCTL(igmpstat_percpu
, IGMP_NSTATS
));
615 sysctl_net_inet_igmp_setup(struct sysctllog
**clog
)
618 sysctl_createv(clog
, 0, NULL
, NULL
,
620 CTLTYPE_NODE
, "net", NULL
,
623 sysctl_createv(clog
, 0, NULL
, NULL
,
625 CTLTYPE_NODE
, "inet", NULL
,
627 CTL_NET
, PF_INET
, CTL_EOL
);
628 sysctl_createv(clog
, 0, NULL
, NULL
,
630 CTLTYPE_NODE
, "igmp",
631 SYSCTL_DESCR("Internet Group Management Protocol"),
633 CTL_NET
, PF_INET
, IPPROTO_IGMP
, CTL_EOL
);
635 sysctl_createv(clog
, 0, NULL
, NULL
,
637 CTLTYPE_STRUCT
, "stats",
638 SYSCTL_DESCR("IGMP statistics"),
639 sysctl_net_inet_igmp_stats
, 0, NULL
, 0,
640 CTL_NET
, PF_INET
, IPPROTO_IGMP
, CTL_CREATE
, CTL_EOL
);