1 /* $KAME: mld6.c,v 1.17 2000/03/01 12:37:25 itojun Exp $ */
4 * Copyright (C) 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 * Copyright (c) 1988 Stephen Deering.
34 * Copyright (c) 1992, 1993
35 * The Regents of the University of California. All rights reserved.
37 * This code is derived from software contributed to Berkeley by
38 * Stephen Deering of Stanford University.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. All advertising materials mentioning features or use of this software
49 * must display the following acknowledgement:
50 * This product includes software developed by the University of
51 * California, Berkeley and its contributors.
52 * 4. Neither the name of the University nor the names of its contributors
53 * may be used to endorse or promote products derived from this software
54 * without specific prior written permission.
56 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68 * @(#)igmp.c 8.1 (Berkeley) 7/19/93
71 #if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
75 #include <sys/param.h>
76 #include <sys/systm.h>
78 #include <sys/socket.h>
79 #include <sys/protosw.h>
80 #include <sys/syslog.h>
84 #include <netinet/in.h>
85 #include <netinet/in_var.h>
86 #include <netinet/ip6.h>
87 #include <netinet6/ip6_var.h>
88 #include <netinet/icmp6.h>
89 #include <netinet6/mld6_var.h>
91 #include <net/net_osdep.h>
97 /* denotes that the MLD max response delay field specifies time in milliseconds */
98 #define MLD6_TIMER_SCALE 1000
100 * time between repetitions of a node's initial report of interest in a
101 * multicast address(in seconds)
103 #define MLD6_UNSOLICITED_REPORT_INTERVAL 10
105 static struct ip6_pktopts ip6_opts
;
106 static int mld6_timers_are_running
;
107 /* XXX: These are necessary for KAME's link-local hack */
108 static struct in6_addr mld6_all_nodes_linklocal
= IN6ADDR_LINKLOCAL_ALLNODES_INIT
;
109 static struct in6_addr mld6_all_routers_linklocal
= IN6ADDR_LINKLOCAL_ALLROUTERS_INIT
;
111 static void mld6_sendpkt
__P((struct in6_multi
*, int, const struct in6_addr
*));
116 static u_int8_t hbh_buf
[8];
117 struct ip6_hbh
*hbh
= (struct ip6_hbh
*)hbh_buf
;
118 u_int16_t rtalert_code
= htons((u_int16_t
)IP6OPT_RTALERT_MLD
);
120 mld6_timers_are_running
= 0;
122 /* ip6h_nxt will be fill in later */
123 hbh
->ip6h_len
= 0; /* (8 >> 3) - 1 */
125 /* XXX: grotty hard coding... */
126 hbh_buf
[2] = IP6OPT_PADN
; /* 2 byte padding */
128 hbh_buf
[4] = IP6OPT_RTALERT
;
129 hbh_buf
[5] = IP6OPT_RTALERT_LEN
- 2;
130 bcopy((caddr_t
)&rtalert_code
, &hbh_buf
[6], sizeof(u_int16_t
));
132 ip6_opts
.ip6po_hbh
= hbh
;
133 /* We will specify the hoplimit by a multicast option. */
134 ip6_opts
.ip6po_hlim
= -1;
138 mld6_start_listening(in6m
)
139 struct in6_multi
*in6m
;
142 int s
= splsoftnet();
149 * The node never sends a Report or Done for the link-scope all-nodes
151 * MLD messages are never sent for multicast addresses whose scope is 0
152 * (reserved) or 1 (node-local).
154 mld6_all_nodes_linklocal
.s6_addr16
[1] =
155 htons(in6m
->in6m_ifp
->if_index
); /* XXX */
156 if (IN6_ARE_ADDR_EQUAL(&in6m
->in6m_addr
, &mld6_all_nodes_linklocal
) ||
157 IPV6_ADDR_MC_SCOPE(&in6m
->in6m_addr
) < IPV6_ADDR_SCOPE_LINKLOCAL
) {
158 in6m
->in6m_timer
= 0;
159 in6m
->in6m_state
= MLD6_OTHERLISTENER
;
161 mld6_sendpkt(in6m
, MLD6_LISTENER_REPORT
, NULL
);
162 in6m
->in6m_timer
= MLD6_RANDOM_DELAY(
163 MLD6_UNSOLICITED_REPORT_INTERVAL
* PR_FASTHZ
);
164 in6m
->in6m_state
= MLD6_IREPORTEDLAST
;
165 mld6_timers_are_running
= 1;
171 mld6_stop_listening(in6m
)
172 struct in6_multi
*in6m
;
174 mld6_all_nodes_linklocal
.s6_addr16
[1] =
175 htons(in6m
->in6m_ifp
->if_index
); /* XXX */
176 mld6_all_routers_linklocal
.s6_addr16
[1] =
177 htons(in6m
->in6m_ifp
->if_index
); /* XXX: necessary when mrouting */
179 if (in6m
->in6m_state
== MLD6_IREPORTEDLAST
&&
180 (!IN6_ARE_ADDR_EQUAL(&in6m
->in6m_addr
, &mld6_all_nodes_linklocal
)) &&
181 IPV6_ADDR_MC_SCOPE(&in6m
->in6m_addr
) > IPV6_ADDR_SCOPE_NODELOCAL
)
182 mld6_sendpkt(in6m
, MLD6_LISTENER_DONE
,
183 &mld6_all_routers_linklocal
);
191 struct ip6_hdr
*ip6
= mtod(m
, struct ip6_hdr
*);
192 struct mld6_hdr
*mldh
;
193 struct ifnet
*ifp
= m
->m_pkthdr
.rcvif
;
194 struct in6_multi
*in6m
;
195 struct in6_ifaddr
*ia
;
196 #if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
197 struct ifmultiaddr
*ifma
;
199 int timer
; /* timer value in the MLD query header */
201 /* source address validation */
202 if (!IN6_IS_ADDR_LINKLOCAL(&ip6
->ip6_src
)) {
204 "mld6_input: src %s is not link-local\n",
205 ip6_sprintf(&ip6
->ip6_src
));
207 * spec (RFC2710) does not explicitly
208 * specify to discard the packet from a non link-local
209 * source address. But we believe it's expected to do so.
215 #ifndef PULLDOWN_TEST
216 IP6_EXTHDR_CHECK(m
, off
, sizeof(*mldh
),);
217 mldh
= (struct mld6_hdr
*)(mtod(m
, caddr_t
) + off
);
219 IP6_EXTHDR_GET(mldh
, struct mld6_hdr
*, m
, off
, sizeof(*mldh
));
221 icmp6stat
.icp6s_tooshort
++;
227 * In the MLD6 specification, there are 3 states and a flag.
229 * In Non-Listener state, we simply don't have a membership record.
230 * In Delaying Listener state, our timer is running (in6m->in6m_timer)
231 * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
233 * The flag is in6m->in6m_state, it is set to MLD6_OTHERLISTENER if
234 * we have heard a report from another member, or MLD6_IREPORTEDLAST
235 * if we sent the last report.
237 switch(mldh
->mld6_type
) {
238 case MLD6_LISTENER_QUERY
:
239 if (ifp
->if_flags
& IFF_LOOPBACK
)
242 if (!IN6_IS_ADDR_UNSPECIFIED(&mldh
->mld6_addr
) &&
243 !IN6_IS_ADDR_MULTICAST(&mldh
->mld6_addr
))
244 break; /* print error or log stat? */
245 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh
->mld6_addr
))
246 mldh
->mld6_addr
.s6_addr16
[1] =
247 htons(ifp
->if_index
); /* XXX */
250 * - Start the timers in all of our membership records
251 * that the query applies to for the interface on
252 * which the query arrived excl. those that belong
253 * to the "all-nodes" group (ff02::1).
254 * - Restart any timer that is already running but has
255 * A value longer than the requested timeout.
256 * - Use the value specified in the query message as
257 * the maximum timeout.
264 * XXX: System timer resolution is too low to handle Max
265 * Response Delay, so set 1 to the internal timer even if
266 * the calculated value equals to zero when Max Response
269 timer
= ntohs(mldh
->mld6_maxdelay
)*PR_FASTHZ
/MLD6_TIMER_SCALE
;
270 if (timer
== 0 && mldh
->mld6_maxdelay
)
272 mld6_all_nodes_linklocal
.s6_addr16
[1] =
273 htons(ifp
->if_index
); /* XXX */
275 #if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
276 LIST_FOREACH(ifma
, &ifp
->if_multiaddrs
, ifma_link
)
278 for (in6m
= ia
->ia6_multiaddrs
.lh_first
;
280 in6m
= in6m
->in6m_entry
.le_next
)
283 #if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
284 if (ifma
->ifma_addr
->sa_family
!= AF_INET6
)
286 in6m
= (struct in6_multi
*)ifma
->ifma_protospec
;
287 if (IN6_ARE_ADDR_EQUAL(&in6m
->in6m_addr
,
288 &mld6_all_nodes_linklocal
) ||
289 IPV6_ADDR_MC_SCOPE(&in6m
->in6m_addr
) <
290 IPV6_ADDR_SCOPE_LINKLOCAL
)
293 if (IN6_ARE_ADDR_EQUAL(&in6m
->in6m_addr
,
294 &mld6_all_nodes_linklocal
) ||
295 IPV6_ADDR_MC_SCOPE(&in6m
->in6m_addr
) <
296 IPV6_ADDR_SCOPE_LINKLOCAL
)
300 if (IN6_IS_ADDR_UNSPECIFIED(&mldh
->mld6_addr
) ||
301 IN6_ARE_ADDR_EQUAL(&mldh
->mld6_addr
,
305 /* send a report immediately */
306 mld6_sendpkt(in6m
, MLD6_LISTENER_REPORT
,
308 in6m
->in6m_timer
= 0; /* reset timer */
309 in6m
->in6m_state
= MLD6_IREPORTEDLAST
;
311 else if (in6m
->in6m_timer
== 0 || /*idle state*/
312 in6m
->in6m_timer
> timer
) {
314 MLD6_RANDOM_DELAY(timer
);
315 mld6_timers_are_running
= 1;
320 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh
->mld6_addr
))
321 mldh
->mld6_addr
.s6_addr16
[1] = 0; /* XXX */
323 case MLD6_LISTENER_REPORT
:
325 * For fast leave to work, we have to know that we are the
326 * last person to send a report for this group. Reports
327 * can potentially get looped back if we are a multicast
328 * router, so discard reports sourced by me.
329 * Note that it is impossible to check IFF_LOOPBACK flag of
330 * ifp for this purpose, since ip6_mloopback pass the physical
331 * interface to looutput.
333 if (m
->m_flags
& M_LOOP
) /* XXX: grotty flag, but efficient */
336 if (!IN6_IS_ADDR_MULTICAST(&mldh
->mld6_addr
))
339 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh
->mld6_addr
))
340 mldh
->mld6_addr
.s6_addr16
[1] =
341 htons(ifp
->if_index
); /* XXX */
343 * If we belong to the group being reported, stop
344 * our timer for that group.
346 IN6_LOOKUP_MULTI(mldh
->mld6_addr
, ifp
, in6m
);
348 in6m
->in6m_timer
= 0; /* transit to idle state */
349 in6m
->in6m_state
= MLD6_OTHERLISTENER
; /* clear flag */
352 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh
->mld6_addr
))
353 mldh
->mld6_addr
.s6_addr16
[1] = 0; /* XXX */
355 default: /* this is impossible */
356 log(LOG_ERR
, "mld6_input: illegal type(%d)", mldh
->mld6_type
);
366 register struct in6_multi
*in6m
;
367 struct in6_multistep step
;
371 * Quick check to see if any work needs to be done, in order
372 * to minimize the overhead of fasttimo processing.
374 if (!mld6_timers_are_running
)
382 mld6_timers_are_running
= 0;
383 IN6_FIRST_MULTI(step
, in6m
);
384 while (in6m
!= NULL
) {
385 if (in6m
->in6m_timer
== 0) {
387 } else if (--in6m
->in6m_timer
== 0) {
388 mld6_sendpkt(in6m
, MLD6_LISTENER_REPORT
, NULL
);
389 in6m
->in6m_state
= MLD6_IREPORTEDLAST
;
391 mld6_timers_are_running
= 1;
393 IN6_NEXT_MULTI(step
, in6m
);
399 mld6_sendpkt(in6m
, type
, dst
)
400 struct in6_multi
*in6m
;
402 const struct in6_addr
*dst
;
404 struct mbuf
*mh
, *md
;
405 struct mld6_hdr
*mldh
;
407 struct ip6_moptions im6o
;
408 struct in6_ifaddr
*ia
;
409 struct ifnet
*ifp
= in6m
->in6m_ifp
;
410 struct ifnet
*outif
= NULL
;
413 * At first, find a link local address on the outgoing interface
414 * to use as the source address of the MLD packet.
416 if ((ia
= in6ifa_ifpforlinklocal(ifp
, IN6_IFF_NOTREADY
|IN6_IFF_ANYCAST
))
421 * Allocate mbufs to store ip6 header and MLD header.
422 * We allocate 2 mbufs and make chain in advance because
423 * it is more convenient when inserting the hop-by-hop option later.
425 MGETHDR(mh
, M_DONTWAIT
, MT_HEADER
);
428 MGET(md
, M_DONTWAIT
, MT_DATA
);
435 mh
->m_pkthdr
.len
= sizeof(struct ip6_hdr
) + sizeof(struct mld6_hdr
);
436 mh
->m_len
= sizeof(struct ip6_hdr
);
437 MH_ALIGN(mh
, sizeof(struct ip6_hdr
));
439 /* fill in the ip6 header */
440 ip6
= mtod(mh
, struct ip6_hdr
*);
442 ip6
->ip6_vfc
&= ~IPV6_VERSION_MASK
;
443 ip6
->ip6_vfc
|= IPV6_VERSION
;
444 /* ip6_plen will be set later */
445 ip6
->ip6_nxt
= IPPROTO_ICMPV6
;
446 /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
447 ip6
->ip6_src
= ia
->ia_addr
.sin6_addr
;
448 ip6
->ip6_dst
= dst
? *dst
: in6m
->in6m_addr
;
450 /* fill in the MLD header */
451 md
->m_len
= sizeof(struct mld6_hdr
);
452 mldh
= mtod(md
, struct mld6_hdr
*);
453 mldh
->mld6_type
= type
;
455 mldh
->mld6_cksum
= 0;
456 /* XXX: we assume the function will not be called for query messages */
457 mldh
->mld6_maxdelay
= 0;
458 mldh
->mld6_reserved
= 0;
459 mldh
->mld6_addr
= in6m
->in6m_addr
;
460 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh
->mld6_addr
))
461 mldh
->mld6_addr
.s6_addr16
[1] = 0; /* XXX */
462 mldh
->mld6_cksum
= in6_cksum(mh
, IPPROTO_ICMPV6
, sizeof(struct ip6_hdr
),
463 sizeof(struct mld6_hdr
));
465 /* construct multicast option */
466 bzero(&im6o
, sizeof(im6o
));
467 im6o
.im6o_multicast_ifp
= ifp
;
468 im6o
.im6o_multicast_hlim
= 1;
471 * Request loopback of the report if we are acting as a multicast
472 * router, so that the process-level routing daemon can hear it.
474 im6o
.im6o_multicast_loop
= (ip6_mrouter
!= NULL
);
476 /* increment output statictics */
477 icmp6stat
.icp6s_outhist
[type
]++;
479 ip6_output(mh
, &ip6_opts
, NULL
, 0, &im6o
, &outif
);
481 icmp6_ifstat_inc(outif
, ifs6_out_msg
);
483 case MLD6_LISTENER_QUERY
:
484 icmp6_ifstat_inc(outif
, ifs6_out_mldquery
);
486 case MLD6_LISTENER_REPORT
:
487 icmp6_ifstat_inc(outif
, ifs6_out_mldreport
);
489 case MLD6_LISTENER_DONE
:
490 icmp6_ifstat_inc(outif
, ifs6_out_mlddone
);