]> git.proxmox.com Git - mirror_frr.git/blobdiff - pimd/pim_igmp.c
pimd: mtrace only IGMP sockets
[mirror_frr.git] / pimd / pim_igmp.c
index c883a2c8bb46c9052751de3db8e4240a6888be16..5e1aecc3a389cec533e495815141c8bd545ad022 100644 (file)
@@ -29,6 +29,7 @@
 #include "pim_igmp.h"
 #include "pim_igmpv2.h"
 #include "pim_igmpv3.h"
+#include "pim_igmp_mtrace.h"
 #include "pim_iface.h"
 #include "pim_sock.h"
 #include "pim_mroute.h"
@@ -43,62 +44,66 @@ static int pim_igmp_general_query(struct thread *t);
 /* 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, struct interface *ifp, uint32_t pim_options)
+static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
+                         uint32_t pim_options)
 {
-  int fd;
-  int join = 0;
-  struct in_addr group;
-
-  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 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, ifp->ifindex))
-       ++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));
-    }
-  }
-
-  /*
-    IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
-    IGMP routers must receive general queries for querier election.
-  */
-  if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
-    if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
-      ++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_SYSTEMS, errno, safe_strerror(errno));
-  }
-
-  if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
-    if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) {
-      ++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_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));
-    close(fd);
-    fd = -1;
-  }
-
-  return fd;
+       int fd;
+       int join = 0;
+       struct in_addr group;
+
+       fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 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, ifp->ifindex))
+                               ++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));
+               }
+       }
+
+       /*
+         IGMP routers periodically send IGMP general queries to
+         AllSystems=224.0.0.1
+         IGMP routers must receive general queries for querier election.
+       */
+       if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
+               if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
+                       ++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_SYSTEMS, errno, safe_strerror(errno));
+       }
+
+       if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
+               if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) {
+                       ++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_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));
+               close(fd);
+               fd = -1;
+       }
+
+       return fd;
 }
 
 #undef IGMP_SOCK_DUMP
@@ -106,828 +111,859 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp, uint32_t
 #ifdef IGMP_SOCK_DUMP
 static void igmp_sock_dump(array_t *igmp_sock_array)
 {
-  int size = array_size(igmp_sock_array);
-  for (int i = 0; i < size; ++i) {
-    
-    struct igmp_sock *igmp = array_get(igmp_sock_array, i);
-    
-    zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
-              __FILE__, __PRETTY_FUNCTION__,
-              i, size,
-              inet_ntoa(igmp->ifaddr),
-              igmp->fd);
-  }
+       int size = array_size(igmp_sock_array);
+       for (int i = 0; i < size; ++i) {
+
+               struct igmp_sock *igmp = array_get(igmp_sock_array, i);
+
+               zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", __FILE__,
+                          __PRETTY_FUNCTION__, i, size,
+                          inet_ntoa(igmp->ifaddr), igmp->fd);
+       }
 }
 #endif
 
 struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
                                              struct in_addr ifaddr)
 {
-  struct listnode  *sock_node;
-  struct igmp_sock *igmp;
+       struct listnode *sock_node;
+       struct igmp_sock *igmp;
 
 #ifdef IGMP_SOCK_DUMP
-  igmp_sock_dump(igmp_sock_list);
+       igmp_sock_dump(igmp_sock_list);
 #endif
 
-  for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
-    if (ifaddr.s_addr == igmp->ifaddr.s_addr)
-      return igmp;
+       for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
+               if (ifaddr.s_addr == igmp->ifaddr.s_addr)
+                       return igmp;
 
-  return 0;
+       return 0;
 }
 
-struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
-                                        int fd)
+struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd)
 {
-  struct listnode  *sock_node;
-  struct igmp_sock *igmp;
+       struct listnode *sock_node;
+       struct igmp_sock *igmp;
 
-  for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
-    if (fd == igmp->fd)
-      return igmp;
+       for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
+               if (fd == igmp->fd)
+                       return igmp;
 
-  return 0;
+       return 0;
 }
 
 static int pim_igmp_other_querier_expire(struct thread *t)
 {
-  struct igmp_sock *igmp;
-
-  igmp = THREAD_ARG(t);
-
-  zassert(!igmp->t_igmp_query_timer);
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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);
-  }
-
-  /*
-    We are the current querier, then
-    re-start sending general queries.
-    RFC 2236 - sec 7 Other Querier
-    present timer expired (Send General
-    Query, Set Gen. Query. timer)
-  */
-  pim_igmp_general_query(t);
-
-  return 0;
+       struct igmp_sock *igmp;
+
+       igmp = THREAD_ARG(t);
+
+       zassert(!igmp->t_igmp_query_timer);
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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);
+       }
+
+       /*
+         We are the current querier, then
+         re-start sending general queries.
+         RFC 2236 - sec 7 Other Querier
+         present timer expired (Send General
+         Query, Set Gen. Query. timer)
+       */
+       pim_igmp_general_query(t);
+
+       return 0;
 }
 
 void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
 {
-  long other_querier_present_interval_msec;
-  struct pim_interface *pim_ifp;
-
-  zassert(igmp);
-  zassert(igmp->interface);
-  zassert(igmp->interface->info);
-
-  pim_ifp = igmp->interface->info;
-
-  if (igmp->t_other_querier_timer) {
-    /*
-      There is other querier present already,
-      then reset the other-querier-present timer.
-    */
-
-    if (PIM_DEBUG_IGMP_TRACE) {
-      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);
-    }
-    THREAD_OFF(igmp->t_other_querier_timer);
-  }
-  else {
-    /*
-      We are the current querier, then stop sending general queries:
-      igmp->t_igmp_query_timer = NULL;
-    */
-    pim_igmp_general_query_off(igmp);
-  }
-
-  /*
-    Since this socket is starting the other-querier-present timer,
-    there should not be periodic query timer for this socket.
-   */
-  zassert(!igmp->t_igmp_query_timer);
-
-  /*
-    RFC 3376: 8.5. Other Querier Present Interval
-
-    The Other Querier Present Interval is the length of time that must
-    pass before a multicast router decides that there is no longer
-    another multicast router which should be the querier.  This value
-    MUST be ((the Robustness Variable) times (the Query Interval)) plus
-    (one half of one Query Response Interval).
-
-    other_querier_present_interval_msec = \
-      igmp->querier_robustness_variable * \
-      1000 * igmp->querier_query_interval + \
-      100 * (pim_ifp->query_max_response_time_dsec >> 1);
-  */
-  other_querier_present_interval_msec =
-    PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
-                      igmp->querier_query_interval,
-                      pim_ifp->igmp_query_max_response_time_dsec);
-  
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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,
-              other_querier_present_interval_msec / 1000,
-              other_querier_present_interval_msec % 1000);
-  }
-  
-  thread_add_timer_msec(master, pim_igmp_other_querier_expire, igmp,
-                        other_querier_present_interval_msec,
-                        &igmp->t_other_querier_timer);
+       long other_querier_present_interval_msec;
+       struct pim_interface *pim_ifp;
+
+       zassert(igmp);
+       zassert(igmp->interface);
+       zassert(igmp->interface->info);
+
+       pim_ifp = igmp->interface->info;
+
+       if (igmp->t_other_querier_timer) {
+               /*
+                 There is other querier present already,
+                 then reset the other-querier-present timer.
+               */
+
+               if (PIM_DEBUG_IGMP_TRACE) {
+                       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);
+               }
+               THREAD_OFF(igmp->t_other_querier_timer);
+       } else {
+               /*
+                 We are the current querier, then stop sending general queries:
+                 igmp->t_igmp_query_timer = NULL;
+               */
+               pim_igmp_general_query_off(igmp);
+       }
+
+       /*
+         Since this socket is starting the other-querier-present timer,
+         there should not be periodic query timer for this socket.
+        */
+       zassert(!igmp->t_igmp_query_timer);
+
+       /*
+         RFC 3376: 8.5. Other Querier Present Interval
+
+         The Other Querier Present Interval is the length of time that must
+         pass before a multicast router decides that there is no longer
+         another multicast router which should be the querier.  This value
+         MUST be ((the Robustness Variable) times (the Query Interval)) plus
+         (one half of one Query Response Interval).
+
+         other_querier_present_interval_msec = \
+           igmp->querier_robustness_variable * \
+           1000 * igmp->querier_query_interval + \
+           100 * (pim_ifp->query_max_response_time_dsec >> 1);
+       */
+       other_querier_present_interval_msec = PIM_IGMP_OQPI_MSEC(
+               igmp->querier_robustness_variable, igmp->querier_query_interval,
+               pim_ifp->igmp_query_max_response_time_dsec);
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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, other_querier_present_interval_msec / 1000,
+                       other_querier_present_interval_msec % 1000);
+       }
+
+       thread_add_timer_msec(master, pim_igmp_other_querier_expire, igmp,
+                             other_querier_present_interval_msec,
+                             &igmp->t_other_querier_timer);
 }
 
 void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
 {
-  zassert(igmp);
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    if (igmp->t_other_querier_timer) {
-      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);
-    }
-  }
-  THREAD_OFF(igmp->t_other_querier_timer);
+       zassert(igmp);
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               if (igmp->t_other_querier_timer) {
+                       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);
+               }
+       }
+       THREAD_OFF(igmp->t_other_querier_timer);
 }
 
-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)
+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;
-  struct in_addr        group_addr;
-  uint16_t              recv_checksum;
-  uint16_t              checksum;
-
-  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
-
-  ifp = igmp->interface;
-  pim_ifp = ifp->info;
-
-  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
-
-  /* for computing checksum */
-  *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
-
-  checksum = in_cksum(igmp_msg, igmp_msg_len);
-  if (checksum != recv_checksum) {
-    zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
-             query_version, from_str, ifp->name, recv_checksum, checksum);
-    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[INET_ADDRSTRLEN];
-    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
-    zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
-              query_version, from_str, ifp->name, group_str);
-  }
-
-  /*
-    RFC 3376: 6.6.2. Querier Election
-
-    When a router receives a query with a lower IP address, it sets
-    the Other-Querier-Present timer to Other Querier Present Interval
-    and ceases to send queries on the network if it was the previously
-    elected querier.
-   */
-  if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
-
-    if (PIM_DEBUG_IGMP_TRACE) {
-      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,
-                ifaddr_str, ntohl(igmp->ifaddr.s_addr),
-                from_str, ntohl(from.s_addr));
-    }
-
-    pim_igmp_other_querier_timer_on(igmp);
-  }
-
-  /* IGMP version 3 is the only one where we process the RXed query */
-  if (query_version == 3) {
-    igmp_v3_recv_query(igmp, from_str, igmp_msg);
-  }
-
-  return 0;
+       struct interface *ifp;
+       struct pim_interface *pim_ifp;
+       struct in_addr group_addr;
+       uint16_t recv_checksum;
+       uint16_t checksum;
+
+       if (igmp->mtrace_only)
+               return 0;
+
+       memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+       ifp = igmp->interface;
+       pim_ifp = ifp->info;
+
+       recv_checksum = *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET);
+
+       /* for computing checksum */
+       *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
+
+       checksum = in_cksum(igmp_msg, igmp_msg_len);
+       if (checksum != recv_checksum) {
+               zlog_warn(
+                       "Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
+                       query_version, from_str, ifp->name, recv_checksum,
+                       checksum);
+               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[INET_ADDRSTRLEN];
+               pim_inet4_dump("<group?>", group_addr, group_str,
+                              sizeof(group_str));
+               zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
+                          query_version, from_str, ifp->name, group_str);
+       }
+
+       /*
+         RFC 3376: 6.6.2. Querier Election
+
+         When a router receives a query with a lower IP address, it sets
+         the Other-Querier-Present timer to Other Querier Present Interval
+         and ceases to send queries on the network if it was the previously
+         elected querier.
+        */
+       if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
+
+               if (PIM_DEBUG_IGMP_TRACE) {
+                       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, ifaddr_str,
+                               ntohl(igmp->ifaddr.s_addr), from_str,
+                               ntohl(from.s_addr));
+               }
+
+               pim_igmp_other_querier_timer_on(igmp);
+       }
+
+       /* IGMP version 3 is the only one where we process the RXed query */
+       if (query_version == 3) {
+               igmp_v3_recv_query(igmp, from_str, igmp_msg);
+       }
+
+       return 0;
 }
 
