The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet6/mld6.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: mld6.c,v 1.61 2022/09/08 10:22:07 kn Exp $    */
    2 /*      $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $   */
    3 
    4 /*
    5  * Copyright (C) 1998 WIDE Project.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the project nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  */
   32 
   33 /*
   34  * Copyright (c) 1988 Stephen Deering.
   35  * Copyright (c) 1992, 1993
   36  *      The Regents of the University of California.  All rights reserved.
   37  *
   38  * This code is derived from software contributed to Berkeley by
   39  * Stephen Deering of Stanford University.
   40  *
   41  * Redistribution and use in source and binary forms, with or without
   42  * modification, are permitted provided that the following conditions
   43  * are met:
   44  * 1. Redistributions of source code must retain the above copyright
   45  *    notice, this list of conditions and the following disclaimer.
   46  * 2. Redistributions in binary form must reproduce the above copyright
   47  *    notice, this list of conditions and the following disclaimer in the
   48  *    documentation and/or other materials provided with the distribution.
   49  * 3. Neither the name of the University nor the names of its contributors
   50  *    may be used to endorse or promote products derived from this software
   51  *    without specific prior written permission.
   52  *
   53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   63  * SUCH DAMAGE.
   64  *
   65  *      @(#)igmp.c      8.1 (Berkeley) 7/19/93
   66  */
   67 
   68 #include <sys/param.h>
   69 #include <sys/systm.h>
   70 #include <sys/mbuf.h>
   71 #include <sys/socket.h>
   72 #include <sys/protosw.h>
   73 #include <sys/syslog.h>
   74 
   75 #include <net/if.h>
   76 #include <net/if_var.h>
   77 
   78 #include <netinet/in.h>
   79 #include <netinet6/in6_var.h>
   80 #include <netinet/ip6.h>
   81 #include <netinet6/ip6_var.h>
   82 #include <netinet/icmp6.h>
   83 #include <netinet6/mld6.h>
   84 #include <netinet6/mld6_var.h>
   85 
   86 static struct ip6_pktopts ip6_opts;
   87 int     mld6_timers_are_running;        /* [N] shortcut for fast timer */
   88 
   89 void mld6_checktimer(struct ifnet *);
   90 static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
   91 
   92 void
   93 mld6_init(void)
   94 {
   95         static u_int8_t hbh_buf[8];
   96         struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
   97         u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
   98 
   99         mld6_timers_are_running = 0;
  100 
  101         /* ip6h_nxt will be fill in later */
  102         hbh->ip6h_len = 0;      /* (8 >> 3) - 1 */
  103 
  104         /* XXX: grotty hard coding... */
  105         hbh_buf[2] = IP6OPT_PADN;       /* 2 byte padding */
  106         hbh_buf[3] = 0;
  107         hbh_buf[4] = IP6OPT_ROUTER_ALERT;
  108         hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
  109         memcpy(&hbh_buf[6], (caddr_t)&rtalert_code, sizeof(u_int16_t));
  110 
  111         ip6_initpktopts(&ip6_opts);
  112         ip6_opts.ip6po_hbh = hbh;
  113 }
  114 
  115 void
  116 mld6_start_listening(struct in6_multi *in6m)
  117 {
  118         /* XXX: These are necessary for KAME's link-local hack */
  119         struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
  120 
  121         /*
  122          * RFC2710 page 10:
  123          * The node never sends a Report or Done for the link-scope all-nodes
  124          * address.
  125          * MLD messages are never sent for multicast addresses whose scope is 0
  126          * (reserved) or 1 (node-local).
  127          */
  128         all_nodes.s6_addr16[1] = htons(in6m->in6m_ifidx);
  129         if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes) ||
  130             __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
  131             __IPV6_ADDR_SCOPE_LINKLOCAL) {
  132                 in6m->in6m_timer = 0;
  133                 in6m->in6m_state = MLD_OTHERLISTENER;
  134         } else {
  135                 mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
  136                 in6m->in6m_timer =
  137                     MLD_RANDOM_DELAY(MLD_V1_MAX_RI *
  138                     PR_FASTHZ);
  139                 in6m->in6m_state = MLD_IREPORTEDLAST;
  140                 mld6_timers_are_running = 1;
  141         }
  142 }
  143 
  144 void
  145 mld6_stop_listening(struct in6_multi *in6m)
  146 {
  147         /* XXX: These are necessary for KAME's link-local hack */
  148         struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
  149         struct in6_addr all_routers = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
  150 
  151         all_nodes.s6_addr16[1] = htons(in6m->in6m_ifidx);
  152         /* XXX: necessary when mrouting */
  153         all_routers.s6_addr16[1] = htons(in6m->in6m_ifidx);
  154 
  155         if (in6m->in6m_state == MLD_IREPORTEDLAST &&
  156             (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes)) &&
  157             __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
  158             __IPV6_ADDR_SCOPE_INTFACELOCAL)
  159                 mld6_sendpkt(in6m, MLD_LISTENER_DONE, &all_routers);
  160 }
  161 
  162 void
  163 mld6_input(struct mbuf *m, int off)
  164 {
  165         struct ip6_hdr *ip6;
  166         struct mld_hdr *mldh;
  167         struct ifnet *ifp;
  168         struct in6_multi *in6m;
  169         struct ifmaddr *ifma;
  170         int timer;              /* timer value in the MLD query header */
  171         /* XXX: These are necessary for KAME's link-local hack */
  172         struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
  173 
  174         IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
  175         if (mldh == NULL) {
  176                 icmp6stat_inc(icp6s_tooshort);
  177                 return;
  178         }
  179 
  180         /* source address validation */
  181         ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
  182         if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
  183 #if 0
  184                 char src[INET6_ADDRSTRLEN], grp[INET6_ADDRSTRLEN];
  185 
  186                 log(LOG_ERR,
  187                     "mld_input: src %s is not link-local (grp=%s)\n",
  188                     inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)),
  189                     inet_ntop(AF_INET6, &mldh->mld_addr, grp, sizeof(grp)));
  190 #endif
  191                 /*
  192                  * spec (RFC2710) does not explicitly
  193                  * specify to discard the packet from a non link-local
  194                  * source address. But we believe it's expected to do so.
  195                  */
  196                 m_freem(m);
  197                 return;
  198         }
  199 
  200         ifp = if_get(m->m_pkthdr.ph_ifidx);
  201         if (ifp == NULL) {
  202                 m_freem(m);
  203                 return;
  204         }
  205 
  206         /*
  207          * In the MLD6 specification, there are 3 states and a flag.
  208          *
  209          * In Non-Listener state, we simply don't have a membership record.
  210          * In Delaying Listener state, our timer is running (in6m->in6m_timer)
  211          * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
  212          *
  213          * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
  214          * we have heard a report from another member, or MLD_IREPORTEDLAST
  215          * if we sent the last report.
  216          */
  217         switch(mldh->mld_type) {
  218         case MLD_LISTENER_QUERY:
  219                 if (ifp->if_flags & IFF_LOOPBACK)
  220                         break;
  221 
  222                 if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
  223                     !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
  224                         break;  /* print error or log stat? */
  225                 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
  226                         mldh->mld_addr.s6_addr16[1] =
  227                             htons(ifp->if_index); /* XXX */
  228 
  229                 /*
  230                  * - Start the timers in all of our membership records
  231                  *   that the query applies to for the interface on
  232                  *   which the query arrived excl. those that belong
  233                  *   to the "all-nodes" group (ff02::1).
  234                  * - Restart any timer that is already running but has
  235                  *   A value longer than the requested timeout.
  236                  * - Use the value specified in the query message as
  237                  *   the maximum timeout.
  238                  */
  239 
  240                 /*
  241                  * XXX: System timer resolution is too low to handle Max
  242                  * Response Delay, so set 1 to the internal timer even if
  243                  * the calculated value equals to zero when Max Response
  244                  * Delay is positive.
  245                  */
  246                 timer = ntohs(mldh->mld_maxdelay)*PR_FASTHZ/MLD_TIMER_SCALE;
  247                 if (timer == 0 && mldh->mld_maxdelay)
  248                         timer = 1;
  249                 all_nodes.s6_addr16[1] = htons(ifp->if_index);
  250 
  251                 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
  252                         if (ifma->ifma_addr->sa_family != AF_INET6)
  253                                 continue;
  254                         in6m = ifmatoin6m(ifma);
  255                         if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes) ||
  256                             __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
  257                             __IPV6_ADDR_SCOPE_LINKLOCAL)
  258                                 continue;
  259 
  260                         if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
  261                             IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
  262                                                 &in6m->in6m_addr))
  263                         {
  264                                 if (timer == 0) {
  265                                         /* send a report immediately */
  266                                         mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
  267                                             NULL);
  268                                         in6m->in6m_timer = 0; /* reset timer */
  269                                         in6m->in6m_state = MLD_IREPORTEDLAST;
  270                                 } else if (in6m->in6m_timer == 0 || /* idle */
  271                                         in6m->in6m_timer > timer) {
  272                                         in6m->in6m_timer =
  273                                             MLD_RANDOM_DELAY(timer);
  274                                         mld6_timers_are_running = 1;
  275                                 }
  276                         }
  277                 }
  278 
  279                 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
  280                         mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
  281                 break;
  282         case MLD_LISTENER_REPORT:
  283                 /*
  284                  * For fast leave to work, we have to know that we are the
  285                  * last person to send a report for this group.  Reports
  286                  * can potentially get looped back if we are a multicast
  287                  * router, so discard reports sourced by me.
  288                  * Note that it is impossible to check IFF_LOOPBACK flag of
  289                  * ifp for this purpose, since ip6_mloopback pass the physical
  290                  * interface to if_input_local().
  291                  */
  292                 if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
  293                         break;
  294 
  295                 if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
  296                         break;
  297 
  298                 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
  299                         mldh->mld_addr.s6_addr16[1] =
  300                                 htons(ifp->if_index); /* XXX */
  301                 /*
  302                  * If we belong to the group being reported, stop
  303                  * our timer for that group.
  304                  */
  305                 IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
  306                 if (in6m) {
  307                         in6m->in6m_timer = 0; /* transit to idle state */
  308                         in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
  309                 }
  310 
  311                 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
  312                         mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
  313                 break;
  314         default:                /* this is impossible */
  315 #if 0
  316                 /*
  317                  * this case should be impossible because of filtering in
  318                  * icmp6_input().  But we explicitly disabled this part
  319                  * just in case.
  320                  */
  321                 log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
  322 #endif
  323                 break;
  324         }
  325         if_put(ifp);
  326 
  327         m_freem(m);
  328 }
  329 
  330 void
  331 mld6_fasttimeo(void)
  332 {
  333         struct ifnet *ifp;
  334 
  335         /*
  336          * Quick check to see if any work needs to be done, in order
  337          * to minimize the overhead of fasttimo processing.
  338          * Variable mld6_timers_are_running is read atomically, but without
  339          * lock intentionally.  In case it is not set due to MP races, we may
  340          * miss to check the timers.  Then run the loop at next fast timeout.
  341          */
  342         if (!mld6_timers_are_running)
  343                 return;
  344 
  345         NET_LOCK();
  346 
  347         mld6_timers_are_running = 0;
  348         TAILQ_FOREACH(ifp, &ifnetlist, if_list)
  349                 mld6_checktimer(ifp);
  350 
  351         NET_UNLOCK();
  352 }
  353 
  354 void
  355 mld6_checktimer(struct ifnet *ifp)
  356 {
  357         struct in6_multi *in6m;
  358         struct ifmaddr *ifma;
  359 
  360         NET_ASSERT_LOCKED();
  361 
  362         TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
  363                 if (ifma->ifma_addr->sa_family != AF_INET6)
  364                         continue;
  365                 in6m = ifmatoin6m(ifma);
  366                 if (in6m->in6m_timer == 0) {
  367                         /* do nothing */
  368                 } else if (--in6m->in6m_timer == 0) {
  369                         mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
  370                         in6m->in6m_state = MLD_IREPORTEDLAST;
  371                 } else {
  372                         mld6_timers_are_running = 1;
  373                 }
  374         }
  375 }
  376 
  377 static void
  378 mld6_sendpkt(struct in6_multi *in6m, int type, const struct in6_addr *dst)
  379 {
  380         struct mbuf *mh, *md;
  381         struct mld_hdr *mldh;
  382         struct ip6_hdr *ip6;
  383         struct ip6_moptions im6o;
  384         struct in6_ifaddr *ia6;
  385         struct ifnet *ifp;
  386         int ignflags;
  387 
  388         ifp = if_get(in6m->in6m_ifidx);
  389         if (ifp == NULL)
  390                 return;
  391 
  392         /*
  393          * At first, find a link local address on the outgoing interface
  394          * to use as the source address of the MLD packet.
  395          * We do not reject tentative addresses for MLD report to deal with
  396          * the case where we first join a link-local address.
  397          */
  398         ignflags = IN6_IFF_DUPLICATED|IN6_IFF_ANYCAST;
  399         if ((ia6 = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL) {
  400                 if_put(ifp);
  401                 return;
  402         }
  403         if ((ia6->ia6_flags & IN6_IFF_TENTATIVE))
  404                 ia6 = NULL;
  405 
  406         /*
  407          * Allocate mbufs to store ip6 header and MLD header.
  408          * We allocate 2 mbufs and make chain in advance because
  409          * it is more convenient when inserting the hop-by-hop option later.
  410          */
  411         MGETHDR(mh, M_DONTWAIT, MT_HEADER);
  412         if (mh == NULL) {
  413                 if_put(ifp);
  414                 return;
  415         }
  416         MGET(md, M_DONTWAIT, MT_DATA);
  417         if (md == NULL) {
  418                 m_free(mh);
  419                 if_put(ifp);
  420                 return;
  421         }
  422         mh->m_next = md;
  423 
  424         mh->m_pkthdr.ph_ifidx = 0;
  425         mh->m_pkthdr.ph_rtableid = ifp->if_rdomain;
  426         mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
  427         mh->m_len = sizeof(struct ip6_hdr);
  428         m_align(mh, sizeof(struct ip6_hdr));
  429 
  430         /* fill in the ip6 header */
  431         ip6 = mtod(mh, struct ip6_hdr *);
  432         ip6->ip6_flow = 0;
  433         ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
  434         ip6->ip6_vfc |= IPV6_VERSION;
  435         /* ip6_plen will be set later */
  436         ip6->ip6_nxt = IPPROTO_ICMPV6;
  437         /* ip6_hlim will be set by im6o.im6o_hlim */
  438         ip6->ip6_src = ia6 ? ia6->ia_addr.sin6_addr : in6addr_any;
  439         ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
  440 
  441         /* fill in the MLD header */
  442         md->m_len = sizeof(struct mld_hdr);
  443         mldh = mtod(md, struct mld_hdr *);
  444         mldh->mld_type = type;
  445         mldh->mld_code = 0;
  446         mldh->mld_cksum = 0;
  447         /* XXX: we assume the function will not be called for query messages */
  448         mldh->mld_maxdelay = 0;
  449         mldh->mld_reserved = 0;
  450         mldh->mld_addr = in6m->in6m_addr;
  451         if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
  452                 mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
  453         mh->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
  454 
  455         /* construct multicast option */
  456         bzero(&im6o, sizeof(im6o));
  457         im6o.im6o_ifidx = ifp->if_index;
  458         im6o.im6o_hlim = 1;
  459 
  460         /*
  461          * Request loopback of the report if we are acting as a multicast
  462          * router, so that the process-level routing daemon can hear it.
  463          */
  464 #ifdef MROUTING
  465         im6o.im6o_loop = (ip6_mrouter[ifp->if_rdomain] != NULL);
  466 #endif
  467         if_put(ifp);
  468 
  469         icmp6stat_inc(icp6s_outhist + type);
  470         ip6_output(mh, &ip6_opts, NULL, ia6 ? 0 : IPV6_UNSPECSRC, &im6o,
  471             NULL);
  472 }

Cache object: 400142a22515cb5233f29b26e3a6044d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.