123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- /* $OpenBSD: mld6.c,v 1.42 2015/06/16 11:09:40 mpi Exp $ */
- /* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */
- /*
- * Copyright (C) 1998 WIDE Project.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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
- * SUCH DAMAGE.
- */
- /*
- * Copyright (c) 1988 Stephen Deering.
- * Copyright (c) 1992, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * 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
- * are met:
- * 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. 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
- * SUCH DAMAGE.
- *
- * @(#)igmp.c 8.1 (Berkeley) 7/19/93
- */
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/mbuf.h>
- #include <sys/socket.h>
- #include <sys/protosw.h>
- #include <sys/syslog.h>
- #include <net/if.h>
- #include <net/if_var.h>
- #include <netinet/in.h>
- #include <netinet6/in6_var.h>
- #include <netinet/ip6.h>
- #include <netinet6/ip6_var.h>
- #include <netinet/icmp6.h>
- #include <netinet6/mld6.h>
- #include <netinet6/mld6_var.h>
- static struct ip6_pktopts ip6_opts;
- static int mld_timers_are_running;
- /* XXX: These are necessary for KAME's link-local hack */
- static struct in6_addr mld_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
- static struct in6_addr mld_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
- void mld6_checktimer(struct ifnet *);
- static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
- void
- mld6_init()
- {
- static u_int8_t hbh_buf[8];
- struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
- u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
- mld_timers_are_running = 0;
- /* ip6h_nxt will be fill in later */
- hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
- /* XXX: grotty hard coding... */
- hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
- hbh_buf[3] = 0;
- hbh_buf[4] = IP6OPT_ROUTER_ALERT;
- hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
- bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
- ip6_opts.ip6po_hbh = hbh;
- }
- void
- mld6_start_listening(struct in6_multi *in6m)
- {
- int s = splsoftnet();
- /*
- * RFC2710 page 10:
- * The node never sends a Report or Done for the link-scope all-nodes
- * address.
- * MLD messages are never sent for multicast addresses whose scope is 0
- * (reserved) or 1 (node-local).
- */
- mld_all_nodes_linklocal.s6_addr16[1] = htons(in6m->in6m_ifidx);/* XXX */
- if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld_all_nodes_linklocal) ||
- __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < __IPV6_ADDR_SCOPE_LINKLOCAL) {
- in6m->in6m_timer = 0;
- in6m->in6m_state = MLD_OTHERLISTENER;
- } else {
- mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
- in6m->in6m_timer =
- MLD_RANDOM_DELAY(MLD_V1_MAX_RI *
- PR_FASTHZ);
- in6m->in6m_state = MLD_IREPORTEDLAST;
- mld_timers_are_running = 1;
- }
- splx(s);
- }
- void
- mld6_stop_listening(struct in6_multi *in6m)
- {
- int s = splsoftnet();
- mld_all_nodes_linklocal.s6_addr16[1] = htons(in6m->in6m_ifidx);/* XXX */
- mld_all_routers_linklocal.s6_addr16[1] =
- htons(in6m->in6m_ifidx); /* XXX: necessary when mrouting */
- if (in6m->in6m_state == MLD_IREPORTEDLAST &&
- (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld_all_nodes_linklocal)) &&
- __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > __IPV6_ADDR_SCOPE_INTFACELOCAL)
- mld6_sendpkt(in6m, MLD_LISTENER_DONE,
- &mld_all_routers_linklocal);
- splx(s);
- }
- void
- mld6_input(struct mbuf *m, int off)
- {
- struct ip6_hdr *ip6;
- struct mld_hdr *mldh;
- struct ifnet *ifp;
- struct in6_multi *in6m;
- struct ifmaddr *ifma;
- int timer; /* timer value in the MLD query header */
- ifp = if_get(m->m_pkthdr.ph_ifidx);
- if (ifp == NULL) {
- m_freem(m);
- return;
- }
- IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
- if (mldh == NULL) {
- icmp6stat.icp6s_tooshort++;
- return;
- }
- /* source address validation */
- ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
- if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
- #if 0
- char src[INET6_ADDRSTRLEN], grp[INET6_ADDRSTRLEN];
- log(LOG_ERR,
- "mld_input: src %s is not link-local (grp=%s)\n",
- inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)),
- inet_ntop(AF_INET6, &mldh->mld_addr, grp, sizeof(grp)));
- #endif
- /*
- * spec (RFC2710) does not explicitly
- * specify to discard the packet from a non link-local
- * source address. But we believe it's expected to do so.
- */
- m_freem(m);
- return;
- }
- /*
- * In the MLD6 specification, there are 3 states and a flag.
- *
- * In Non-Listener state, we simply don't have a membership record.
- * In Delaying Listener state, our timer is running (in6m->in6m_timer)
- * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
- *
- * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
- * we have heard a report from another member, or MLD_IREPORTEDLAST
- * if we sent the last report.
- */
- switch(mldh->mld_type) {
- case MLD_LISTENER_QUERY:
- if (ifp->if_flags & IFF_LOOPBACK)
- break;
- if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
- !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
- break; /* print error or log stat? */
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] =
- htons(ifp->if_index); /* XXX */
- /*
- * - Start the timers in all of our membership records
- * that the query applies to for the interface on
- * which the query arrived excl. those that belong
- * to the "all-nodes" group (ff02::1).
- * - Restart any timer that is already running but has
- * A value longer than the requested timeout.
- * - Use the value specified in the query message as
- * the maximum timeout.
- */
- /*
- * XXX: System timer resolution is too low to handle Max
- * Response Delay, so set 1 to the internal timer even if
- * the calculated value equals to zero when Max Response
- * Delay is positive.
- */
- timer = ntohs(mldh->mld_maxdelay)*PR_FASTHZ/MLD_TIMER_SCALE;
- if (timer == 0 && mldh->mld_maxdelay)
- timer = 1;
- mld_all_nodes_linklocal.s6_addr16[1] =
- htons(ifp->if_index); /* XXX */
- TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
- if (ifma->ifma_addr->sa_family != AF_INET6)
- continue;
- in6m = ifmatoin6m(ifma);
- if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
- &mld_all_nodes_linklocal) ||
- __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
- __IPV6_ADDR_SCOPE_LINKLOCAL)
- continue;
- if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
- IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
- &in6m->in6m_addr))
- {
- if (timer == 0) {
- /* send a report immediately */
- mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
- NULL);
- in6m->in6m_timer = 0; /* reset timer */
- in6m->in6m_state = MLD_IREPORTEDLAST;
- } else if (in6m->in6m_timer == 0 || /* idle */
- in6m->in6m_timer > timer) {
- in6m->in6m_timer =
- MLD_RANDOM_DELAY(timer);
- mld_timers_are_running = 1;
- }
- }
- }
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
- break;
- case MLD_LISTENER_REPORT:
- /*
- * For fast leave to work, we have to know that we are the
- * last person to send a report for this group. Reports
- * can potentially get looped back if we are a multicast
- * router, so discard reports sourced by me.
- * Note that it is impossible to check IFF_LOOPBACK flag of
- * ifp for this purpose, since ip6_mloopback pass the physical
- * interface to looutput.
- */
- if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
- break;
- if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
- break;
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] =
- htons(ifp->if_index); /* XXX */
- /*
- * If we belong to the group being reported, stop
- * our timer for that group.
- */
- IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
- if (in6m) {
- in6m->in6m_timer = 0; /* transit to idle state */
- in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
- }
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
- break;
- 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, "mld_input: illegal type(%d)", mldh->mld_type);
- #endif
- break;
- }
- m_freem(m);
- }
- void
- mld6_fasttimeo(void)
- {
- struct ifnet *ifp;
- int s;
- /*
- * Quick check to see if any work needs to be done, in order
- * to minimize the overhead of fasttimo processing.
- */
- if (!mld_timers_are_running)
- return;
- s = splsoftnet();
- mld_timers_are_running = 0;
- TAILQ_FOREACH(ifp, &ifnet, if_list)
- mld6_checktimer(ifp);
- splx(s);
- }
- void
- mld6_checktimer(struct ifnet *ifp)
- {
- struct in6_multi *in6m;
- struct ifmaddr *ifma;
- splsoftassert(IPL_SOFTNET);
- TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
- if (ifma->ifma_addr->sa_family != AF_INET6)
- continue;
- in6m = ifmatoin6m(ifma);
- if (in6m->in6m_timer == 0) {
- /* do nothing */
- } else if (--in6m->in6m_timer == 0) {
- mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
- in6m->in6m_state = MLD_IREPORTEDLAST;
- } else {
- mld_timers_are_running = 1;
- }
- }
- }
- static void
- mld6_sendpkt(struct in6_multi *in6m, int type, const struct in6_addr *dst)
- {
- struct mbuf *mh, *md;
- struct mld_hdr *mldh;
- struct ip6_hdr *ip6;
- struct ip6_moptions im6o;
- struct in6_ifaddr *ia6;
- struct ifnet *ifp;
- int ignflags;
- ifp = if_get(in6m->in6m_ifidx);
- if (ifp == NULL)
- return;
- /*
- * At first, find a link local address on the outgoing interface
- * 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.
- */
- ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
- if ((ia6 = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL)
- return;
- if ((ia6->ia6_flags & IN6_IFF_TENTATIVE))
- ia6 = NULL;
- /*
- * Allocate mbufs to store ip6 header and MLD header.
- * We allocate 2 mbufs and make chain in advance because
- * it is more convenient when inserting the hop-by-hop option later.
- */
- MGETHDR(mh, M_DONTWAIT, MT_HEADER);
- if (mh == NULL)
- return;
- MGET(md, M_DONTWAIT, MT_DATA);
- if (md == NULL) {
- m_free(mh);
- return;
- }
- mh->m_next = md;
- mh->m_pkthdr.ph_ifidx = 0;
- mh->m_pkthdr.ph_rtableid = ifp->if_rdomain;
- mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
- mh->m_len = sizeof(struct ip6_hdr);
- MH_ALIGN(mh, sizeof(struct ip6_hdr));
- /* fill in the ip6 header */
- ip6 = mtod(mh, struct ip6_hdr *);
- ip6->ip6_flow = 0;
- ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
- ip6->ip6_vfc |= IPV6_VERSION;
- /* ip6_plen will be set later */
- ip6->ip6_nxt = IPPROTO_ICMPV6;
- /* ip6_hlim will be set by im6o.im6o_hlim */
- ip6->ip6_src = ia6 ? ia6->ia_addr.sin6_addr : in6addr_any;
- ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
- /* fill in the MLD header */
- md->m_len = sizeof(struct mld_hdr);
- mldh = mtod(md, struct mld_hdr *);
- mldh->mld_type = type;
- mldh->mld_code = 0;
- mldh->mld_cksum = 0;
- /* XXX: we assume the function will not be called for query messages */
- mldh->mld_maxdelay = 0;
- mldh->mld_reserved = 0;
- mldh->mld_addr = in6m->in6m_addr;
- if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
- mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
- mh->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
- /* construct multicast option */
- bzero(&im6o, sizeof(im6o));
- im6o.im6o_ifidx = ifp->if_index;
- im6o.im6o_hlim = 1;
- /*
- * Request loopback of the report if we are acting as a multicast
- * router, so that the process-level routing daemon can hear it.
- */
- #ifdef MROUTING
- im6o.im6o_loop = (ip6_mrouter != NULL);
- #endif
- /* increment output statictics */
- icmp6stat.icp6s_outhist[type]++;
- icmp6_ifstat_inc(ifp, ifs6_out_msg);
- switch (type) {
- case MLD_LISTENER_QUERY:
- icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
- break;
- case MLD_LISTENER_REPORT:
- icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
- break;
- case MLD_LISTENER_DONE:
- icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
- break;
- }
- ip6_output(mh, &ip6_opts, NULL, ia6 ? 0 : IPV6_UNSPECSRC, &im6o, NULL,
- NULL);
- }
|