-static void on_trace(const char *label,
-                    struct interface *ifp, struct in_addr from)
+static void on_trace(const char *label, struct interface *ifp,
+                    struct in_addr from)
 {
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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);
-  }
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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_v1_recv_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;
-  struct in_addr group_addr;
+       struct interface *ifp = igmp->interface;
+       struct igmp_group *group;
+       struct in_addr group_addr;
+
+       on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
 
-  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+       if (igmp->mtrace_only)
+               return 0;
 
-  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
-    zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
-             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
-    return -1;
-  }
+       if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+               zlog_warn(
+                       "Recv IGMP report v1 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__);
+       }
 
-  if (PIM_DEBUG_IGMP_TRACE) {
-    zlog_warn("%s %s: FIXME WRITEME",
-             __FILE__, __PRETTY_FUNCTION__);
-  }
+       memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
 
-  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+       if (pim_is_group_filtered(ifp->info, &group_addr))
+               return -1;
 
-  /* non-existant group is created as INCLUDE {empty} */
-  group = igmp_add_group_by_addr(igmp, group_addr);
-  if (!group) {
-    return -1;
-  }
+       /* non-existant group is created as INCLUDE {empty} */
+       group = igmp_add_group_by_addr(igmp, group_addr);
+       if (!group) {
+               return -1;
+       }
 
-  group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
+       group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
 
-  return 0;
+       return 0;
 }
 
 int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
 {
-  struct ip *ip_hdr;
-  size_t ip_hlen; /* ip header length in bytes */
-  char *igmp_msg;
-  int igmp_msg_len;
-  int msg_type;
-  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",
-             len, sizeof(*ip_hdr));
-    return -1;
-  }
-
-  ip_hdr = (struct ip *) buf;
-
-  pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str , sizeof(from_str));
-  pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str , sizeof(to_str));
-
-  ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
-
-  if (PIM_DEBUG_IGMP_PACKETS) {
-    zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
-              from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p);
-  }
-
-  igmp_msg = buf + ip_hlen;
-  msg_type = *igmp_msg;
-  igmp_msg_len = len - ip_hlen;
-
-  if (PIM_DEBUG_IGMP_PACKETS) {
-    zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
-              from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
-              igmp_msg_len);
-  }
-
-  if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
-    zlog_warn("IGMP message size=%d shorter than minimum=%d",
-             igmp_msg_len, PIM_IGMP_MIN_LEN);
-    return -1;
-  }
-
-  switch (msg_type) {
-  case PIM_IGMP_MEMBERSHIP_QUERY:
-    {
-      int max_resp_code = igmp_msg[1];
-      int query_version;
-
-      /*
-       RFC 3376: 7.1. Query Version Distinctions
-       IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
-       IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
-       IGMPv3 Query: length >= 12 octets
-      */
-
-      if (igmp_msg_len == 8) {
-       query_version = max_resp_code ? 2 : 1;
-      }
-      else if (igmp_msg_len >= 12) {
-       query_version = 3;
-      }
-      else {
-       zlog_warn("Unknown IGMP query version");
-       return -1;
-      }
+       struct ip *ip_hdr;
+       size_t ip_hlen; /* ip header length in bytes */
+       char *igmp_msg;
+       int igmp_msg_len;
+       int msg_type;
+       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", len,
+                         sizeof(*ip_hdr));
+               return -1;
+       }
+
+       ip_hdr = (struct ip *)buf;
+
+       pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
+       pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
 
-      return igmp_recv_query(igmp, query_version, max_resp_code,
-                            ip_hdr->ip_src, from_str,
-                            igmp_msg, igmp_msg_len);
-    }
+       ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
 
-  case PIM_IGMP_V3_MEMBERSHIP_REPORT:
-    return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
-                               igmp_msg, igmp_msg_len);
+       if (PIM_DEBUG_IGMP_PACKETS) {
+               zlog_debug(
+                       "Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
+                       from_str, to_str, igmp->interface->name, len, ip_hlen,
+                       ip_hdr->ip_p);
+       }
+
+       igmp_msg = buf + ip_hlen;
+       msg_type = *igmp_msg;
+       igmp_msg_len = len - ip_hlen;
+
+       if (PIM_DEBUG_IGMP_PACKETS) {
+               zlog_debug(
+                       "Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
+                       from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl,
+                       msg_type, igmp_msg_len);
+       }
 
