]> git.proxmox.com Git - mirror_frr.git/blobdiff - pimd/pim_igmp.c
Merge remote-tracking branch 'origin/master' into pim_lib_work2
[mirror_frr.git] / pimd / pim_igmp.c
index ef1b3cbac0434bc8185eb796bf2216f2a805cc1c..a309de690bdd2686861ead68a778565c8837b77a 100644 (file)
   along with this program; see the file COPYING; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA 02110-1301 USA
+<<<<<<< HEAD
+  
+=======
+>>>>>>> origin/master
 */
 
 #include <zebra.h>
@@ -26,6 +30,7 @@
 
 #include "pimd.h"
 #include "pim_igmp.h"
+#include "pim_igmpv2.h"
 #include "pim_igmpv3.h"
 #include "pim_iface.h"
 #include "pim_sock.h"
 #include "pim_time.h"
 #include "pim_zebra.h"
 
-#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE        (1)
-#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE        (2)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
-#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES      (5)
-#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES      (6)
-
 static void group_timer_off(struct igmp_group *group);
 
+/* This socket is used for TXing IGMP packets only, IGMP RX happens
+ * in pim_mroute_msg()
+ */
 static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim_options)
 {
   int fd;
   int join = 0;
   struct in_addr group;
 
-  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1 /* loop=true */);
+  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1);
+
   if (fd < 0)
     return -1;
 
   if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
     if (inet_aton(PIM_ALL_ROUTERS, &group)) {
       if (!pim_socket_join(fd, group, ifaddr, ifindex))
-       ++join;
+       ++join;
     }
     else {
       zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
-               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
-               PIM_ALL_ROUTERS, errno, safe_strerror(errno));
+               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+               PIM_ALL_ROUTERS, errno, safe_strerror(errno));
     }
   }
 
@@ -76,8 +78,8 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim
   }
   else {
     zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
-             __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
-             PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
+             __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+             PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
   }
 
   if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
@@ -87,13 +89,13 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim
   }
   else {
       zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
-               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
-               PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
+               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+               PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
   }    
 
   if (!join) {
     zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
-            fd, inet_ntoa(ifaddr));
+            fd, inet_ntoa(ifaddr));
     close(fd);
     fd = -1;
   }
@@ -154,22 +156,20 @@ static int pim_igmp_other_querier_expire(struct thread *t)
 {
   struct igmp_sock *igmp;
 
-  zassert(t);
   igmp = THREAD_ARG(t);
-  zassert(igmp);
 
   zassert(igmp->t_other_querier_timer);
   zassert(!igmp->t_igmp_query_timer);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char ifaddr_str[100];
+    char ifaddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
     zlog_debug("%s: Querier %s resuming",
               __PRETTY_FUNCTION__,
               ifaddr_str);
   }
 
-  igmp->t_other_querier_timer = 0;
+  igmp->t_other_querier_timer = NULL;
 
   /*
     We are the current querier, then
@@ -198,7 +198,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
     */
 
     if (PIM_DEBUG_IGMP_TRACE) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
                 ifaddr_str);
@@ -210,7 +210,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
   else {
     /*
       We are the current querier, then stop sending general queries:
-      igmp->t_igmp_query_timer = 0;
+      igmp->t_igmp_query_timer = NULL;
     */
     pim_igmp_general_query_off(igmp);
   }
@@ -241,7 +241,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
                       pim_ifp->igmp_query_max_response_time_dsec);
   
   if (PIM_DEBUG_IGMP_TRACE) {
-    char ifaddr_str[100];
+    char ifaddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
     zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
               ifaddr_str,
@@ -260,7 +260,7 @@ void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
 
   if (PIM_DEBUG_IGMP_TRACE) {
     if (igmp->t_other_querier_timer) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
                 ifaddr_str, igmp->fd, igmp->interface->name);
@@ -270,31 +270,27 @@ void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
   zassert(!igmp->t_other_querier_timer);
 }
 
