Example #1
0
int igmpv1_input(PACKET p)
{
   u_char igmp_type;
   ip_addr igmp_group;
   struct in_multi * inm;
   NET netp  = p->net;
   int rc;

   /* extract IGMP type from received packet */
   rc = cb_write(p, 0, (uint8_t *) &igmp_type, sizeof(igmp_type));
   if (rc != sizeof(igmp_type))
   {
      LOCK_NET_RESOURCE(FREEQ_RESID);
      PK_FREE(p);
      UNLOCK_NET_RESOURCE(FREEQ_RESID);
      return (IGMP_ERR);
   }

   switch (igmp_type) 
   {
   case IGMP_HOST_MEMBERSHIP_QUERY:
      ++igmpstats.igmpv1mode_v1_queries_rcvd;
      /*
       * Start the timers in all of our membership records for
       * the interface on which the query arrived, except those
       * that are already running and those that belong to the
       * "all-hosts" group.
       */
      for (inm = netp->mc_list; inm; inm = inm->inm_next)
      {
         /* skip all IPv6 entries - they are indicated by 
          * an IPv4 address field of 0 */
         if (inm->inm_addr == 0)
            continue;
         /* skip IPv4 multicast address of 224.0.0.1 (note that
          * the IPv4 address stored in inm_addr is in network 
          * byte order */
         if (inm->inm_addr != igmp_all_hosts_group)
         {
            if (inm->inm_timer == 0)
            {
               inm->inm_timer = (unsigned) IGMP_RANDOM_DELAY(inm->inm_addr);
               /* increment the count of running timers */
               ++igmp_timers_are_running;            
            }   
         }
      }
      rc = IGMP_OK;
      break;

   case IGMP_HOST_MEMBERSHIP_REPORT:
      ++igmpstats.igmpv1mode_v1_reports_rcvd;
      /*
       * If we belong to the group being reported and have a 
       * running timer for that group, stop our timer for that 
       * group.
       */
      /* extract IGMP group from received packet */
      rc = cb_write(p, IGMP_GRP_OFFSET, (uint8_t *) &igmp_group, sizeof(igmp_group));
      if (rc != sizeof(igmp_group))
      {
         rc = IGMP_ERR;
         break;
      }
      inm = lookup_mcast(igmp_group, netp);
      if (inm != NULL) 
      {
         if (inm->inm_timer > 0)
         {
            inm->inm_timer = 0;
            /* decrement the count of running timers */
            --igmp_timers_are_running;
            ++igmpstats.igmpv1mode_v1_reports_rcvd_canceled_timer;
         }
      }
      rc = IGMP_OK;
      break;
      
   default:
      ++igmpstats.igmpv1mode_unknown_pkttype;
      rc = IGMP_ERR;
      break;   
   }

   /* we're done with the received packet; return packet buffer back 
    * to free pool */
   LOCK_NET_RESOURCE(FREEQ_RESID);
   PK_FREE(p);
   UNLOCK_NET_RESOURCE(FREEQ_RESID);
      
   return rc;
}
Example #2
0
int igmpv2_process_report (PACKET p)
{
   struct igmp * igmp;
   struct ip * pip;
   NET netp;
   struct in_multi * inm;

   netp = p->net;
   pip = ip_head (p);
   igmp = (struct igmp *) (ip_data (pip));   
   
   /* If we receive a IGMP (v1 or v2) report for a multicast group 
    * that we have a timer running for, the IGMPv2 specification 
    * requires that we stop the timer (and thereby cancel the 
    * future transmission of our report).
    
    * However, we will use the following table to guide our actions
    * instead.  Scenario #4 causes us to not cancel the timer, 
    * since we have received a IGMPv2 report, but we believe that 
    * the querier on the network is running IGMPv1, and therefore 
    * will not be able to understand the IGMPv2 report.  As a 
    * result, we let our timer run, and if it expires, we will 
    * send out a report.

    * The type of a received report can be determined by examining 
    * the first byte of the IGMP message.  This byte is 0x12 for a 
    * IGMPv1 Host Membership Report, and 0x16 for a Version 2 
    * Membership Report.
    
    * Scenario# igmpv1_rtr_present Type of rcvd report Cancel timer
    * =============================================================
    * 1         No                 IGMPv1              Yes 
    * 2         No                 IGMPv2              Yes
    * 3         Yes                IGMPv1              Yes
    * 4         Yes                IGMPv2              No

    * In scenario #1 and #2, we have a IGMPv2-capable router that 
    * can understand both IGMPv1 and IGMPv2 reports.  In scenario 
    * #3, we have a IGMPv1-capable router that can understand IGMPv1 
    * reports. In scenario #4, we have a IGMPv1-capable router that
    * cannot understand a IGMPv2 report.  It is possible that the 
    * IGMPv1-capable router in scenario #4 is also capable of 
    * processing IGMPv2 packets (it has "downgraded" itself because
    * there are IGMPv1 routers on that network); however, we do not
    * know that, and hence we don't cancel our timer (for the 
    * subsequent transmission of a IGMPv1 report).
    */
   inm = lookup_mcast(igmp->igmp_group, netp);
   if (inm != NULL) 
   {
      if (inm->inm_timer != 0)
      {
         /* we have a timer running */
         if (!(netp->igmpv1_rtr_present && 
             igmp->igmp_type == IGMPv2_MEMBERSHIP_REPORT))
         {
            /* cancel timer */
            inm->inm_timer = 0;
            /* decrement the count of running timers */
            --igmp_timers_are_running;
            /* indicate that we are not the last host to send a 
             * report for this group */
            inm->last2send_report = IGMP_FALSE;
            ++igmpstats.igmpv2mode_v12_reports_rcvd_canceled_timer;
         }
      }
      else
      {
         /* we don't have a timer running; perhaps the source
          * host has just joined the group, and has sent an
          * unsolicited report */
         ++igmpstats.igmpv2mode_v12_reports_rcvd_no_timer;   
      }
   }
   else 
   {
      /* since the Reports are sent the group address, we should
       * never receive them unless we are a member of that group
       * on that interface.  Even if imperfect filtering at the 
       * device level causes reports for unregistered groups to 
       * be passed up to the IP module, ip_rcv_phase2 () is 
       * responsible for dropping them, and so we should never
       * receive such packets. */
      ++igmpstats.igmpv2mode_v12_unknown_grp_reports_rcvd;
   }
   
   return IGMP_OK;   
}
Example #3
0
int igmpv2_process_query (PACKET p)
{
   struct igmp * igmp;
   struct ip * pip;
   NET netp;
   u_short max_resp_time;
   u_char process_all;
   struct in_multi * inm;
   ip_addr mcgrp_addr;

   netp = p->net;
   pip = ip_head (p);
   igmp = (struct igmp *) (ip_data (pip));
   mcgrp_addr = ntohl(igmp->igmp_group);

   if (igmp->igmp_code == 0)
   {
      /* this is a IGMPv1 Host Membership Query */
      netp->igmpv1_rtr_present = IGMP_TRUE;
      netp->igmpv1_query_rcvd_time = cticks;      
      ++igmpstats.igmpv2mode_v1_queries_rcvd;
      /* set maximum time to respond to the equivalent of 10 
       * seconds worth of "ticks" (the timeout routine is
       * intended to be invoked PR_FASTHZ (5) times a second,
       * so each tick is equal to 200 ms) */
      max_resp_time = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ;
      process_all = IGMP_TRUE;
   }
   else
   {
      /* this is either a IGMPv2 General Query or 
       * a IGMPv2 Group-Specific Query */
      if (igmp->igmp_group == 0)
      {
         /* this is a IGMPv2 General Query */
         ++igmpstats.igmpv2mode_v2_general_queries_rcvd;
         process_all = IGMP_TRUE;
      }
      else
      {
         /* this is a IGMPv2 Group-Specific Query */       
         ++igmpstats.igmpv2mode_v2_grp_specific_queries_rcvd;
         process_all = IGMP_FALSE;
      }
      
      /* irrespective of whether received message is a 
       * IGMPv2 General Query or a IGMPv2 Group-Specific Query,
       * set maximum time to respond to value extracted 
       * from received message. The value in the message
       * is in tenths of a second.  max_resp_time is in
       * units of ticks (where one tick is 200 ms) */
      max_resp_time = (igmp->igmp_code * PR_FASTHZ) / 10;
   }
   
   /* process all entries in a link's multicast address linked
    * list (pointed to by mc_list) as part of the response to
    * the received IGMPv1 Host Membership Query or IGMPv2 General
    * Query message */
   if (process_all)
   {
      for (inm = netp->mc_list; inm; inm = inm->inm_next)
      {
         /* skip all IPv6 entries - they are indicated by 
          * an IPv4 address field of 0 */
         if (!(inm->inm_addr)) continue;
         /* skip IPv4 multicast address of 224.0.0.1 (note that
          * the IPv4 address stored in inm_addr is in network 
          * byte order */
         if (inm->inm_addr != igmp_all_hosts_group)
            igmpv2_chk_set_timer (inm, max_resp_time);
      } /* end FOR (iterate thru' mc_list on net) */
   } /* end IF (process all) */
   else
   {
      /* process one (for IGMPv2 Group-Specific Query) entry (the 
       * one that corresponds to the address listed in the received 
       * query) - it should be present in the link's multicast
       * address list */
      inm = lookup_mcast(igmp->igmp_group, netp);
      if (inm != NULL)
         igmpv2_chk_set_timer (inm, max_resp_time);
      else ++igmpstats.igmpv2mode_v2_unknown_grp_specific_queries_rcvd;
   } /* end ELSE (process ALL) */
   
   /* return success; caller will the received packet back to the 
    * free pool */
   return IGMP_OK;
}
Example #4
0
struct in_multi * 
in_addmulti(ip_addr *ap, struct net *netp, int addrtype)
{
   struct in_multi *inm = (struct in_multi *)NULL;
   int error;

   /* check for good addr. */
   if ((ap == (ip_addr *)NULL) || (*ap == 0))
      return ((struct in_multi *)NULL);  

   ENTER_CRIT_SECTION(netp);

   /* See if address already in list. */
#ifdef IP_V6
   if(addrtype == 6)
      inm = v6_lookup_mcast((ip6_addr*)ap, netp);
#endif
#ifdef IP_V4
   if(addrtype != 6)
      inm = lookup_mcast(*ap, netp);
#endif

   if (inm != (struct in_multi *)NULL) 
   {
      /* Found it; just increment the reference count. */
      ++inm->inm_refcount;
   }
   else
   {
      /*
       * New address; allocate a new multicast record
       * and link it into the interface's multicast list.
       */
      inm = (struct in_multi *)INM_ALLOC(sizeof(*inm));

      if (inm == (struct in_multi *)NULL) 
      {
         EXIT_CRIT_SECTION(netp);
         return ((struct in_multi *)NULL);
      }
#ifdef IP_V6
      if(addrtype == 6)
         IP6CPY(&inm->ip6addr, (struct in6_addr *)ap);
#endif
#ifdef IP_V4
      if(addrtype != 6)
         inm->inm_addr = *ap;
#endif
      inm->inm_netp = netp;
      inm->inm_refcount = 1;
      inm->inm_next = netp->mc_list;
      netp->mc_list = inm;

      /*
       * If net has a multicast address registration routine then ask
       * the network driver to update its multicast reception
       * filter appropriately for the new address.
       */
      if(netp->n_mcastlist)
         error = netp->n_mcastlist(inm);
      else
         error = 0;
#if defined (IGMP_V1) || defined (IGMP_V2)
      /*
       * Let IGMP know that we have joined a new IP multicast group.
       */
      if (inm->inm_addr) igmp_joingroup(inm);
#endif      
   }

   EXIT_CRIT_SECTION(netp);
   USE_ARG(error);

   return (inm);
}