-  case PIM_IGMP_V2_MEMBERSHIP_REPORT:
-    return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
-                               igmp_msg, igmp_msg_len);
+       if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
+               zlog_warn("IGMP message size=%d shorter than minimum=%d",
+                         igmp_msg_len, PIM_IGMP_MIN_LEN);
+               return -1;
+       }
 
-  case PIM_IGMP_V1_MEMBERSHIP_REPORT:
-    return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
-                               igmp_msg, igmp_msg_len);
+       switch (msg_type) {
+       case PIM_IGMP_MEMBERSHIP_QUERY: {
+               int max_resp_code = igmp_msg[1];
+               int query_version;
+
+               /*
+                 RFC 3376: 7.1. Query Version Distinctions
+                 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
+                 zero
+                 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
+                 non-zero
+                 IGMPv3 Query: length >= 12 octets
+               */
+
+               if (igmp_msg_len == 8) {
+                       query_version = max_resp_code ? 2 : 1;
+               } else if (igmp_msg_len >= 12) {
+                       query_version = 3;
+               } else {
+                       zlog_warn("Unknown IGMP query version");
+                       return -1;
+               }
+
+               return igmp_recv_query(igmp, query_version, max_resp_code,
+                                      ip_hdr->ip_src, from_str, igmp_msg,
+                                      igmp_msg_len);
+       }
 
-  case PIM_IGMP_V2_LEAVE_GROUP:
-    return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
-                              igmp_msg, igmp_msg_len);
-  }
+       case PIM_IGMP_V3_MEMBERSHIP_REPORT:
+               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_recv_report(igmp, ip_hdr->ip_src, from_str,
+                                          igmp_msg, igmp_msg_len);
+
+       case PIM_IGMP_V1_MEMBERSHIP_REPORT:
+               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_recv_leave(igmp, ip_hdr->ip_src, from_str,
+                                         igmp_msg, igmp_msg_len);
+
+       case PIM_IGMP_MTRACE_RESPONSE:
+               return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
+                                                from_str, igmp_msg,
+                                                igmp_msg_len);
+       case PIM_IGMP_MTRACE_QUERY_REQUEST:
+               return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
+                                               from_str, igmp_msg,
+                                               igmp_msg_len);
+       }
 
-  zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
+       zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
 