-static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
-                          int max_resp_code,
-                          struct in_addr from, const char *from_str,
-                          char *igmp_msg, int igmp_msg_len)
+static int
+igmp_recv_query(struct igmp_sock *igmp, int query_version,
+                int max_resp_code,
+                struct in_addr from, const char *from_str,
+                char *igmp_msg, int igmp_msg_len)
 {
   struct interface     *ifp;
   struct pim_interface *pim_ifp;
-  uint8_t               resv_s_qrv = 0;
-  uint8_t               s_flag = 0;
-  uint8_t               qrv = 0;
   struct in_addr        group_addr;
   uint16_t              recv_checksum;
   uint16_t              checksum;
-  int                   i;
 
-  //group_addr = *(struct in_addr *)(igmp_msg + 4);
   memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
 
   ifp = igmp->interface;
   pim_ifp = ifp->info;
 
-  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
+  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
 
   /* for computing checksum */
-  *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
+  *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
 
   checksum = in_cksum(igmp_msg, igmp_msg_len);
   if (checksum != recv_checksum) {
@@ -303,12 +299,33 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
     return -1;
   }
 
+  /* RFC 3376 defines some guidelines on operating in backwards compatibility
+   * with older versions of IGMP but there are some gaps in the logic:
+   *
+   * - once we drop from say version 3 to version 2 we will never go back to
+   *   version 3 even if the node that TXed an IGMP v2 query upgrades to v3
+   *
+   * - The node with the lowest IP is the querier so we will only know to drop
+   *   from v3 to v2 if the node that is the querier is also the one that is
+   *   running igmp v2.  If a non-querier only supports igmp v2 we will have
+   *   no way of knowing.
+   *
+   * For now we will simplify things and inform the user that they need to
+   * configure all PIM routers to use the same version of IGMP.
+   */
+  if (query_version != pim_ifp->igmp_version) {
+    zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please "
+              "configure all PIM routers on this subnet to use the same "
+              "IGMP version",
+              query_version, from_str, ifp->name, pim_ifp->igmp_version);
+    return 0;
+  }
+
   if (PIM_DEBUG_IGMP_PACKETS) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
-    zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
-              query_version, from_str, ifp->name,
-              igmp_msg_len, checksum, group_str);
+    zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
+              query_version, from_str, ifp->name, group_str);
   }
 
   /*
@@ -320,9 +337,9 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
     elected querier.
    */
   if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
-    
+
     if (PIM_DEBUG_IGMP_TRACE) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
                 ifp->name,
@@ -333,272 +350,11 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
     pim_igmp_other_querier_timer_on(igmp);
   }
 
