root/netinet/igmp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. igmp_init
  2. rti_fill
  3. rti_find
  4. rti_delete
  5. igmp_input
  6. igmp_joingroup
  7. igmp_leavegroup
  8. igmp_fasttimo
  9. igmp_slowtimo
  10. igmp_sendpkt

    1 /*      $OpenBSD: igmp.c,v 1.24 2007/07/20 19:00:35 claudio Exp $       */
    2 /*      $NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos Exp $       */
    3 
    4 /*
    5  * Internet Group Management Protocol (IGMP) routines.
    6  *
    7  * Written by Steve Deering, Stanford, May 1988.
    8  * Modified by Rosen Sharma, Stanford, Aug 1994.
    9  * Modified by Bill Fenner, Xerox PARC, Feb 1995.
   10  *
   11  * MULTICAST Revision: 1.3
   12  */
   13 
   14 #include <sys/param.h>
   15 #include <sys/mbuf.h>
   16 #include <sys/socket.h>
   17 #include <sys/protosw.h>
   18 
   19 #include <net/if.h>
   20 #include <net/route.h>
   21 
   22 #include <netinet/in.h>
   23 #include <netinet/in_var.h>
   24 #include <netinet/in_systm.h>
   25 #include <netinet/ip.h>
   26 #include <netinet/ip_var.h>
   27 #include <netinet/igmp.h>
   28 #include <netinet/igmp_var.h>
   29 #include <dev/rndvar.h>
   30 
   31 #include <sys/stdarg.h>
   32 
   33 #define IP_MULTICASTOPTS        0
   34 
   35 int             igmp_timers_are_running;
   36 static struct router_info *rti_head;
   37 struct igmpstat igmpstat;
   38 
   39 void igmp_sendpkt(struct in_multi *, int, in_addr_t);
   40 int rti_fill(struct in_multi *);
   41 struct router_info * rti_find(struct ifnet *);
   42 
   43 void
   44 igmp_init()
   45 {
   46 
   47         /*
   48          * To avoid byte-swapping the same value over and over again.
   49          */
   50         igmp_timers_are_running = 0;
   51         rti_head = 0;
   52 }
   53 
   54 /* Return -1 for error. */
   55 int
   56 rti_fill(inm)
   57         struct in_multi *inm;
   58 {
   59         struct router_info *rti;
   60 
   61         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
   62                 if (rti->rti_ifp == inm->inm_ia->ia_ifp) {
   63                         inm->inm_rti = rti;
   64                         if (rti->rti_type == IGMP_v1_ROUTER)
   65                                 return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
   66                         else
   67                                 return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
   68                 }
   69         }
   70 
   71         rti = (struct router_info *)malloc(sizeof(struct router_info),
   72                                            M_MRTABLE, M_NOWAIT);
   73         if (rti == NULL)
   74                 return (-1);
   75         rti->rti_ifp = inm->inm_ia->ia_ifp;
   76         rti->rti_type = IGMP_v2_ROUTER;
   77         rti->rti_next = rti_head;
   78         rti_head = rti;
   79         inm->inm_rti = rti;
   80         return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
   81 }
   82 
   83 struct router_info *
   84 rti_find(ifp)
   85         struct ifnet *ifp;
   86 {
   87         struct router_info *rti;
   88 
   89         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
   90                 if (rti->rti_ifp == ifp)
   91                         return (rti);
   92         }
   93 
   94         rti = (struct router_info *)malloc(sizeof(struct router_info),
   95                                            M_MRTABLE, M_NOWAIT);
   96         if (rti == NULL)
   97                 return (NULL);
   98         rti->rti_ifp = ifp;
   99         rti->rti_type = IGMP_v2_ROUTER;
  100         rti->rti_next = rti_head;
  101         rti_head = rti;
  102         return (rti);
  103 }
  104 
  105 void
  106 rti_delete(ifp)
  107         struct ifnet *ifp;
  108 {
  109         struct router_info *rti, **prti = &rti_head;
  110 
  111         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
  112                 if (rti->rti_ifp == ifp) {
  113                         *prti = rti->rti_next;
  114                         free(rti, M_MRTABLE);
  115                         break;
  116                 }
  117                 prti = &rti->rti_next;
  118         }
  119 }
  120 
  121 void
  122 igmp_input(struct mbuf *m, ...)
  123 {
  124         int iphlen;
  125         struct ifnet *ifp = m->m_pkthdr.rcvif;
  126         struct ip *ip = mtod(m, struct ip *);
  127         struct igmp *igmp;
  128         int igmplen;
  129         int minlen;
  130         struct in_multi *inm;
  131         struct in_multistep step;
  132         struct router_info *rti;
  133         struct in_ifaddr *ia;
  134         int timer;
  135         va_list ap;
  136 
  137         va_start(ap, m);
  138         iphlen = va_arg(ap, int);
  139         va_end(ap);
  140 
  141         ++igmpstat.igps_rcv_total;
  142 
  143         igmplen = ntohs(ip->ip_len) - iphlen;
  144 
  145         /*
  146          * Validate lengths
  147          */
  148         if (igmplen < IGMP_MINLEN) {
  149                 ++igmpstat.igps_rcv_tooshort;
  150                 m_freem(m);
  151                 return;
  152         }
  153         minlen = iphlen + IGMP_MINLEN;
  154         if ((m->m_flags & M_EXT || m->m_len < minlen) &&
  155             (m = m_pullup(m, minlen)) == NULL) {
  156                 ++igmpstat.igps_rcv_tooshort;
  157                 return;
  158         }
  159 
  160         /*
  161          * Validate checksum
  162          */
  163         m->m_data += iphlen;
  164         m->m_len -= iphlen;
  165         igmp = mtod(m, struct igmp *);
  166         if (in_cksum(m, igmplen)) {
  167                 ++igmpstat.igps_rcv_badsum;
  168                 m_freem(m);
  169                 return;
  170         }
  171         m->m_data -= iphlen;
  172         m->m_len += iphlen;
  173         ip = mtod(m, struct ip *);
  174 
  175         switch (igmp->igmp_type) {
  176 
  177         case IGMP_HOST_MEMBERSHIP_QUERY:
  178                 ++igmpstat.igps_rcv_queries;
  179 
  180                 if (ifp->if_flags & IFF_LOOPBACK)
  181                         break;
  182 
  183                 if (igmp->igmp_code == 0) {
  184                         rti = rti_find(ifp);
  185                         if (rti == NULL) {
  186                                 m_freem(m);
  187                                 return;
  188                         }
  189                         rti->rti_type = IGMP_v1_ROUTER;
  190                         rti->rti_age = 0;
  191 
  192                         if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
  193                                 ++igmpstat.igps_rcv_badqueries;
  194                                 m_freem(m);
  195                                 return;
  196                         }
  197 
  198                         /*
  199                          * Start the timers in all of our membership records
  200                          * for the interface on which the query arrived,
  201                          * except those that are already running and those
  202                          * that belong to a "local" group (224.0.0.X).
  203                          */
  204                         IN_FIRST_MULTI(step, inm);
  205                         while (inm != NULL) {
  206                                 if (inm->inm_ia->ia_ifp == ifp &&
  207                                     inm->inm_timer == 0 &&
  208                                     !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
  209                                         inm->inm_state = IGMP_DELAYING_MEMBER;
  210                                         inm->inm_timer = IGMP_RANDOM_DELAY(
  211                                             IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
  212                                         igmp_timers_are_running = 1;
  213                                 }
  214                                 IN_NEXT_MULTI(step, inm);
  215                         }
  216                 } else {
  217                         if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
  218                                 ++igmpstat.igps_rcv_badqueries;
  219                                 m_freem(m);
  220                                 return;
  221                         }
  222 
  223                         timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
  224                         if (timer == 0)
  225                                 timer = 1;
  226 
  227                         /*
  228                          * Start the timers in all of our membership records
  229                          * for the interface on which the query arrived,
  230                          * except those that are already running and those
  231                          * that belong to a "local" group (224.0.0.X).  For
  232                          * timers already running, check if they need to be
  233                          * reset.
  234                          */
  235                         IN_FIRST_MULTI(step, inm);
  236                         while (inm != NULL) {
  237                                 if (inm->inm_ia->ia_ifp == ifp &&
  238                                     !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  239                                     (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
  240                                      ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
  241                                         switch (inm->inm_state) {
  242                                         case IGMP_DELAYING_MEMBER:
  243                                                 if (inm->inm_timer <= timer)
  244                                                         break;
  245                                                 /* FALLTHROUGH */
  246                                         case IGMP_IDLE_MEMBER:
  247                                         case IGMP_LAZY_MEMBER:
  248                                         case IGMP_AWAKENING_MEMBER:
  249                                                 inm->inm_state =
  250                                                     IGMP_DELAYING_MEMBER;
  251                                                 inm->inm_timer =
  252                                                     IGMP_RANDOM_DELAY(timer);
  253                                                 igmp_timers_are_running = 1;
  254                                                 break;
  255                                         case IGMP_SLEEPING_MEMBER:
  256                                                 inm->inm_state =
  257                                                     IGMP_AWAKENING_MEMBER;
  258                                                 break;
  259                                         }
  260                                 }
  261                                 IN_NEXT_MULTI(step, inm);
  262                         }
  263                 }
  264 
  265                 break;
  266 
  267         case IGMP_v1_HOST_MEMBERSHIP_REPORT:
  268                 ++igmpstat.igps_rcv_reports;
  269 
  270                 if (ifp->if_flags & IFF_LOOPBACK)
  271                         break;
  272 
  273                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
  274                     igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
  275                         ++igmpstat.igps_rcv_badreports;
  276                         m_freem(m);
  277                         return;
  278                 }
  279 
  280                 /*
  281                  * KLUDGE: if the IP source address of the report has an
  282                  * unspecified (i.e., zero) subnet number, as is allowed for
  283                  * a booting host, replace it with the correct subnet number
  284                  * so that a process-level multicast routing daemon can
  285                  * determine which subnet it arrived from.  This is necessary
  286                  * to compensate for the lack of any way for a process to
  287                  * determine the arrival interface of an incoming packet.
  288                  */
  289                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
  290                         IFP_TO_IA(ifp, ia);
  291                         if (ia)
  292                                 ip->ip_src.s_addr = ia->ia_subnet;
  293                 }
  294 
  295                 /*
  296                  * If we belong to the group being reported, stop
  297                  * our timer for that group.
  298                  */
  299                 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
  300                 if (inm != NULL) {
  301                         inm->inm_timer = 0;
  302                         ++igmpstat.igps_rcv_ourreports;
  303 
  304                         switch (inm->inm_state) {
  305                         case IGMP_IDLE_MEMBER:
  306                         case IGMP_LAZY_MEMBER:
  307                         case IGMP_AWAKENING_MEMBER:
  308                         case IGMP_SLEEPING_MEMBER:
  309                                 inm->inm_state = IGMP_SLEEPING_MEMBER;
  310                                 break;
  311                         case IGMP_DELAYING_MEMBER:
  312                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
  313                                         inm->inm_state = IGMP_LAZY_MEMBER;
  314                                 else
  315                                         inm->inm_state = IGMP_SLEEPING_MEMBER;
  316                                 break;
  317                         }
  318                 }
  319 
  320                 break;
  321 
  322         case IGMP_v2_HOST_MEMBERSHIP_REPORT:
  323 #ifdef MROUTING
  324                 /*
  325                  * Make sure we don't hear our own membership report.  Fast
  326                  * leave requires knowing that we are the only member of a
  327                  * group.
  328                  */
  329                 IFP_TO_IA(ifp, ia);
  330                 if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
  331                         break;
  332 #endif
  333 
  334                 ++igmpstat.igps_rcv_reports;
  335 
  336                 if (ifp->if_flags & IFF_LOOPBACK)
  337                         break;
  338 
  339                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
  340                     igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
  341                         ++igmpstat.igps_rcv_badreports;
  342                         m_freem(m);
  343                         return;
  344                 }
  345 
  346                 /*
  347                  * KLUDGE: if the IP source address of the report has an
  348                  * unspecified (i.e., zero) subnet number, as is allowed for
  349                  * a booting host, replace it with the correct subnet number
  350                  * so that a process-level multicast routing daemon can
  351                  * determine which subnet it arrived from.  This is necessary
  352                  * to compensate for the lack of any way for a process to
  353                  * determine the arrival interface of an incoming packet.
  354                  */
  355                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
  356 #ifndef MROUTING
  357                         IFP_TO_IA(ifp, ia);
  358 #endif
  359                         if (ia)
  360                                 ip->ip_src.s_addr = ia->ia_subnet;
  361                 }
  362 
  363                 /*
  364                  * If we belong to the group being reported, stop
  365                  * our timer for that group.
  366                  */
  367                 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
  368                 if (inm != NULL) {
  369                         inm->inm_timer = 0;
  370                         ++igmpstat.igps_rcv_ourreports;
  371 
  372                         switch (inm->inm_state) {
  373                         case IGMP_DELAYING_MEMBER:
  374                         case IGMP_IDLE_MEMBER:
  375                         case IGMP_AWAKENING_MEMBER:
  376                                 inm->inm_state = IGMP_LAZY_MEMBER;
  377                                 break;
  378                         case IGMP_LAZY_MEMBER:
  379                         case IGMP_SLEEPING_MEMBER:
  380                                 break;
  381                         }
  382                 }
  383 
  384                 break;
  385 
  386         }
  387 
  388         /*
  389          * Pass all valid IGMP packets up to any process(es) listening
  390          * on a raw IGMP socket.
  391          */
  392         rip_input(m);
  393 }
  394 
  395 void
  396 igmp_joingroup(inm)
  397         struct in_multi *inm;
  398 {
  399         int i, s = splsoftnet();
  400 
  401         inm->inm_state = IGMP_IDLE_MEMBER;
  402 
  403         if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  404             (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0) {
  405                 if ((i = rti_fill(inm)) == -1) {
  406                         splx(s);
  407                         return;
  408                 }
  409                 igmp_sendpkt(inm, i, 0);
  410                 inm->inm_state = IGMP_DELAYING_MEMBER;
  411                 inm->inm_timer = IGMP_RANDOM_DELAY(
  412                     IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
  413                 igmp_timers_are_running = 1;
  414         } else
  415                 inm->inm_timer = 0;
  416         splx(s);
  417 }
  418 
  419 void
  420 igmp_leavegroup(inm)
  421         struct in_multi *inm;
  422 {
  423 
  424         switch (inm->inm_state) {
  425         case IGMP_DELAYING_MEMBER:
  426         case IGMP_IDLE_MEMBER:
  427                 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  428                     (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0)
  429                         if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
  430                                 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE,
  431                                     INADDR_ALLROUTERS_GROUP);
  432                 break;
  433         case IGMP_LAZY_MEMBER:
  434         case IGMP_AWAKENING_MEMBER:
  435         case IGMP_SLEEPING_MEMBER:
  436                 break;
  437         }
  438 }
  439 
  440 void
  441 igmp_fasttimo()
  442 {
  443         struct in_multi *inm;
  444         struct in_multistep step;
  445         int s;
  446 
  447         /*
  448          * Quick check to see if any work needs to be done, in order
  449          * to minimize the overhead of fasttimo processing.
  450          */
  451         if (!igmp_timers_are_running)
  452                 return;
  453 
  454         s = splsoftnet();
  455         igmp_timers_are_running = 0;
  456         IN_FIRST_MULTI(step, inm);
  457         while (inm != NULL) {
  458                 if (inm->inm_timer == 0) {
  459                         /* do nothing */
  460                 } else if (--inm->inm_timer == 0) {
  461                         if (inm->inm_state == IGMP_DELAYING_MEMBER) {
  462                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
  463                                         igmp_sendpkt(inm,
  464                                             IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
  465                                 else
  466                                         igmp_sendpkt(inm,
  467                                             IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
  468                                 inm->inm_state = IGMP_IDLE_MEMBER;
  469                         }
  470                 } else {
  471                         igmp_timers_are_running = 1;
  472                 }
  473                 IN_NEXT_MULTI(step, inm);
  474         }
  475         splx(s);
  476 }
  477 
  478 void
  479 igmp_slowtimo()
  480 {
  481         struct router_info *rti;
  482         int s;
  483 
  484         s = splsoftnet();
  485         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
  486                 if (rti->rti_type == IGMP_v1_ROUTER &&
  487                     ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
  488                         rti->rti_type = IGMP_v2_ROUTER;
  489                 }
  490         }
  491         splx(s);
  492 }
  493 
  494 void
  495 igmp_sendpkt(inm, type, addr)
  496         struct in_multi *inm;
  497         int type;
  498         in_addr_t addr;
  499 {
  500         struct mbuf *m;
  501         struct igmp *igmp;
  502         struct ip *ip;
  503         struct ip_moptions imo;
  504 #ifdef MROUTING
  505         extern struct socket *ip_mrouter;
  506 #endif /* MROUTING */
  507 
  508         MGETHDR(m, M_DONTWAIT, MT_HEADER);
  509         if (m == NULL)
  510                 return;
  511         /*
  512          * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
  513          * is smaller than mbuf size returned by MGETHDR.
  514          */
  515         m->m_data += max_linkhdr;
  516         m->m_len = sizeof(struct ip) + IGMP_MINLEN;
  517         m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
  518 
  519         ip = mtod(m, struct ip *);
  520         ip->ip_tos = 0;
  521         ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
  522         ip->ip_off = 0;
  523         ip->ip_p = IPPROTO_IGMP;
  524         ip->ip_src.s_addr = INADDR_ANY;
  525         if (addr) {
  526                 ip->ip_dst.s_addr = addr;
  527         } else {
  528                 ip->ip_dst = inm->inm_addr;
  529         }
  530 
  531         m->m_data += sizeof(struct ip);
  532         m->m_len -= sizeof(struct ip);
  533         igmp = mtod(m, struct igmp *);
  534         igmp->igmp_type = type;
  535         igmp->igmp_code = 0;
  536         igmp->igmp_group = inm->inm_addr;
  537         igmp->igmp_cksum = 0;
  538         igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
  539         m->m_data -= sizeof(struct ip);
  540         m->m_len += sizeof(struct ip);
  541 
  542         imo.imo_multicast_ifp = inm->inm_ia->ia_ifp;
  543         imo.imo_multicast_ttl = 1;
  544 #ifdef RSVP_ISI
  545         imo.imo_multicast_vif = -1;
  546 #endif
  547         /*
  548          * Request loopback of the report if we are acting as a multicast
  549          * router, so that the process-level routing daemon can hear it.
  550          */
  551 #ifdef MROUTING
  552         imo.imo_multicast_loop = (ip_mrouter != NULL);
  553 #else
  554         imo.imo_multicast_loop = 0;
  555 #endif /* MROUTING */
  556 
  557         ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
  558             &imo, (void *)NULL);
  559 
  560         ++igmpstat.igps_snd_reports;
  561 }

/* [<][>][^][v][top][bottom][index][help] */