-  return -1;
+       return -1;
 }
 
 void pim_igmp_general_query_on(struct igmp_sock *igmp)
 {
-  struct pim_interface *pim_ifp;
-  int startup_mode;
-  int query_interval;
-
-  /*
-    Since this socket is starting as querier,
-    there should not exist a timer for other-querier-present.
-   */
-  zassert(!igmp->t_other_querier_timer);
-  pim_ifp = igmp->interface->info;
-  zassert(pim_ifp);
-
-  /*
-    RFC 3376: 8.6. Startup Query Interval
-
-    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) {
-    /*
-     * 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);
-
-    --igmp->startup_query_count;
-  }
-  else {
-    query_interval = igmp->querier_query_interval;
-  }
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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,
-              query_interval,
-              startup_mode ? "startup" : "non-startup",
-              igmp->fd);
-  }
-  igmp->t_igmp_query_timer = NULL;
-  thread_add_timer(master, pim_igmp_general_query, igmp, query_interval,
-                   &igmp->t_igmp_query_timer);
+       struct pim_interface *pim_ifp;
+       int startup_mode;
+       int query_interval;
+
+       /*
+         Since this socket is starting as querier,
+         there should not exist a timer for other-querier-present.
+        */
+       zassert(!igmp->t_other_querier_timer);
+       pim_ifp = igmp->interface->info;
+       zassert(pim_ifp);
+
+       /*
+         RFC 3376: 8.6. Startup Query Interval
+
+         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) {
+               /*
+                * 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);
+
+               --igmp->startup_query_count;
+       } else {
+               query_interval = igmp->querier_query_interval;
+       }
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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, query_interval,
+                       startup_mode ? "startup" : "non-startup", igmp->fd);
+       }
+       igmp->t_igmp_query_timer = NULL;
+       thread_add_timer(master, pim_igmp_general_query, igmp, query_interval,
+                        &igmp->t_igmp_query_timer);
 }
 
 void pim_igmp_general_query_off(struct igmp_sock *igmp)
 {
-  zassert(igmp);
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    if (igmp->t_igmp_query_timer) {
-      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);
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               if (igmp->t_igmp_query_timer) {
+                       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);
 }
 
 /* Issue IGMP general query */
 static int pim_igmp_general_query(struct thread *t)
 {
-  struct igmp_sock *igmp;
-  struct in_addr dst_addr;
-  struct in_addr group_addr;
-  struct pim_interface *pim_ifp;
-  int    query_buf_size;
-
-  igmp = THREAD_ARG(t);
-
-  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
-
-    In IGMPv3, General Queries are sent with an IP destination address
-    of 224.0.0.1, the all-systems multicast address.  Group-Specific
-    and Group-and-Source-Specific Queries are sent with an IP
-    destination address equal to the multicast address of interest.
-  */
-
-  dst_addr.s_addr   = htonl(INADDR_ALLHOSTS_GROUP);
-  group_addr.s_addr = PIM_NET_INADDR_ANY;
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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));
-    zlog_debug("Querier %s issuing IGMP general query to %s on %s",
-              querier_str, dst_str, igmp->interface->name);
-  }
-
-  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;
+       struct igmp_sock *igmp;
+       struct in_addr dst_addr;
+       struct in_addr group_addr;
+       struct pim_interface *pim_ifp;
+       int query_buf_size;
+
+       igmp = THREAD_ARG(t);
+
+       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
+
+         In IGMPv3, General Queries are sent with an IP destination address
+         of 224.0.0.1, the all-systems multicast address.  Group-Specific
+         and Group-and-Source-Specific Queries are sent with an IP
+         destination address equal to the multicast address of interest.
+       */
+
+       dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+       group_addr.s_addr = PIM_NET_INADDR_ANY;
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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));
+               zlog_debug("Querier %s issuing IGMP general query to %s on %s",
+                          querier_str, dst_str, igmp->interface->name);
+       }
+
+       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 void sock_close(struct igmp_sock *igmp)
 {
-  pim_igmp_other_querier_timer_off(igmp);
-  pim_igmp_general_query_off(igmp);
-
-  if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
-    if (igmp->t_igmp_read) {
-      zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
-                inet_ntoa(igmp->ifaddr), igmp->fd,
-                igmp->interface->name);
-    }
-  }
-  THREAD_OFF(igmp->t_igmp_read);
-  
-  if (close(igmp->fd)) {
-    zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
-            inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
-            errno, safe_strerror(errno));
-  }
-  
-  if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
-    zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
-              inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
-  }
+       pim_igmp_other_querier_timer_off(igmp);
+       pim_igmp_general_query_off(igmp);
+
+       if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
+               if (igmp->t_igmp_read) {
+                       zlog_debug(
+                               "Cancelling READ event on IGMP socket %s fd=%d on interface %s",
+                               inet_ntoa(igmp->ifaddr), igmp->fd,
+                               igmp->interface->name);
+               }
+       }
+       THREAD_OFF(igmp->t_igmp_read);
+
+       if (close(igmp->fd)) {
+               zlog_err(
+                       "Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
+                       inet_ntoa(igmp->ifaddr), igmp->fd,
+                       igmp->interface->name, errno, safe_strerror(errno));
+       }
+
+       if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
+               zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
+                          inet_ntoa(igmp->ifaddr), igmp->fd,
+                          igmp->interface->name);
+       }
 }
 
 void igmp_startup_mode_on(struct igmp_sock *igmp)
 {
-  struct pim_interface *pim_ifp;
+       struct pim_interface *pim_ifp;
 
-  pim_ifp = igmp->interface->info;
+       pim_ifp = igmp->interface->info;
 
-  /*
-    RFC 3376: 8.7. Startup Query Count
+       /*
+         RFC 3376: 8.7. Startup Query Count
 
-    The Startup Query Count is the number of Queries sent out on
-    startup, separated by the Startup Query Interval.  Default: the
-    Robustness Variable.
-  */
-  igmp->startup_query_count = igmp->querier_robustness_variable;
+         The Startup Query Count is the number of Queries sent out on
+         startup, separated by the Startup Query Interval.  Default: the
+         Robustness Variable.
+       */
+       igmp->startup_query_count = igmp->querier_robustness_variable;
 
-  /*
-    Since we're (re)starting, reset QQI to default Query Interval
-  */
-  igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+       /*
+         Since we're (re)starting, reset QQI to default Query Interval
+       */
+       igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
 }
 
 static void igmp_group_free(struct igmp_group *group)
 {
-  list_free(group->group_source_list);
+       list_delete_and_null(&group->group_source_list);
 
-  XFREE(MTYPE_PIM_IGMP_GROUP, group);
+       XFREE(MTYPE_PIM_IGMP_GROUP, group);
 }
 
 static void igmp_group_delete(struct igmp_group *group)
 {
-  struct listnode *src_node;
-  struct listnode *src_nextnode;
-  struct igmp_source *src;
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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,
-              group->group_igmp_sock->fd,
-              group->group_igmp_sock->interface->name);
-  }
-
-  for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
-    igmp_source_delete(src);
-  }
-
-  if (group->t_group_query_retransmit_timer) {
-    THREAD_OFF(group->t_group_query_retransmit_timer);
-  }
-
-  group_timer_off(group);
-  listnode_delete(group->group_igmp_sock->igmp_group_list, group);
-  hash_release (group->group_igmp_sock->igmp_group_hash, group);
-
-  igmp_group_free(group);
+       struct listnode *src_node;
+       struct listnode *src_nextnode;
+       struct igmp_source *src;
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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, group->group_igmp_sock->fd,
+                          group->group_igmp_sock->interface->name);
+       }
+
+       for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
+                              src)) {
+               igmp_source_delete(src);
+       }
+
+       if (group->t_group_query_retransmit_timer) {
+               THREAD_OFF(group->t_group_query_retransmit_timer);
+       }
+
+       group_timer_off(group);
+       listnode_delete(group->group_igmp_sock->igmp_group_list, group);
+       hash_release(group->group_igmp_sock->igmp_group_hash, group);
+
+       igmp_group_free(group);
 }
 
 void igmp_group_delete_empty_include(struct igmp_group *group)
 {
-  zassert(!group->group_filtermode_isexcl);
-  zassert(!listcount(group->group_source_list));
+       zassert(!group->group_filtermode_isexcl);
+       zassert(!listcount(group->group_source_list));
 
-  igmp_group_delete(group);
+       igmp_group_delete(group);
 }
 
 void igmp_sock_free(struct igmp_sock *igmp)
 {
-  zassert(!igmp->t_igmp_read);
-  zassert(!igmp->t_igmp_query_timer);
-  zassert(!igmp->t_other_querier_timer);
-  zassert(igmp->igmp_group_list);
-  zassert(!listcount(igmp->igmp_group_list));
+       zassert(!igmp->t_igmp_read);
+       zassert(!igmp->t_igmp_query_timer);
+       zassert(!igmp->t_other_querier_timer);
+       zassert(igmp->igmp_group_list);
+       zassert(!listcount(igmp->igmp_group_list));
 
-  list_free(igmp->igmp_group_list);
-  hash_free(igmp->igmp_group_hash);
+       list_delete_and_null(&igmp->igmp_group_list);
+       hash_free(igmp->igmp_group_hash);
 
-  XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
+       XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
 }
 
 void igmp_sock_delete(struct igmp_sock *igmp)
 {
-  struct pim_interface *pim_ifp;
-  struct listnode      *grp_node;
-  struct listnode      *grp_nextnode;
-  struct igmp_group    *grp;
-
-  for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
-    igmp_group_delete(grp);
-  }
+       struct pim_interface *pim_ifp;
+       struct listnode *grp_node;
+       struct listnode *grp_nextnode;
+       struct igmp_group *grp;
+
+       for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode,
+                              grp)) {
+               igmp_group_delete(grp);
+       }
 