+  /* IGMP version 3 is the only one where we process the RXed query */
   if (query_version == 3) {
-    /*
-      RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
-
-      Routers adopt the QRV value from the most recently received Query
-      as their own [Robustness Variable] value, unless that most
-      recently received QRV was zero, in which case the receivers use
-      the default [Robustness Variable] value specified in section 8.1
-      or a statically configured value.
-    */
-    resv_s_qrv = igmp_msg[8];
-    qrv = 7 & resv_s_qrv;
-    igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
-  }
-
-  /*
-    RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
-
-    Multicast routers that are not the current querier adopt the QQI
-    value from the most recently received Query as their own [Query
-    Interval] value, unless that most recently received QQI was zero,
-    in which case the receiving routers use the default.
-  */
-  if (igmp->t_other_querier_timer && query_version == 3) {
-    /* other querier present */
-    uint8_t  qqic;
-    uint16_t qqi;
-    qqic = igmp_msg[9];
-    qqi = igmp_msg_decode8to16(qqic);
-    igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
-
-    if (PIM_DEBUG_IGMP_TRACE) {
-      char ifaddr_str[100];
-      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
-      zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
-                ifaddr_str,
-                qqi ? "recv-non-default" : "default",
-                igmp->querier_query_interval,
-                qqic,
-                from_str);
-    }
-  }
-
-  /*
-    RFC 3376: 6.6.1. Timer Updates
-
-    When a router sends or receives a query with a clear Suppress
-    Router-Side Processing flag, it must update its timers to reflect
-    the correct timeout values for the group or sources being queried.
-
-    General queries don't trigger timer update.
-  */
-  if (query_version == 3) {
-    s_flag = (1 << 3) & resv_s_qrv;
-  }
-  else {
-    /* Neither V1 nor V2 have this field. Pimd should really go into
-     * a compatibility mode here and run as V2 (or V1) but it doesn't
-     * so for now, lets just set the flag to suppress these timer updates.
-     */
-    s_flag = 1;
-  }
-  
-  if (!s_flag) {
-    /* s_flag is clear */
-
-    if (PIM_INADDR_IS_ANY(group_addr)) {
-      /* this is a general query */
-
-      /* log that general query should have the s_flag set */
-      zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
-               query_version, from_str, ifp->name);
-    }
-    else {
-      struct igmp_group *group;
-
-      /* this is a non-general query: perform timer updates */
-
-      group = find_group_by_addr(igmp, group_addr);
-      if (group) {
-       int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
-
-       /*
-         RFC 3376: 6.6.1. Timer Updates
-         Query Q(G,A): Source Timer for sources in A are lowered to LMQT
-         Query Q(G): Group Timer is lowered to LMQT
-       */
-       if (recv_num_sources < 1) {
-         /* Query Q(G): Group Timer is lowered to LMQT */
-
-         igmp_group_timer_lower_to_lmqt(group);
-       }
-       else {
-         /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
-
-         /* Scan sources in query and lower their timers to LMQT */
-         struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
-         for (i = 0; i < recv_num_sources; ++i) {
-           //struct in_addr src_addr = sources[i];
-           //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
-           struct in_addr src_addr;
-           struct igmp_source *src;
-            memcpy(&src_addr, sources + i, sizeof(struct in_addr));
-           src = igmp_find_source_by_addr(group, src_addr);
-           if (src) {
-             igmp_source_timer_lower_to_lmqt(src);
-           }
-         }
-       }
-
-      }
-      else {
-       char group_str[100];
-       pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
-       zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
-                 query_version, from_str, ifp->name, group_str);
-      }
-    }
-  } /* s_flag is clear: timer updates */
-  
-  return 0;
-}
-
-static int igmp_v3_report(struct igmp_sock *igmp,
-                         struct in_addr from, const char *from_str,
-                         char *igmp_msg, int igmp_msg_len)
-{
-  uint16_t          recv_checksum;
-  uint16_t          checksum;
-  int               num_groups;
-  uint8_t          *group_record;
-  uint8_t          *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
-  struct interface *ifp = igmp->interface;
-  int               i;
-  int               local_ncb = 0;
-
-  if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
-    zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
-             from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
-    return -1;
-  }
-
-  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
-
-  /* for computing checksum */
-  *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
-
-  checksum = in_cksum(igmp_msg, igmp_msg_len);
-  if (checksum != recv_checksum) {
-    zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
-             from_str, ifp->name, recv_checksum, checksum);
-    return -1;
-  }
-
-  num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
-  if (num_groups < 1) {
-    zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
-             from_str, ifp->name);
-    return -1;
+    igmp_v3_recv_query(igmp, from_str, igmp_msg);
   }
 
