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 /*-
    2  * Copyright (C) 1998 WIDE Project.
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  * 3. Neither the name of the project nor the names of its contributors
   14  *    may be used to endorse or promote products derived from this software
   15  *    without specific prior written permission.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  *      $KAME: mld6.c,v 1.27 2001/04/04 05:17:30 itojun Exp $
   30  */
   31 
   32 /*-
   33  * Copyright (c) 1988 Stephen Deering.
   34  * Copyright (c) 1992, 1993
   35  *      The Regents of the University of California.  All rights reserved.
   36  *
   37  * This code is derived from software contributed to Berkeley by
   38  * Stephen Deering of Stanford University.
   39  *
   40  * Redistribution and use in source and binary forms, with or without
   41  * modification, are permitted provided that the following conditions
   42  * are met:
   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  * 4. Neither the name of the University nor the names of its contributors
   49  *    may be used to endorse or promote products derived from this software
   50  *    without specific prior written permission.
   51  *
   52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   62  * SUCH DAMAGE.
   63  *
   64  *      @(#)igmp.c      8.1 (Berkeley) 7/19/93
   65  */
   66 
   67 #include <sys/cdefs.h>
   68 __FBSDID("$FreeBSD$");
   69 
   70 #include "opt_inet.h"
   71 #include "opt_inet6.h"
   72 
   73 #include <sys/param.h>
   74 #include <sys/systm.h>
   75 #include <sys/mbuf.h>
   76 #include <sys/socket.h>
   77 #include <sys/protosw.h>
   78 #include <sys/syslog.h>
   79 #include <sys/kernel.h>
   80 #include <sys/callout.h>
   81 #include <sys/malloc.h>
   82 
   83 #include <net/if.h>
   84 
   85 #include <netinet/in.h>
   86 #include <netinet/in_var.h>
   87 #include <netinet6/in6_var.h>
   88 #include <netinet/ip6.h>
   89 #include <netinet6/ip6_var.h>
   90 #include <netinet6/scope6_var.h>
   91 #include <netinet/icmp6.h>
   92 #include <netinet6/mld6_var.h>
   93 
   94 #include <net/net_osdep.h>
   95 
   96 /*
   97  * Protocol constants
   98  */
   99 
  100 /* denotes that the MLD max response delay field specifies time in milliseconds */
  101 #define MLD_TIMER_SCALE 1000
  102 /*
  103  * time between repetitions of a node's initial report of interest in a
  104  * multicast address(in seconds)
  105  */
  106 #define MLD_UNSOLICITED_REPORT_INTERVAL 10
  107 
  108 static struct ip6_pktopts ip6_opts;
  109 
  110 static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
  111 static void mld_starttimer(struct in6_multi *);
  112 static void mld_stoptimer(struct in6_multi *);
  113 static void mld_timeo(struct in6_multi *);
  114 static u_long mld_timerresid(struct in6_multi *);
  115 
  116 void
  117 mld6_init()
  118 {
  119         static u_int8_t hbh_buf[8];
  120         struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
  121         u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
  122 
  123         /* ip6h_nxt will be fill in later */
  124         hbh->ip6h_len = 0;      /* (8 >> 3) - 1 */
  125 
  126         /* XXX: grotty hard coding... */
  127         hbh_buf[2] = IP6OPT_PADN;       /* 2 byte padding */
  128         hbh_buf[3] = 0;
  129         hbh_buf[4] = IP6OPT_ROUTER_ALERT;
  130         hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
  131         bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
  132 
  133         ip6_initpktopts(&ip6_opts);
  134         ip6_opts.ip6po_hbh = hbh;
  135 }
  136 
  137 static void
  138 mld_starttimer(in6m)
  139         struct in6_multi *in6m;
  140 {
  141         struct timeval now;
  142 
  143         microtime(&now);
  144         in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz;
  145         in6m->in6m_timer_expire.tv_usec = now.tv_usec +
  146             (in6m->in6m_timer % hz) * (1000000 / hz);
  147         if (in6m->in6m_timer_expire.tv_usec > 1000000) {
  148                 in6m->in6m_timer_expire.tv_sec++;
  149                 in6m->in6m_timer_expire.tv_usec -= 1000000;
  150         }
  151 
  152         /* start or restart the timer */
  153         callout_reset(in6m->in6m_timer_ch, in6m->in6m_timer,
  154             (void (*)(void *))mld_timeo, in6m);
  155 }
  156 
  157 static void
  158 mld_stoptimer(in6m)
  159         struct in6_multi *in6m;
  160 {
  161         if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
  162                 return;
  163 
  164         callout_stop(in6m->in6m_timer_ch);
  165         in6m->in6m_timer = IN6M_TIMER_UNDEF;
  166 }
  167 
  168 static void
  169 mld_timeo(in6m)
  170         struct in6_multi *in6m;
  171 {
  172         int s = splnet();
  173 
  174         in6m->in6m_timer = IN6M_TIMER_UNDEF;
  175 
  176         callout_stop(in6m->in6m_timer_ch);
  177 
  178         switch (in6m->in6m_state) {
  179         case MLD_REPORTPENDING:
  180                 mld6_start_listening(in6m);
  181                 break;
  182         default:
  183                 mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
  184                 break;
  185         }
  186 
  187         splx(s);
  188 }
  189 
  190 static u_long
  191 mld_timerresid(in6m)
  192         struct in6_multi *in6m;
  193 {
  194         struct timeval now, diff;
  195 
  196         microtime(&now);
  197 
  198         if (now.tv_sec > in6m->in6m_timer_expire.tv_sec ||
  199             (now.tv_sec == in6m->in6m_timer_expire.tv_sec &&
  200             now.tv_usec > in6m->in6m_timer_expire.tv_usec)) {
  201                 return (0);
  202         }
  203         diff = in6m->in6m_timer_expire;
  204         diff.tv_sec -= now.tv_sec;
  205         diff.tv_usec -= now.tv_usec;
  206         if (diff.tv_usec < 0) {
  207                 diff.tv_sec--;
  208                 diff.tv_usec += 1000000;
  209         }
  210 
  211         /* return the remaining time in milliseconds */
  212         return (((u_long)(diff.tv_sec * 1000000 + diff.tv_usec)) / 1000);
  213 }
  214 
  215 void
  216 mld6_start_listening(in6m)
  217         struct in6_multi *in6m;
  218 {
  219         struct in6_addr all_in6;
  220         int s = splnet();
  221 
  222         /*
  223          * RFC2710 page 10:
  224          * The node never sends a Report or Done for the link-scope all-nodes
  225          * address.
  226          * MLD messages are never sent for multicast addresses whose scope is 0
  227          * (reserved) or 1 (node-local).
  228          */
  229         all_in6 = in6addr_linklocal_allnodes;
  230         if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
  231                 /* XXX: this should not happen! */
  232                 in6m->in6m_timer = 0;
  233                 in6m->in6m_state = MLD_OTHERLISTENER;
  234         }
  235         if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
  236             IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
  237             IPV6_ADDR_SCOPE_LINKLOCAL) {
  238                 in6m->in6m_timer = 0;
  239                 in6m->in6m_state = MLD_OTHERLISTENER;
  240         } else {
  241                 mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
  242                 in6m->in6m_timer = arc4random() %
  243                         MLD_UNSOLICITED_REPORT_INTERVAL * hz;
  244                 in6m->in6m_state = MLD_IREPORTEDLAST;
  245 
  246                 mld_starttimer(in6m);
  247         }
  248         splx(s);
  249 }
  250 
  251 void
  252 mld6_stop_listening(in6m)
  253         struct in6_multi *in6m;
  254 {
  255         struct in6_addr allnode, allrouter;
  256 
  257         allnode = in6addr_linklocal_allnodes;
  258         if (in6_setscope(&allnode, in6m->in6m_ifp, NULL)) {
  259                 /* XXX: this should not happen! */
  260                 return;
  261         }
  262         allrouter = in6addr_linklocal_allrouters;
  263         if (in6_setscope(&allrouter, in6m->in6m_ifp, NULL)) {
  264                 /* XXX impossible */
  265                 return;
  266         }
  267         if (in6m->in6m_state == MLD_IREPORTEDLAST &&
  268             !IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode) &&
  269             IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
  270             IPV6_ADDR_SCOPE_INTFACELOCAL) {
  271                 mld6_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
  272         }
  273 }
  274 
  275 void
  276 mld6_input(m, off)
  277         struct mbuf *m;
  278         int off;
  279 {
  280         struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
  281         struct mld_hdr *mldh;
  282         struct ifnet *ifp = m->m_pkthdr.rcvif;
  283         struct in6_multi *in6m;
  284         struct in6_addr mld_addr, all_in6;
  285         struct in6_ifaddr *ia;
  286         struct ifmultiaddr *ifma;
  287         u_long timer;           /* timer value in the MLD query header */
  288 
  289 #ifndef PULLDOWN_TEST
  290         IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),);
  291         mldh = (struct mld_hdr *)(mtod(m, caddr_t) + off);
  292 #else
  293         IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
  294         if (mldh == NULL) {
  295                 icmp6stat.icp6s_tooshort++;
  296                 return;
  297         }
  298 #endif
  299 
  300         /* source address validation */
  301         ip6 = mtod(m, struct ip6_hdr *); /* in case mpullup */
  302         if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
  303                 log(LOG_ERR,
  304                     "mld6_input: src %s is not link-local (grp=%s)\n",
  305                     ip6_sprintf(&ip6->ip6_src),
  306                     ip6_sprintf(&mldh->mld_addr));
  307                 /*
  308                  * spec (RFC2710) does not explicitly
  309                  * specify to discard the packet from a non link-local
  310                  * source address. But we believe it's expected to do so.
  311                  * XXX: do we have to allow :: as source?
  312                  */
  313                 m_freem(m);
  314                 return;
  315         }
  316 
  317         /*
  318          * make a copy for local work (in6_setscope() may modify the 1st arg)
  319          */
  320         mld_addr = mldh->mld_addr;
  321         if (in6_setscope(&mld_addr, ifp, NULL)) {
  322                 /* XXX: this should not happen! */
  323                 m_free(m);
  324                 return;
  325         }
  326 
  327         /*
  328          * In the MLD6 specification, there are 3 states and a flag.
  329          *
  330          * In Non-Listener state, we simply don't have a membership record.
  331          * In Delaying Listener state, our timer is running (in6m->in6m_timer)
  332          * In Idle Listener state, our timer is not running 
  333          * (in6m->in6m_timer==IN6M_TIMER_UNDEF)
  334          *
  335          * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
  336          * we have heard a report from another member, or MLD_IREPORTEDLAST
  337          * if we sent the last report.
  338          */
  339         switch(mldh->mld_type) {
  340         case MLD_LISTENER_QUERY:
  341                 if (ifp->if_flags & IFF_LOOPBACK)
  342                         break;
  343 
  344                 if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
  345                     !IN6_IS_ADDR_MULTICAST(&mld_addr))
  346                         break;  /* print error or log stat? */
  347 
  348                 all_in6 = in6addr_linklocal_allnodes;
  349                 if (in6_setscope(&all_in6, ifp, NULL)) {
  350                         /* XXX: this should not happen! */
  351                         break;
  352                 }
  353 
  354                 /*
  355                  * - Start the timers in all of our membership records
  356                  *   that the query applies to for the interface on
  357                  *   which the query arrived excl. those that belong
  358                  *   to the "all-nodes" group (ff02::1).
  359                  * - Restart any timer that is already running but has
  360                  *   A value longer than the requested timeout.
  361                  * - Use the value specified in the query message as
  362                  *   the maximum timeout.
  363                  */
  364                 timer = ntohs(mldh->mld_maxdelay);
  365 
  366                 IFP_TO_IA6(ifp, ia);
  367                 if (ia == NULL)
  368                         break;
  369 
  370                 /*
  371                  * XXX: System timer resolution is too low to handle Max
  372                  * Response Delay, so set 1 to the internal timer even if
  373                  * the calculated value equals to zero when Max Response
  374                  * Delay is positive.
  375                  */
  376                 timer = ntohs(mldh->mld_maxdelay) * PR_FASTHZ / MLD_TIMER_SCALE;
  377                 if (timer == 0 && mldh->mld_maxdelay)
  378                         timer = 1;
  379 
  380                 IF_ADDR_LOCK(ifp);
  381                 TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
  382                         if (ifma->ifma_addr->sa_family != AF_INET6)
  383                                 continue;
  384                         in6m = (struct in6_multi *)ifma->ifma_protospec;
  385 
  386                         if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
  387                             IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
  388                             IPV6_ADDR_SCOPE_LINKLOCAL)
  389                                 continue;
  390 
  391                         if (IN6_IS_ADDR_UNSPECIFIED(&mld_addr) ||
  392                             IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr)) {
  393                                 if (timer == 0) {
  394                                         /* send a report immediately */
  395                                         mld_stoptimer(in6m);
  396                                         mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
  397                                                 NULL);
  398                                         in6m->in6m_timer = 0; /* reset timer */
  399                                         in6m->in6m_state = MLD_IREPORTEDLAST;
  400                                 }
  401                                 else if (in6m->in6m_timer == IN6M_TIMER_UNDEF ||
  402                                     mld_timerresid(in6m) > timer) {
  403                                         in6m->in6m_timer =
  404                                            1 + (arc4random() % timer) * hz / 1000;
  405                                         mld_starttimer(in6m);
  406                                 }
  407                         }
  408                 }
  409                 IF_ADDR_UNLOCK(ifp);
  410                 break;
  411 
  412         case MLD_LISTENER_REPORT:
  413                 /*
  414                  * For fast leave to work, we have to know that we are the
  415                  * last person to send a report for this group.  Reports
  416                  * can potentially get looped back if we are a multicast
  417                  * router, so discard reports sourced by me.
  418                  * Note that it is impossible to check IFF_LOOPBACK flag of
  419                  * ifp for this purpose, since ip6_mloopback pass the physical
  420                  * interface to looutput.
  421                  */
  422                 if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
  423                         break;
  424 
  425                 if (!IN6_IS_ADDR_MULTICAST(&mld_addr))
  426                         break;
  427 
  428                 /*
  429                  * If we belong to the group being reported, stop
  430                  * our timer for that group.
  431                  */
  432                 IN6_LOOKUP_MULTI(mld_addr, ifp, in6m);
  433                 if (in6m) {
  434                         in6m->in6m_timer = 0; /* transit to idle state */
  435                         in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
  436                 }
  437                 break;
  438         default:                /* this is impossible */
  439                 log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld_type);
  440                 break;
  441         }
  442 
  443         m_freem(m);
  444 }
  445 
  446 static void
  447 mld6_sendpkt(in6m, type, dst)
  448         struct in6_multi *in6m;
  449         int type;
  450         const struct in6_addr *dst;
  451 {
  452         struct mbuf *mh, *md;
  453         struct mld_hdr *mldh;
  454         struct ip6_hdr *ip6;
  455         struct ip6_moptions im6o;
  456         struct in6_ifaddr *ia;
  457         struct ifnet *ifp = in6m->in6m_ifp;
  458         struct ifnet *outif = NULL;
  459 
  460         /*
  461          * At first, find a link local address on the outgoing interface
  462          * to use as the source address of the MLD packet.
  463          */
  464         if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST))
  465             == NULL)
  466                 return;
  467 
  468         /*
  469          * Allocate mbufs to store ip6 header and MLD header.
  470          * We allocate 2 mbufs and make chain in advance because
  471          * it is more convenient when inserting the hop-by-hop option later.
  472          */
  473         MGETHDR(mh, M_DONTWAIT, MT_HEADER);
  474         if (mh == NULL)
  475                 return;
  476         MGET(md, M_DONTWAIT, MT_DATA);
  477         if (md == NULL) {
  478                 m_free(mh);
  479                 return;
  480         }
  481         mh->m_next = md;
  482 
  483         mh->m_pkthdr.rcvif = NULL;
  484         mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
  485         mh->m_len = sizeof(struct ip6_hdr);
  486         MH_ALIGN(mh, sizeof(struct ip6_hdr));
  487 
  488         /* fill in the ip6 header */
  489         ip6 = mtod(mh, struct ip6_hdr *);
  490         ip6->ip6_flow = 0;
  491         ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
  492         ip6->ip6_vfc |= IPV6_VERSION;
  493         /* ip6_plen will be set later */
  494         ip6->ip6_nxt = IPPROTO_ICMPV6;
  495         /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
  496         ip6->ip6_src = ia->ia_addr.sin6_addr;
  497         ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
  498 
  499         /* fill in the MLD header */
  500         md->m_len = sizeof(struct mld_hdr);
  501         mldh = mtod(md, struct mld_hdr *);
  502         mldh->mld_type = type;
  503         mldh->mld_code = 0;
  504         mldh->mld_cksum = 0;
  505         /* XXX: we assume the function will not be called for query messages */
  506         mldh->mld_maxdelay = 0;
  507         mldh->mld_reserved = 0;
  508         mldh->mld_addr = in6m->in6m_addr;
  509         in6_clearscope(&mldh->mld_addr); /* XXX */
  510         mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
  511                                     sizeof(struct mld_hdr));
  512 
  513         /* construct multicast option */
  514         bzero(&im6o, sizeof(im6o));
  515         im6o.im6o_multicast_ifp = ifp;
  516         im6o.im6o_multicast_hlim = 1;
  517 
  518         /*
  519          * Request loopback of the report if we are acting as a multicast
  520          * router, so that the process-level routing daemon can hear it.
  521          */
  522         im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
  523 
  524         /* increment output statictics */
  525         icmp6stat.icp6s_outhist[type]++;
  526 
  527         ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif, NULL);
  528         if (outif) {
  529                 icmp6_ifstat_inc(outif, ifs6_out_msg);
  530                 switch (type) {
  531                 case MLD_LISTENER_QUERY:
  532                         icmp6_ifstat_inc(outif, ifs6_out_mldquery);
  533                         break;
  534                 case MLD_LISTENER_REPORT:
  535                         icmp6_ifstat_inc(outif, ifs6_out_mldreport);
  536                         break;
  537                 case MLD_LISTENER_DONE:
  538                         icmp6_ifstat_inc(outif, ifs6_out_mlddone);
  539                         break;
  540                 }
  541         }
  542 }
  543 
  544 /*
  545  * Add an address to the list of IP6 multicast addresses for a given interface.
  546  * Add source addresses to the list also, if upstream router is MLDv2 capable
  547  * and the number of source is not 0.
  548  */
  549 struct  in6_multi *
  550 in6_addmulti(maddr6, ifp, errorp, delay)
  551         struct in6_addr *maddr6;
  552         struct ifnet *ifp;
  553         int *errorp, delay;
  554 {
  555         struct in6_multi *in6m;
  556         struct ifmultiaddr *ifma;
  557         struct sockaddr_in6 sa6;
  558         int     s = splnet();
  559 
  560         *errorp = 0;
  561 
  562         /*
  563          * Call generic routine to add membership or increment
  564          * refcount.  It wants addresses in the form of a sockaddr,
  565          * so we build one here (being careful to zero the unused bytes).
  566          */
  567         bzero(&sa6, sizeof(sa6));
  568         sa6.sin6_family = AF_INET6;
  569         sa6.sin6_len = sizeof(struct sockaddr_in6);
  570         sa6.sin6_addr = *maddr6;
  571         *errorp = if_addmulti(ifp, (struct sockaddr *)&sa6, &ifma);
  572         if (*errorp) {
  573                 splx(s);
  574                 return 0;
  575         }
  576 
  577         /*
  578          * If ifma->ifma_protospec is null, then if_addmulti() created
  579          * a new record.  Otherwise, we are done.
  580          */
  581         if (ifma->ifma_protospec != NULL) {
  582                 splx(s);
  583                 return ifma->ifma_protospec;
  584         }
  585 
  586         /* XXX - if_addmulti uses M_WAITOK.  Can this really be called
  587            at interrupt time?  If so, need to fix if_addmulti. XXX */
  588         in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IP6MADDR, M_NOWAIT);
  589         if (in6m == NULL) {
  590                 splx(s);
  591                 return (NULL);
  592         }
  593 
  594         bzero(in6m, sizeof *in6m);
  595         in6m->in6m_addr = *maddr6;
  596         in6m->in6m_ifp = ifp;
  597         in6m->in6m_refcount = 1;
  598         in6m->in6m_ifma = ifma;
  599         ifma->ifma_protospec = in6m;
  600         in6m->in6m_timer_ch = malloc(sizeof(*in6m->in6m_timer_ch), M_IP6MADDR,
  601             M_NOWAIT);
  602         if (in6m->in6m_timer_ch == NULL) {
  603                 free(in6m, M_IP6MADDR);
  604                 splx(s);
  605                 return (NULL);
  606         }
  607         LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
  608 
  609         callout_init(in6m->in6m_timer_ch, 0);
  610         in6m->in6m_timer = delay;
  611         if (in6m->in6m_timer > 0) {
  612                 in6m->in6m_state = MLD_REPORTPENDING;
  613                 mld_starttimer(in6m);
  614 
  615                 splx(s);
  616                 return (in6m);
  617         }
  618 
  619         /*
  620          * Let MLD6 know that we have joined a new IPv6 multicast
  621          * group.
  622          */
  623         mld6_start_listening(in6m);
  624         splx(s);
  625         return (in6m);
  626 }
  627 
  628 /*
  629  * Delete a multicast address record.
  630  */
  631 void
  632 in6_delmulti(in6m)
  633         struct in6_multi *in6m;
  634 {
  635         struct ifmultiaddr *ifma = in6m->in6m_ifma;
  636         int     s = splnet();
  637 
  638         if (ifma->ifma_refcount == 1) {
  639                 /*
  640                  * No remaining claims to this record; let MLD6 know
  641                  * that we are leaving the multicast group.
  642                  */
  643                 mld_stoptimer(in6m);
  644                 mld6_stop_listening(in6m);
  645                 ifma->ifma_protospec = NULL;
  646                 LIST_REMOVE(in6m, in6m_entry);
  647                 free(in6m->in6m_timer_ch, M_IP6MADDR);
  648                 free(in6m, M_IP6MADDR);
  649         }
  650         /* XXX - should be separate API for when we have an ifma? */
  651         if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
  652         splx(s);
  653 }

Cache object: 7a4e260e984e7ede7c92c4a6c4febedc


[ 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.