-  sock_close(igmp);
+       sock_close(igmp);
 
-  pim_ifp = igmp->interface->info;
+       pim_ifp = igmp->interface->info;
 
-  listnode_delete(pim_ifp->igmp_socket_list, igmp);
+       listnode_delete(pim_ifp->igmp_socket_list, igmp);
 
-  igmp_sock_free(igmp);
+       igmp_sock_free(igmp);
 }
 
-void
-igmp_sock_delete_all (struct interface *ifp)
+void igmp_sock_delete_all(struct interface *ifp)
 {
-  struct pim_interface *pim_ifp;
-  struct listnode *igmp_node, *igmp_nextnode;
-  struct igmp_sock *igmp;
+       struct pim_interface *pim_ifp;
+       struct listnode *igmp_node, *igmp_nextnode;
+       struct igmp_sock *igmp;
 
-  pim_ifp = ifp->info;
+       pim_ifp = ifp->info;
 
-  for (ALL_LIST_ELEMENTS (pim_ifp->igmp_socket_list, igmp_node,
-                         igmp_nextnode, igmp))
-    {
-      igmp_sock_delete(igmp);
-    }
+       for (ALL_LIST_ELEMENTS(pim_ifp->igmp_socket_list, igmp_node,
+                              igmp_nextnode, igmp)) {
+               igmp_sock_delete(igmp);
+       }
 }
 
-static unsigned int
-igmp_group_hash_key (void *arg)
+static unsigned int igmp_group_hash_key(void *arg)
 {
-  struct igmp_group *group = (struct igmp_group *)arg;
+       struct igmp_group *group = (struct igmp_group *)arg;
 
-  return jhash_1word(group->group_addr.s_addr, 0);
+       return jhash_1word(group->group_addr.s_addr, 0);
 }
 
-static int
-igmp_group_hash_equal (const void *arg1, const void *arg2)
+static int igmp_group_hash_equal(const void *arg1, const void *arg2)
 {
-  const struct igmp_group *g1 = (const struct igmp_group *)arg1;
-  const struct igmp_group *g2 = (const struct igmp_group *)arg2;
+       const struct igmp_group *g1 = (const struct igmp_group *)arg1;
+       const struct igmp_group *g2 = (const struct igmp_group *)arg2;
 
-  if (g1->group_addr.s_addr == g2->group_addr.s_addr)
-    return 1;
+       if (g1->group_addr.s_addr == g2->group_addr.s_addr)
+               return 1;
 
-  return 0;
+       return 0;
 }
 
-static struct igmp_sock *igmp_sock_new(int fd,
-                                      struct in_addr ifaddr,
-                                      struct interface *ifp)
+static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
+                                      struct interface *ifp, int mtrace_only)
 {
-  struct pim_interface *pim_ifp;
-  struct igmp_sock *igmp;
-
-  pim_ifp = ifp->info;
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
-              fd, inet_ntoa(ifaddr), ifp->name);
-  }
-
-  igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
-  if (!igmp) {
-    zlog_warn("%s %s: XCALLOC() failure",
-              __FILE__, __PRETTY_FUNCTION__);
-    return 0;
-  }
-
-  igmp->igmp_group_list = list_new();
-  if (!igmp->igmp_group_list) {
-    zlog_err("%s %s: failure: igmp_group_list = list_new()",
-            __FILE__, __PRETTY_FUNCTION__);
-    return 0;
-  }
-  igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
-
-  igmp->igmp_group_hash = hash_create (igmp_group_hash_key,
-                                       igmp_group_hash_equal);
-
-  igmp->fd                          = fd;
-  igmp->interface                   = ifp;
-  igmp->ifaddr                      = ifaddr;
-  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();
-
-  /*
-    igmp_startup_mode_on() will reset QQI:
-
-    igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
-  */
-  igmp_startup_mode_on(igmp);
-  pim_igmp_general_query_on(igmp);
-
-  return igmp;
+       struct pim_interface *pim_ifp;
+       struct igmp_sock *igmp;
+       char hash_name[64];
+
+       pim_ifp = ifp->info;
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               zlog_debug(
+                       "Creating IGMP socket fd=%d for address %s on interface %s",
+                       fd, inet_ntoa(ifaddr), ifp->name);
+       }
+
+       igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
+       if (!igmp) {
+               zlog_warn("%s %s: XCALLOC() failure", __FILE__,
+                         __PRETTY_FUNCTION__);
+               return 0;
+       }
+
+       igmp->igmp_group_list = list_new();
+       if (!igmp->igmp_group_list) {
+               zlog_err("%s %s: failure: igmp_group_list = list_new()",
+                        __FILE__, __PRETTY_FUNCTION__);
+               return 0;
+       }
+       igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free;
+
+       snprintf(hash_name, 64, "IGMP %s hash", ifp->name);
+       igmp->igmp_group_hash = hash_create(igmp_group_hash_key,
+                                           igmp_group_hash_equal, hash_name);
+
+       igmp->fd = fd;
+       igmp->interface = ifp;
+       igmp->ifaddr = ifaddr;
+       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();
+
+       if (mtrace_only) {
+               igmp->mtrace_only = mtrace_only;
+               return igmp;
+       }
+
+       igmp->mtrace_only = false;
+
+       /*
+         igmp_startup_mode_on() will reset QQI:
+
+         igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+       */
+       igmp_startup_mode_on(igmp);
+       pim_igmp_general_query_on(igmp);
+
+       return igmp;
 }
 