-  if (PIM_DEBUG_IGMP_PACKETS) {
-    zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
-              from_str, ifp->name, igmp_msg_len, checksum, num_groups);
-  }
-
-  group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
-
-  /* Scan groups */
-  for (i = 0; i < num_groups; ++i) {
-    struct in_addr  rec_group;
-    uint8_t        *sources;
-    uint8_t        *src;
-    int             rec_type;
-    int             rec_auxdatalen;
-    int             rec_num_sources;
-    int             j;
-    struct prefix   lncb;
-    struct prefix   g;
-
-    if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
-      zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
-               from_str, ifp->name);
-      return -1;
-    }
-
-    rec_type        = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
-    rec_auxdatalen  = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
-    rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
-
-    //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
-    memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
-
-    if (PIM_DEBUG_IGMP_PACKETS) {
-      zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
-                from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
-    }
-
-    /* Scan sources */
-    
-    sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
-
-    for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
-
-      if ((src + 4) > report_pastend) {
-       zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
-                 from_str, ifp->name);
-       return -1;
-      }
-
-      if (PIM_DEBUG_IGMP_PACKETS) {
-       char src_str[200];
-
-       if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
-         sprintf(src_str, "<source?>");
-       
-       zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
-                  from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
-      }
-    } /* for (sources) */
-
-
-    lncb.family = AF_INET;
-    lncb.u.prefix4.s_addr = 0x000000E0;
-    lncb.prefixlen = 24;
-
-    g.family = AF_INET;
-    g.u.prefix4 = rec_group;
-    g.prefixlen = 32;
-    /*
-     * If we receive a igmp report with the group in 224.0.0.0/24
-     * then we should ignore it
-     */
-    if (prefix_match(&lncb, &g))
-      local_ncb = 1;
-
-    if (!local_ncb)
-      switch (rec_type) {
-      case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
-       igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
-       igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
-       igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
-       igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
-       igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
-       igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
-       break;
-      default:
-       zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
-                 from_str, ifp->name, rec_type);
-      }
-
-    group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
-    local_ncb = 0;
-
-  } /* for (group records) */
-
   return 0;
 }
 
@@ -606,73 +362,17 @@ static void on_trace(const char *label,
                     struct interface *ifp, struct in_addr from)
 {
   if (PIM_DEBUG_IGMP_TRACE) {
-    char from_str[100];
+    char from_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
     zlog_debug("%s: from %s on %s",
               label, from_str, ifp->name);
   }
 }
 
-static int igmp_v2_report(struct igmp_sock *igmp,
-                         struct in_addr from, const char *from_str,
-                         char *igmp_msg, int igmp_msg_len)
-{
-  struct interface *ifp = igmp->interface;
-  struct igmp_group *group;
-  struct in_addr group_addr;
-
-  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
-  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
-    zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
-             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
-    return -1;
-  }
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    zlog_warn("%s %s: FIXME WRITEME",
-             __FILE__, __PRETTY_FUNCTION__);
-  }
-
-  //group_addr = *(struct in_addr *)(igmp_msg + 4);
-  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
-
-  /* non-existant group is created as INCLUDE {empty} */
-  group = igmp_add_group_by_addr(igmp, group_addr);
-  if (!group) {
-    return -1;
-  }
-
-  group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
-
-  return 0;
-}
-
-static int igmp_v2_leave(struct igmp_sock *igmp,
-                        struct in_addr from, const char *from_str,
-                        char *igmp_msg, int igmp_msg_len)
-{
-  struct interface *ifp = igmp->interface;
-
-  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
-  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
-    zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
-             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
-    return -1;
-  }
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    zlog_warn("%s %s: FIXME WRITEME",
-             __FILE__, __PRETTY_FUNCTION__);
-  }
-
-  return 0;
-}
-
-static int igmp_v1_report(struct igmp_sock *igmp,
-                         struct in_addr from, const char *from_str,
-                         char *igmp_msg, int igmp_msg_len)
+static int
+igmp_v1_recv_report (struct igmp_sock *igmp,
+                     struct in_addr from, const char *from_str,
+                     char *igmp_msg, int igmp_msg_len)
 {
   struct interface *ifp = igmp->interface;
   struct igmp_group *group;
@@ -691,7 +391,6 @@ static int igmp_v1_report(struct igmp_sock *igmp,
              __FILE__, __PRETTY_FUNCTION__);
   }
 
-  //group_addr = *(struct in_addr *)(igmp_msg + 4);
   memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
 
   /* non-existant group is created as INCLUDE {empty} */
@@ -712,8 +411,8 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
   char *igmp_msg;
   int igmp_msg_len;
   int msg_type;
-  char from_str[100];
-  char to_str[100];
+  char from_str[INET_ADDRSTRLEN];
+  char to_str[INET_ADDRSTRLEN];
     
   if (len < sizeof(*ip_hdr)) {
     zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
@@ -790,26 +489,26 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
        return -1;
       }
 
