version 1.9, 2000/01/06 15:46:10 |
version 1.13.4.2, 2002/06/23 17:51:19 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
/* $KAME: mld6.c,v 1.25 2001/01/16 14:14:18 itojun Exp $ */ |
|
|
/* |
/* |
* Copyright (C) 1998 WIDE Project. |
* Copyright (C) 1998 WIDE Project. |
* All rights reserved. |
* All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
* are met: |
* are met: |
|
|
* 3. Neither the name of the project nor the names of its contributors |
* 3. Neither the name of the project nor the names of its contributors |
* may be used to endorse or promote products derived from this software |
* may be used to endorse or promote products derived from this software |
* without specific prior written permission. |
* without specific prior written permission. |
* |
* |
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
|
* @(#)igmp.c 8.1 (Berkeley) 7/19/93 |
* @(#)igmp.c 8.1 (Berkeley) 7/19/93 |
*/ |
*/ |
|
|
|
#include <sys/cdefs.h> |
|
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
#include "opt_inet.h" |
#include "opt_inet.h" |
#include "opt_ipsec.h" |
|
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
|
|
|
|
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <netinet/in_var.h> |
#include <netinet/in_var.h> |
#include <netinet6/ip6.h> |
#include <netinet/ip6.h> |
#include <netinet6/ip6_var.h> |
#include <netinet6/ip6_var.h> |
#include <netinet6/icmp6.h> |
#include <netinet/icmp6.h> |
#include <netinet6/mld6_var.h> |
#include <netinet6/mld6_var.h> |
|
|
#include <net/net_osdep.h> |
#include <net/net_osdep.h> |
|
|
mld6_timers_are_running = 0; |
mld6_timers_are_running = 0; |
|
|
/* ip6h_nxt will be fill in later */ |
/* ip6h_nxt will be fill in later */ |
hbh->ip6h_len = 0; /* (8 >> 3) - 1*/ |
hbh->ip6h_len = 0; /* (8 >> 3) - 1 */ |
|
|
/* XXX: grotty hard coding... */ |
/* XXX: grotty hard coding... */ |
hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */ |
hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */ |
Line 140 mld6_start_listening(in6m) |
|
Line 143 mld6_start_listening(in6m) |
|
int s = splsoftnet(); |
int s = splsoftnet(); |
|
|
/* |
/* |
* (draft-ietf-ipngwg-mld, page 10) |
* RFC2710 page 10: |
* The node never sends a Report or Done for the link-scope all-nodes |
* The node never sends a Report or Done for the link-scope all-nodes |
* address. |
* address. |
* MLD messages are never sent for multicast addresses whose scope is 0 |
* MLD messages are never sent for multicast addresses whose scope is 0 |
* (reserved) or 1 (node-local). |
* (reserved) or 1 (node-local). |
*/ |
*/ |
mld6_all_nodes_linklocal.s6_addr16[1] = |
mld6_all_nodes_linklocal.s6_addr16[1] = |
htons(in6m->in6m_ifp->if_index); /* XXX */ |
htons(in6m->in6m_ifp->if_index); /* XXX */ |
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) || |
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) || |
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) { |
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) { |
in6m->in6m_timer = 0; |
in6m->in6m_timer = 0; |
in6m->in6m_state = MLD6_OTHERLISTENER; |
in6m->in6m_state = MLD6_OTHERLISTENER; |
} else { |
} else { |
mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); |
mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); |
in6m->in6m_timer = MLD6_RANDOM_DELAY( |
in6m->in6m_timer = |
MLD6_UNSOLICITED_REPORT_INTERVAL * PR_FASTHZ); |
MLD6_RANDOM_DELAY(MLD6_UNSOLICITED_REPORT_INTERVAL * |
|
PR_FASTHZ); |
in6m->in6m_state = MLD6_IREPORTEDLAST; |
in6m->in6m_state = MLD6_IREPORTEDLAST; |
mld6_timers_are_running = 1; |
mld6_timers_are_running = 1; |
} |
} |
Line 167 mld6_stop_listening(in6m) |
|
Line 171 mld6_stop_listening(in6m) |
|
struct in6_multi *in6m; |
struct in6_multi *in6m; |
{ |
{ |
mld6_all_nodes_linklocal.s6_addr16[1] = |
mld6_all_nodes_linklocal.s6_addr16[1] = |
htons(in6m->in6m_ifp->if_index); /* XXX */ |
htons(in6m->in6m_ifp->if_index); /* XXX */ |
mld6_all_routers_linklocal.s6_addr16[1] = |
mld6_all_routers_linklocal.s6_addr16[1] = |
htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */ |
htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */ |
|
|
if (in6m->in6m_state == MLD6_IREPORTEDLAST && |
if (in6m->in6m_state == MLD6_IREPORTEDLAST && |
(!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) && |
(!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) && |
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL) |
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL) |
mld6_sendpkt(in6m, MLD6_LISTENER_DONE, |
mld6_sendpkt(in6m, MLD6_LISTENER_DONE, |
&mld6_all_routers_linklocal); |
&mld6_all_routers_linklocal); |
} |
} |
|
|
void |
void |
Line 184 mld6_input(m, off) |
|
Line 188 mld6_input(m, off) |
|
int off; |
int off; |
{ |
{ |
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
struct mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); |
struct mld6_hdr *mldh; |
struct ifnet *ifp = m->m_pkthdr.rcvif; |
struct ifnet *ifp = m->m_pkthdr.rcvif; |
struct in6_multi *in6m; |
struct in6_multi *in6m; |
struct in6_ifaddr *ia; |
struct in6_ifaddr *ia; |
int timer; /* timer value in the MLD query header */ |
int timer; /* timer value in the MLD query header */ |
|
|
|
#ifndef PULLDOWN_TEST |
|
IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); |
|
mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); |
|
#else |
|
IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); |
|
if (mldh == NULL) { |
|
icmp6stat.icp6s_tooshort++; |
|
return; |
|
} |
|
#endif |
|
|
/* source address validation */ |
/* source address validation */ |
|
ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */ |
if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { |
if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { |
|
#if 0 |
log(LOG_ERR, |
log(LOG_ERR, |
"mld6_input: src %s is not link-local\n", |
"mld6_input: src %s is not link-local (grp=%s)\n", |
ip6_sprintf(&ip6->ip6_src)); |
ip6_sprintf(&ip6->ip6_src), |
|
ip6_sprintf(&mldh->mld6_addr)); |
|
#endif |
/* |
/* |
* spec(draft-ietf-ipngwg-mld) does not explicitly |
* spec (RFC2710) does not explicitly |
* specify to discard the packet from a non link-local |
* specify to discard the packet from a non link-local |
* source address. But we believe it's expected to do so. |
* source address. But we believe it's expected to do so. |
|
* XXX: do we have to allow :: as source? |
*/ |
*/ |
|
m_freem(m); |
return; |
return; |
} |
} |
|
|
Line 214 mld6_input(m, off) |
|
Line 235 mld6_input(m, off) |
|
* we have heard a report from another member, or MLD6_IREPORTEDLAST |
* we have heard a report from another member, or MLD6_IREPORTEDLAST |
* if we sent the last report. |
* if we sent the last report. |
*/ |
*/ |
switch(mldh->mld6_type) { |
switch (mldh->mld6_type) { |
case MLD6_LISTENER_QUERY: |
case MLD6_LISTENER_QUERY: |
if (ifp->if_flags & IFF_LOOPBACK) |
if (ifp->if_flags & IFF_LOOPBACK) |
break; |
break; |
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) && |
if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) && |
!IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) |
!IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) |
break; /* print error or log stat? */ |
break; /* print error or log stat? */ |
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) |
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) |
mldh->mld6_addr.s6_addr16[1] = |
mldh->mld6_addr.s6_addr16[1] = |
htons(ifp->if_index); /* XXX */ |
htons(ifp->if_index); /* XXX */ |
|
|
/* |
/* |
* - Start the timers in all of our membership records |
* - Start the timers in all of our membership records |
* that the query applies to for the interface on |
* that the query applies to for the interface on |
* which the query arrived excl. those that belong |
* which the query arrived excl. those that belong |
* to the "all-nodes" group (ff02::1). |
* to the "all-nodes" group (ff02::1). |
* - Restart any timer that is already running but has |
* - Restart any timer that is already running but has |
* A value longer than the requested timeout. |
* A value longer than the requested timeout. |
* - Use the value specified in the query message as |
* - Use the value specified in the query message as |
* the maximum timeout. |
* the maximum timeout. |
*/ |
*/ |
IFP_TO_IA6(ifp, ia); |
IFP_TO_IA6(ifp, ia); |
if (ia == NULL) |
if (ia == NULL) |
break; |
break; |
|
|
/* |
/* |
* XXX: System timer resolution is too low to handle Max |
* XXX: System timer resolution is too low to handle Max |
* Response Delay, so set 1 to the internal timer even if |
* Response Delay, so set 1 to the internal timer even if |
* the calculated value equals to zero when Max Response |
* the calculated value equals to zero when Max Response |
* Delay is positive. |
* Delay is positive. |
*/ |
*/ |
timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE; |
timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE; |
if (timer == 0 && mldh->mld6_maxdelay) |
if (timer == 0 && mldh->mld6_maxdelay) |
timer = 1; |
timer = 1; |
mld6_all_nodes_linklocal.s6_addr16[1] = |
mld6_all_nodes_linklocal.s6_addr16[1] = |
htons(ifp->if_index); /* XXX */ |
htons(ifp->if_index); /* XXX */ |
|
|
for (in6m = ia->ia6_multiaddrs.lh_first; |
for (in6m = ia->ia6_multiaddrs.lh_first; |
in6m; |
in6m; |
in6m = in6m->in6m_entry.le_next) |
in6m = in6m->in6m_entry.le_next) |
Line 269 mld6_input(m, off) |
|
Line 290 mld6_input(m, off) |
|
if (timer == 0) { |
if (timer == 0) { |
/* send a report immediately */ |
/* send a report immediately */ |
mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, |
mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, |
NULL); |
NULL); |
in6m->in6m_timer = 0; /* reset timer */ |
in6m->in6m_timer = 0; /* reset timer */ |
in6m->in6m_state = MLD6_IREPORTEDLAST; |
in6m->in6m_state = MLD6_IREPORTEDLAST; |
} |
} |
else if (in6m->in6m_timer == 0 || /*idle state*/ |
else if (in6m->in6m_timer == 0 || /*idle state*/ |
in6m->in6m_timer > timer) { |
in6m->in6m_timer > timer) { |
in6m->in6m_timer = |
in6m->in6m_timer = |
MLD6_RANDOM_DELAY(timer); |
MLD6_RANDOM_DELAY(timer); |
mld6_timers_are_running = 1; |
mld6_timers_are_running = 1; |
} |
} |
} |
} |
Line 287 mld6_input(m, off) |
|
Line 308 mld6_input(m, off) |
|
break; |
break; |
case MLD6_LISTENER_REPORT: |
case MLD6_LISTENER_REPORT: |
/* |
/* |
* For fast leave to work, we have to know that we are the |
* For fast leave to work, we have to know that we are the |
* last person to send a report for this group. Reports |
* last person to send a report for this group. Reports |
* can potentially get looped back if we are a multicast |
* can potentially get looped back if we are a multicast |
* router, so discard reports sourced by me. |
* router, so discard reports sourced by me. |
* Note that it is impossible to check IFF_LOOPBACK flag of |
* Note that it is impossible to check IFF_LOOPBACK flag of |
* ifp for this purpose, since ip6_mloopback pass the physical |
* ifp for this purpose, since ip6_mloopback pass the physical |
* interface to looutput. |
* interface to looutput. |
*/ |
*/ |
if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */ |
if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */ |
break; |
break; |
|
|
Line 305 mld6_input(m, off) |
|
Line 326 mld6_input(m, off) |
|
mldh->mld6_addr.s6_addr16[1] = |
mldh->mld6_addr.s6_addr16[1] = |
htons(ifp->if_index); /* XXX */ |
htons(ifp->if_index); /* XXX */ |
/* |
/* |
* If we belong to the group being reported, stop |
* If we belong to the group being reported, stop |
* our timer for that group. |
* our timer for that group. |
*/ |
*/ |
IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m); |
IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m); |
if (in6m) { |
if (in6m) { |
in6m->in6m_timer = 0; /* transit to idle state */ |
in6m->in6m_timer = 0; /* transit to idle state */ |
Line 318 mld6_input(m, off) |
|
Line 339 mld6_input(m, off) |
|
mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ |
mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ |
break; |
break; |
default: /* this is impossible */ |
default: /* this is impossible */ |
|
#if 0 |
|
/* |
|
* this case should be impossible because of filtering in |
|
* icmp6_input(). But we explicitly disabled this part |
|
* just in case. |
|
*/ |
log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type); |
log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type); |
|
#endif |
break; |
break; |
} |
} |
|
|
|
m_freem(m); |
} |
} |
|
|
void |
void |
mld6_fasttimeo() |
mld6_fasttimeo() |
{ |
{ |
register struct in6_multi *in6m; |
struct in6_multi *in6m; |
struct in6_multistep step; |
struct in6_multistep step; |
int s; |
int s; |
|
|
/* |
/* |
* Quick check to see if any work needs to be done, in order |
* Quick check to see if any work needs to be done, in order |
* to minimize the overhead of fasttimo processing. |
* to minimize the overhead of fasttimo processing. |
Line 366 mld6_sendpkt(in6m, type, dst) |
|
Line 395 mld6_sendpkt(in6m, type, dst) |
|
struct ip6_moptions im6o; |
struct ip6_moptions im6o; |
struct in6_ifaddr *ia; |
struct in6_ifaddr *ia; |
struct ifnet *ifp = in6m->in6m_ifp; |
struct ifnet *ifp = in6m->in6m_ifp; |
struct ifnet *outif = NULL; |
int ignflags; |
|
|
/* |
/* |
* At first, find a link local address on the outgoing interface |
* At first, find a link local address on the outgoing interface |
* to use as the source address of the MLD packet. |
* to use as the source address of the MLD packet. |
|
* We do not reject tentative addresses for MLD report to deal with |
|
* the case where we first join a link-local address. |
*/ |
*/ |
if ((ia = in6ifa_ifpforlinklocal(ifp)) == NULL) |
ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE; |
|
if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL) |
return; |
return; |
|
if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) |
|
ia = NULL; |
|
|
/* |
/* |
* Allocate mbufs to store ip6 header and MLD header. |
* Allocate mbufs to store ip6 header and MLD header. |
Line 390 mld6_sendpkt(in6m, type, dst) |
|
Line 424 mld6_sendpkt(in6m, type, dst) |
|
} |
} |
mh->m_next = md; |
mh->m_next = md; |
|
|
#ifdef IPSEC |
|
mh->m_pkthdr.rcvif = NULL; |
mh->m_pkthdr.rcvif = NULL; |
#endif |
|
mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); |
mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); |
mh->m_len = sizeof(struct ip6_hdr); |
mh->m_len = sizeof(struct ip6_hdr); |
MH_ALIGN(mh, sizeof(struct ip6_hdr)); |
MH_ALIGN(mh, sizeof(struct ip6_hdr)); |
Line 405 mld6_sendpkt(in6m, type, dst) |
|
Line 437 mld6_sendpkt(in6m, type, dst) |
|
/* ip6_plen will be set later */ |
/* ip6_plen will be set later */ |
ip6->ip6_nxt = IPPROTO_ICMPV6; |
ip6->ip6_nxt = IPPROTO_ICMPV6; |
/* ip6_hlim will be set by im6o.im6o_multicast_hlim */ |
/* ip6_hlim will be set by im6o.im6o_multicast_hlim */ |
ip6->ip6_src = ia->ia_addr.sin6_addr; |
ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any; |
ip6->ip6_dst = dst ? *dst : in6m->in6m_addr; |
ip6->ip6_dst = dst ? *dst : in6m->in6m_addr; |
|
|
/* fill in the MLD header */ |
/* fill in the MLD header */ |
Line 420 mld6_sendpkt(in6m, type, dst) |
|
Line 452 mld6_sendpkt(in6m, type, dst) |
|
mldh->mld6_addr = in6m->in6m_addr; |
mldh->mld6_addr = in6m->in6m_addr; |
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) |
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) |
mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ |
mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ |
mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), |
mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, |
sizeof(struct mld6_hdr)); |
sizeof(struct ip6_hdr), |
|
sizeof(struct mld6_hdr)); |
|
|
/* construct multicast option */ |
/* construct multicast option */ |
bzero(&im6o, sizeof(im6o)); |
bzero(&im6o, sizeof(im6o)); |
Line 436 mld6_sendpkt(in6m, type, dst) |
|
Line 469 mld6_sendpkt(in6m, type, dst) |
|
|
|
/* increment output statictics */ |
/* increment output statictics */ |
icmp6stat.icp6s_outhist[type]++; |
icmp6stat.icp6s_outhist[type]++; |
|
icmp6_ifstat_inc(ifp, ifs6_out_msg); |
ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif); |
switch (type) { |
if (outif) { |
case MLD6_LISTENER_QUERY: |
icmp6_ifstat_inc(outif, ifs6_out_msg); |
icmp6_ifstat_inc(ifp, ifs6_out_mldquery); |
switch(type) { |
break; |
case MLD6_LISTENER_QUERY: |
case MLD6_LISTENER_REPORT: |
icmp6_ifstat_inc(outif, ifs6_out_mldquery); |
icmp6_ifstat_inc(ifp, ifs6_out_mldreport); |
break; |
break; |
case MLD6_LISTENER_REPORT: |
case MLD6_LISTENER_DONE: |
icmp6_ifstat_inc(outif, ifs6_out_mldreport); |
icmp6_ifstat_inc(ifp, ifs6_out_mlddone); |
break; |
break; |
case MLD6_LISTENER_DONE: |
|
icmp6_ifstat_inc(outif, ifs6_out_mlddone); |
|
break; |
|
} |
|
} |
} |
|
|
|
ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC, &im6o, NULL); |
} |
} |