-static void igmp_read_on (struct igmp_sock *igmp);
+static void igmp_read_on(struct igmp_sock *igmp);
 
-static int
-pim_igmp_read (struct thread *t)
+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)
-           break;
-
-         goto done;
+       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)
+                               break;
+
+                       goto done;
+               }
        }
-    }
 
- done:
-  igmp_read_on(igmp);
-  return 0;
+done:
+       igmp_read_on(igmp);
+       return 0;
 }
 
-static void
-igmp_read_on (struct igmp_sock *igmp)
+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_add_read(master, pim_igmp_read, igmp, igmp->fd, &igmp->t_igmp_read);
-
+       if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
+               zlog_debug("Scheduling READ event on IGMP socket fd=%d",
+                          igmp->fd);
+       }
+       igmp->t_igmp_read = NULL;
+       thread_add_read(master, pim_igmp_read, igmp, igmp->fd,
+                       &igmp->t_igmp_read);
 }
 
 struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
                                    struct in_addr ifaddr,
-                                   struct interface *ifp)
+                                   struct interface *ifp,
+                                   bool mtrace_only)
 {
-  struct pim_interface *pim_ifp;
-  struct igmp_sock *igmp;
-  int fd;
+       struct pim_interface *pim_ifp;
+       struct igmp_sock *igmp;
+       int fd;
 
-  pim_ifp = ifp->info;
+       pim_ifp = ifp->info;
 
-  fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
-  if (fd < 0) {
-    zlog_warn("Could not open IGMP socket for %s on %s",
-             inet_ntoa(ifaddr), ifp->name);
-    return 0;
-  }
+       fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
+       if (fd < 0) {
+               zlog_warn("Could not open IGMP socket for %s on %s",
+                         inet_ntoa(ifaddr), ifp->name);
+               return 0;
+       }
 
-  igmp = igmp_sock_new(fd, ifaddr, ifp);
-  if (!igmp) {
-    zlog_err("%s %s: igmp_sock_new() failure",
-            __FILE__, __PRETTY_FUNCTION__);
-    close(fd);
-    return 0;
-  }
+       igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
+       if (!igmp) {
+               zlog_err("%s %s: igmp_sock_new() failure", __FILE__,
+                        __PRETTY_FUNCTION__);
+               close(fd);
+               return 0;
+       }
 
-  igmp_read_on (igmp);
+       igmp_read_on(igmp);
 
-  listnode_add(igmp_sock_list, igmp);
+       listnode_add(igmp_sock_list, igmp);
 
 #ifdef IGMP_SOCK_DUMP
-  igmp_sock_dump(igmp_sock_array);
+       igmp_sock_dump(igmp_sock_array);
 #endif
 
-  return igmp;
+       return igmp;
 }
 
 /*
@@ -946,210 +982,203 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
  */
 static int igmp_group_timer(struct thread *t)
 {
-  struct igmp_group *group;
+       struct igmp_group *group;
 
-  group = THREAD_ARG(t);
+       group = THREAD_ARG(t);
 
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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__,
-              group_str, group->group_igmp_sock->interface->name);
-  }
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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__, group_str,
+                          group->group_igmp_sock->interface->name);
+       }
 
-  zassert(group->group_filtermode_isexcl);
+       zassert(group->group_filtermode_isexcl);
 
-  group->group_filtermode_isexcl = 0;
+       group->group_filtermode_isexcl = 0;
 
-  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
-  igmp_anysource_forward_stop(group);
+       /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+       igmp_anysource_forward_stop(group);
 
-  igmp_source_delete_expired(group->group_source_list);
+       igmp_source_delete_expired(group->group_source_list);
 
-  zassert(!group->group_filtermode_isexcl);
+       zassert(!group->group_filtermode_isexcl);
 
-  /*
-    RFC 3376: 6.2.2. Definition of Group Timers
+       /*
+         RFC 3376: 6.2.2. Definition of Group Timers
 
-    If there are no more source records for the group, delete group
-    record.
-  */
-  if (listcount(group->group_source_list) < 1) {
-    igmp_group_delete_empty_include(group);
-  }
+         If there are no more source records for the group, delete group
+         record.
+       */
+       if (listcount(group->group_source_list) < 1) {
+               igmp_group_delete_empty_include(group);
+       }
 
-  return 0;
+       return 0;
 }
 
 static void group_timer_off(struct igmp_group *group)
 {
-  if (!group->t_group_timer)
-    return;
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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);
-  }
-  THREAD_OFF(group->t_group_timer);
+       if (!group->t_group_timer)
+               return;
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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);
+       }
+       THREAD_OFF(group->t_group_timer);
 }
 