-      return recv_igmp_query(igmp, query_version, max_resp_code,
+      return igmp_recv_query(igmp, query_version, max_resp_code,
                             ip_hdr->ip_src, from_str,
                             igmp_msg, igmp_msg_len);
     }
 
   case PIM_IGMP_V3_MEMBERSHIP_REPORT:
-    return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
-                         igmp_msg, igmp_msg_len);
+    return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
+                               igmp_msg, igmp_msg_len);
 
   case PIM_IGMP_V2_MEMBERSHIP_REPORT:
-    return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
-                         igmp_msg, igmp_msg_len);
+    return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
+                               igmp_msg, igmp_msg_len);
 
   case PIM_IGMP_V1_MEMBERSHIP_REPORT:
-    return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
-                         igmp_msg, igmp_msg_len);
+    return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
+                               igmp_msg, igmp_msg_len);
 
   case PIM_IGMP_V2_LEAVE_GROUP:
-    return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
-                        igmp_msg, igmp_msg_len);
+    return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
+                              igmp_msg, igmp_msg_len);
   }
 
   zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
@@ -825,9 +524,6 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp)
   int startup_mode;
   int query_interval;
 
-  zassert(igmp);
-  zassert(igmp->interface);
-
   /*
     Since this socket is starting as querier,
     there should not exist a timer for other-querier-present.
@@ -841,20 +537,31 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp)
 
     The Startup Query Interval is the interval between General Queries
     sent by a Querier on startup.  Default: 1/4 the Query Interval.
+    The first one should be sent out immediately instead of 125/4
+    seconds from now.
   */
   startup_mode = igmp->startup_query_count > 0;
   if (startup_mode) {
-    --igmp->startup_query_count;
+    /*
+     * If this is the first time we are sending a query on a
+     * newly configured igmp interface send it out in 1 second
+     * just to give the entire world a tiny bit of time to settle
+     * else the query interval is:
+     * query_interval = pim_ifp->igmp_default_query_interval >> 2;
+     */
+    if (igmp->startup_query_count == igmp->querier_robustness_variable)
+      query_interval = 1;
+    else
+      query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
 
-    /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
-    query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+    --igmp->startup_query_count;
   }
   else {
     query_interval = igmp->querier_query_interval;
   }
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char ifaddr_str[100];
+    char ifaddr_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
     zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
               ifaddr_str,
@@ -862,8 +569,7 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp)
               startup_mode ? "startup" : "non-startup",
               igmp->fd);
   }
-  igmp->t_igmp_query_timer = 0;
-  zassert(!igmp->t_igmp_query_timer);
+  igmp->t_igmp_query_timer = NULL;
   THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
                  pim_igmp_general_query,
                  igmp, query_interval);
@@ -875,35 +581,39 @@ void pim_igmp_general_query_off(struct igmp_sock *igmp)
 
   if (PIM_DEBUG_IGMP_TRACE) {
     if (igmp->t_igmp_query_timer) {
-      char ifaddr_str[100];
+      char ifaddr_str[INET_ADDRSTRLEN];
       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
       zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
                 ifaddr_str, igmp->fd, igmp->interface->name);
     }
   }
   THREAD_OFF(igmp->t_igmp_query_timer);
-  zassert(!igmp->t_igmp_query_timer);
 }
 
 /* Issue IGMP general query */
 static int pim_igmp_general_query(struct thread *t)
 {
-  char   query_buf[PIM_IGMP_BUFSIZE_WRITE];
   struct igmp_sock *igmp;
   struct in_addr dst_addr;
   struct in_addr group_addr;
   struct pim_interface *pim_ifp;
-
-  zassert(t);
+  int    query_buf_size;
 
   igmp = THREAD_ARG(t);
 
-  zassert(igmp);
   zassert(igmp->interface);
   zassert(igmp->interface->info);
 
   pim_ifp = igmp->interface->info;
 
+  if (pim_ifp->igmp_version == 3) {
+    query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
+  } else {
+    query_buf_size = IGMP_V12_MSG_SIZE;
+  }
+
+  char query_buf[query_buf_size];
+
   /*
     RFC3376: 4.1.12. IP Destination Addresses for Queries
 
@@ -917,8 +627,8 @@ static int pim_igmp_general_query(struct thread *t)
   group_addr.s_addr = PIM_NET_INADDR_ANY;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char querier_str[100];
-    char dst_str[100];
+    char querier_str[INET_ADDRSTRLEN];
+    char dst_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
                   sizeof(querier_str));
     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
@@ -926,124 +636,25 @@ static int pim_igmp_general_query(struct thread *t)
               querier_str, dst_str, igmp->interface->name);
   }
 
-  pim_igmp_send_membership_query(0 /* igmp_group */,
-                                igmp->fd,
-                                igmp->interface->name,
-                                query_buf,
-                                sizeof(query_buf),
-                                0 /* num_sources */,
-                                dst_addr,
-                                group_addr,
-                                pim_ifp->igmp_query_max_response_time_dsec,
-                                1 /* s_flag: always set for general queries */,
-                                igmp->querier_robustness_variable,
-                                igmp->querier_query_interval);
+  igmp_send_query (pim_ifp->igmp_version,
+                   0 /* igmp_group */,
+                   igmp->fd,
+                   igmp->interface->name,
+                   query_buf,
+                   sizeof(query_buf),
+                   0 /* num_sources */,
+                   dst_addr,
+                   group_addr,
+                   pim_ifp->igmp_query_max_response_time_dsec,
+                   1 /* s_flag: always set for general queries */,
+                   igmp->querier_robustness_variable,
+                   igmp->querier_query_interval);
 
   pim_igmp_general_query_on(igmp);
 
   return 0;
 }
 
-static int pim_igmp_read(struct thread *t);
-
-static void igmp_read_on(struct igmp_sock *igmp)
-{
-  zassert(igmp);
-
-  if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
-    zlog_debug("Scheduling READ event on IGMP socket fd=%d",
-              igmp->fd);
-  }
-  igmp->t_igmp_read = 0;
-  zassert(!igmp->t_igmp_read);
-  THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
-}
-
-static int pim_igmp_read(struct thread *t)
-{
-  struct igmp_sock *igmp;
-  int fd;
-  struct sockaddr_in from;
-  struct sockaddr_in to;
-  socklen_t fromlen = sizeof(from);
-  socklen_t tolen = sizeof(to);
-  uint8_t buf[PIM_IGMP_BUFSIZE_READ];
-  int len;
-  ifindex_t ifindex = -1;
-  int result = -1; /* defaults to bad */
-
-  zassert(t);
-
-  igmp = THREAD_ARG(t);
-
-  zassert(igmp);
-
-  fd = THREAD_FD(t);
-
-  zassert(fd == igmp->fd);
-
-  len = pim_socket_recvfromto(fd, buf, sizeof(buf),
-                             &from, &fromlen,
-                             &to, &tolen,
-                             &ifindex);
-  if (len < 0) {
-    zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
-             fd, errno, safe_strerror(errno));
-    goto done;
-  }
-
-  if (PIM_DEBUG_IGMP_PACKETS) {
-    char from_str[100];
-    char to_str[100];
-    
-    if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
-      sprintf(from_str, "<from?>");
-    if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
-      sprintf(to_str, "<to?>");
-    
-    zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
-              len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
-  }
-
-#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
-  /* ifindex sanity check */
-  if (ifindex != igmp->interface->ifindex) {
-    char from_str[100];
-    char to_str[100];
-    struct interface *ifp;
-
-    if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
-      sprintf(from_str, "<from?>");
-    if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
-      sprintf(to_str, "<to?>");
-
-    ifp = if_lookup_by_index(ifindex);
-    if (ifp) {
-      zassert(ifindex == ifp->ifindex);
-    }
-
-#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
-    zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
-             from_str, to_str, fd,
-             ifindex, ifp ? ifp->name : "<if-notfound>",
-             igmp->interface->ifindex, igmp->interface->name);
-#endif
-    goto done;
-  }
-#endif
-
-  if (pim_igmp_packet(igmp, (char *)buf, len)) {
-    goto done;
-  }
-
-  result = 0; /* good */
-
- done:
-  igmp_read_on(igmp);
-
-  return result;
-}
-
 static void sock_close(struct igmp_sock *igmp)
 {
   pim_igmp_other_querier_timer_off(igmp);
@@ -1057,7 +668,6 @@ static void sock_close(struct igmp_sock *igmp)
     }
   }
   THREAD_OFF(igmp->t_igmp_read);