-void igmp_group_timer_on(struct igmp_group *group,
-                        long interval_msec, const char *ifname)
+void igmp_group_timer_on(struct igmp_group *group, long interval_msec,
+                        const char *ifname)
 {
-  group_timer_off(group);
-
-  if (PIM_DEBUG_IGMP_EVENTS) {
-    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,
-              interval_msec % 1000,
-              group_str, ifname);
-  }
-
-  /*
-    RFC 3376: 6.2.2. Definition of Group Timers
-
-    The group timer is only used when a group is in EXCLUDE mode and
-    it represents the time for the *filter-mode* of the group to
-    expire and switch to INCLUDE mode.
-  */
-  zassert(group->group_filtermode_isexcl);
-
-  thread_add_timer_msec(master, igmp_group_timer, group, interval_msec,
-                        &group->t_group_timer);
+       group_timer_off(group);
+
+       if (PIM_DEBUG_IGMP_EVENTS) {
+               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, interval_msec % 1000, group_str,
+                       ifname);
+       }
+
+       /*
+         RFC 3376: 6.2.2. Definition of Group Timers
+
+         The group timer is only used when a group is in EXCLUDE mode and
+         it represents the time for the *filter-mode* of the group to
+         expire and switch to INCLUDE mode.
+       */
+       zassert(group->group_filtermode_isexcl);
+
+       thread_add_timer_msec(master, igmp_group_timer, group, interval_msec,
+                             &group->t_group_timer);
 }
 
-struct igmp_group *
-find_group_by_addr (struct igmp_sock *igmp,
-                  struct in_addr group_addr)
+struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
+                                     struct in_addr group_addr)
 {
-  struct igmp_group lookup;
+       struct igmp_group lookup;
 
-  lookup.group_addr.s_addr = group_addr.s_addr;
+       lookup.group_addr.s_addr = group_addr.s_addr;
 
-  return hash_lookup(igmp->igmp_group_hash, &lookup);
+       return hash_lookup(igmp->igmp_group_hash, &lookup);
 }
 
 struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
                                          struct in_addr group_addr)
 {
-  struct igmp_group *group;
-
-  group = find_group_by_addr(igmp, group_addr);
-  if (group) {
-    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}:
-
-    RFC 3376 - 5.1. Action on Change of Interface State
-
-    If no interface state existed for that multicast address before
-    the change (i.e., the change consisted of creating a new
-    per-interface record), or if no state exists after the change
-    (i.e., the change consisted of deleting a per-interface record),
-    then the "non-existent" state is considered to have a filter mode
-    of INCLUDE and an empty source list.
-  */
-
-  group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
-  if (!group) {
-    zlog_warn("%s %s: XCALLOC() failure",
-             __FILE__, __PRETTY_FUNCTION__);
-    return NULL; /* error, not found, could not create */
-  }
-
-  group->group_source_list = list_new();
-  if (!group->group_source_list) {
-    zlog_warn("%s %s: list_new() failure",
-             __FILE__, __PRETTY_FUNCTION__);
-    XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
-    return NULL; /* error, not found, could not initialize */
-  }
-  group->group_source_list->del = (void (*)(void *)) igmp_source_free;
-
-  group->t_group_timer                         = NULL;
-  group->t_group_query_retransmit_timer        = NULL;
-  group->group_specific_query_retransmit_count = 0;
-  group->group_addr                            = group_addr;
-  group->group_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 */
-
-  listnode_add(igmp->igmp_group_list, group);
-  group = hash_get (igmp->igmp_group_hash, group, hash_alloc_intern);
-
-  if (PIM_DEBUG_IGMP_TRACE) {
-    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);
-  }
-
-  /*
-    RFC 3376: 6.2.2. Definition of Group Timers
-
-    The group timer is only used when a group is in EXCLUDE mode and
-    it represents the time for the *filter-mode* of the group to
-    expire and switch to INCLUDE mode.
-  */
-  zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
-  zassert(!group->t_group_timer); /* group timer == 0 */
-
-  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
-  igmp_anysource_forward_stop(group);
-
-  return group;
+       struct igmp_group *group;
+
+       group = find_group_by_addr(igmp, group_addr);
+       if (group) {
+               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}:
+
+         RFC 3376 - 5.1. Action on Change of Interface State
+
+         If no interface state existed for that multicast address before
+         the change (i.e., the change consisted of creating a new
+         per-interface record), or if no state exists after the change
+         (i.e., the change consisted of deleting a per-interface record),
+         then the "non-existent" state is considered to have a filter mode
+         of INCLUDE and an empty source list.
+       */
+
+       group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
+       if (!group) {
+               zlog_warn("%s %s: XCALLOC() failure", __FILE__,
+                         __PRETTY_FUNCTION__);
+               return NULL; /* error, not found, could not create */
+       }
+
+       group->group_source_list = list_new();
+       if (!group->group_source_list) {
+               zlog_warn("%s %s: list_new() failure", __FILE__,
+                         __PRETTY_FUNCTION__);
+               XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
+               return NULL; /* error, not found, could not initialize */
+       }
+       group->group_source_list->del = (void (*)(void *))igmp_source_free;
+
+       group->t_group_timer = NULL;
+       group->t_group_query_retransmit_timer = NULL;
+       group->group_specific_query_retransmit_count = 0;
+       group->group_addr = group_addr;
+       group->group_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 */
+
+       listnode_add(igmp->igmp_group_list, group);
+       group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern);
+
+       if (PIM_DEBUG_IGMP_TRACE) {
+               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);
+       }
+
+       /*
+         RFC 3376: 6.2.2. Definition of Group Timers
+
+         The group timer is only used when a group is in EXCLUDE mode and
+         it represents the time for the *filter-mode* of the group to
+         expire and switch to INCLUDE mode.
+       */
+       zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
+       zassert(!group->t_group_timer);           /* group timer == 0 */
+
+       /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+       igmp_anysource_forward_stop(group);
+
+       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)
+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);
-  }
+       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);
+       }
 }