-  zassert(!igmp->t_igmp_read);
   
   if (close(igmp->fd)) {
     zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
@@ -1106,7 +716,7 @@ static void igmp_group_delete(struct igmp_group *group)
   struct igmp_source *src;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Deleting IGMP group %s from socket %d interface %s",
               group_str,
@@ -1168,6 +778,22 @@ void igmp_sock_delete(struct igmp_sock *igmp)
   igmp_sock_free(igmp);
 }
 
+void
+igmp_sock_delete_all (struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *igmp_node, *igmp_nextnode;
+  struct igmp_sock *igmp;
+
+  pim_ifp = ifp->info;
+
+  for (ALL_LIST_ELEMENTS (pim_ifp->igmp_socket_list, igmp_node,
+                         igmp_nextnode, igmp))
+    {
+      igmp_sock_delete(igmp);
+    }
+}
+
 static struct igmp_sock *igmp_sock_new(int fd,
                                       struct in_addr ifaddr,
                                       struct interface *ifp)
@@ -1182,9 +808,9 @@ static struct igmp_sock *igmp_sock_new(int fd,
               fd, inet_ntoa(ifaddr), ifp->name);
   }
 
-  igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
+  igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
   if (!igmp) {
-    zlog_warn("%s %s: XMALLOC() failure",
+    zlog_warn("%s %s: XCALLOC() failure",
               __FILE__, __PRETTY_FUNCTION__);
     return 0;
   }
@@ -1200,9 +826,9 @@ static struct igmp_sock *igmp_sock_new(int fd,
   igmp->fd                          = fd;
   igmp->interface                   = ifp;
   igmp->ifaddr                      = ifaddr;
-  igmp->t_igmp_read                 = 0;
-  igmp->t_igmp_query_timer          = 0;
-  igmp->t_other_querier_timer       = 0; /* no other querier present */
+  igmp->t_igmp_read                 = NULL;
+  igmp->t_igmp_query_timer          = NULL;
+  igmp->t_other_querier_timer       = NULL; /* no other querier present */
   igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
   igmp->sock_creation               = pim_time_monotonic_sec();
 
@@ -1212,13 +838,63 @@ static struct igmp_sock *igmp_sock_new(int fd,
     igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
   */
   igmp_startup_mode_on(igmp);
-
-  igmp_read_on(igmp);
   pim_igmp_general_query_on(igmp);
 
   return igmp;
 }
 
+static void igmp_read_on (struct igmp_sock *igmp);
+
+static int
+pim_igmp_read (struct thread *t)
+{
+  uint8_t buf[10000];
+  struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t);
+  struct sockaddr_in from;
+  struct sockaddr_in to;
+  socklen_t fromlen = sizeof(from);
+  socklen_t tolen = sizeof(to);
+  ifindex_t ifindex = -1;
+  int cont = 1;
+  int len;
+
+  while (cont)
+    {
+      len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf),
+                                 &from, &fromlen,
+                                 &to, &tolen,
+                                 &ifindex);
+      if (len < 0)
+       {
+         if (errno == EINTR)
+           continue;
+         if (errno == EWOULDBLOCK || errno == EAGAIN)
+         {
+           cont = 0;
+           break;
+         }
+         goto done;
+       }
+    }
+
+ done:
+  igmp_read_on(igmp);
+  return 0;
+}
+
+static void
+igmp_read_on (struct igmp_sock *igmp)
+{
+
+  if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
+    zlog_debug("Scheduling READ event on IGMP socket fd=%d",
+              igmp->fd);
+  }
+  igmp->t_igmp_read = NULL;
+  THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
+
+}
+
 struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
                                    struct in_addr ifaddr,
                                    struct interface *ifp)
@@ -1244,6 +920,8 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
     return 0;
   }
 
+  igmp_read_on (igmp);
+
   listnode_add(igmp_sock_list, igmp);
 
 #ifdef IGMP_SOCK_DUMP
@@ -1271,12 +949,10 @@ static int igmp_group_timer(struct thread *t)
 {
   struct igmp_group *group;
 
-  zassert(t);
   group = THREAD_ARG(t);
-  zassert(group);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("%s: Timer for group %s on interface %s",
               __PRETTY_FUNCTION__,
@@ -1315,7 +991,7 @@ static void group_timer_off(struct igmp_group *group)
     return;
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Cancelling TIMER event for group %s on %s",
               group_str, group->group_igmp_sock->interface->name);
@@ -1331,7 +1007,7 @@ void igmp_group_timer_on(struct igmp_group *group,
   group_timer_off(group);
 
   if (PIM_DEBUG_IGMP_EVENTS) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
               interval_msec / 1000,
@@ -1377,6 +1053,19 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
     return group;
   }
 
+  if (!pim_is_group_224_4 (group_addr))
+    {
+      zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
+               __PRETTY_FUNCTION__);
+      return NULL;
+    }
+
+  if (pim_is_group_224_0_0_0_24 (group_addr))
+    {
+      zlog_warn("%s: Group specified is part of 224.0.0.0/24",
+               __PRETTY_FUNCTION__);
+      return NULL;
+    }
   /*
     Non-existant group is created as INCLUDE {empty}:
 
@@ -1390,11 +1079,11 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
     of INCLUDE and an empty source list.
   */
 
-  group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
+  group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
   if (!group) {
-    zlog_warn("%s %s: XMALLOC() failure",
+    zlog_warn("%s %s: XCALLOC() failure",
              __FILE__, __PRETTY_FUNCTION__);
-    return 0; /* error, not found, could not create */
+    return NULL; /* error, not found, could not create */
   }
 
   group->group_source_list = list_new();
@@ -1402,7 +1091,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
     zlog_warn("%s %s: list_new() failure",
              __FILE__, __PRETTY_FUNCTION__);
     XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
-    return 0; /* error, not found, could not initialize */
+    return NULL; /* error, not found, could not initialize */
   }
   group->group_source_list->del = (void (*)(void *)) igmp_source_free;
 
@@ -1414,6 +1103,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
   group->last_igmp_v1_report_dsec              = -1;
   group->last_igmp_v2_report_dsec              = -1;
   group->group_creation                        = pim_time_monotonic_sec();
+  group->igmp_version                          = IGMP_DEFAULT_VERSION;
 
   /* initialize new group as INCLUDE {empty} */
   group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
@@ -1421,7 +1111,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
   listnode_add(igmp->igmp_group_list, group);
 
   if (PIM_DEBUG_IGMP_TRACE) {
-    char group_str[100];
+    char group_str[INET_ADDRSTRLEN];
     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
     zlog_debug("Creating new IGMP group %s on socket %d interface %s",
               group_str, igmp->fd, igmp->interface->name);
@@ -1442,3 +1132,32 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
 
   return group;
 }
+
+void
+igmp_send_query (int igmp_version,
+                 struct igmp_group *group,
+                 int fd,
+                 const char *ifname,
+                 char *query_buf,
+                 int query_buf_size,
+                 int num_sources,
+                 struct in_addr dst_addr,
+                 struct in_addr group_addr,
+                 int query_max_response_time_dsec,
+                 uint8_t s_flag,
+                 uint8_t querier_robustness_variable,
+                 uint16_t querier_query_interval)
+{
+  if (igmp_version == 3) {
+    igmp_v3_send_query (group, fd, ifname, query_buf,
+                        query_buf_size, num_sources,
+                        dst_addr, group_addr,
+                        query_max_response_time_dsec, s_flag,
+                        querier_robustness_variable,
+                        querier_query_interval);
+  } else if (igmp_version == 2) {
+    igmp_v2_send_query (group, fd, ifname, query_buf,
+                        dst_addr, group_addr,
+                        query_max_response_time_dsec);
+  }
+}