IGMP improvements
[vpp.git] / src / plugins / igmp / igmp.c
index 71c91b0..5cb8f02 100644 (file)
 #include <vnet/mfib/mfib_table.h>
 
 #include <igmp/igmp.h>
+#include <igmp/igmp_format.h>
+#include <igmp/igmp_pkt.h>
 
 #include <limits.h>
 #include <float.h>
 
 igmp_main_t igmp_main;
 
-void
-igmp_clear_group (igmp_config_t * config, igmp_group_t * group)
-{
-  igmp_src_t *src;
-
-  ASSERT (config);
-  ASSERT (group);
-
-  IGMP_DBG ("group_type %u, sw_if_index %d", group->type,
-           config->sw_if_index);
-
-  /* *INDENT-OFF* */
-  pool_foreach (src, group->srcs, (
-    {
-      clib_mem_free (src->key);
-    }));
-  /* *INDENT-ON* */
-  pool_free (group->srcs);
-  hash_free (group->igmp_src_by_key);
-
-  hash_unset_mem (config->igmp_group_by_key, group->key);
-  clib_mem_free (group->key);
-  pool_put (config->groups, group);
-}
-
-void
-igmp_clear_config (igmp_config_t * config)
-{
-  igmp_main_t *im = &igmp_main;
-  igmp_group_t *group;
-
-  ASSERT (config);
-  /* *INDENT-OFF* */
-  pool_foreach (group, config->groups, (
-    {
-      igmp_clear_group (config, group);
-    }));
-  /* *INDENT-ON* */
-  pool_free (config->groups);
-  hash_free (config->igmp_group_by_key);
-
-  hash_unset (im->igmp_config_by_sw_if_index, config->sw_if_index);
-  pool_put (im->configs, config);
-}
-
-/** \brief igmp timer compare
-    @param _a - igmp timer
-    @param _b - igmp timer
-
-    Compare function for igmp_timer_t sorting.
-*/
-int
-igmp_timer_compare (const void *_a, const void *_b)
-{
-  const igmp_timer_t *a = _a;
-  const igmp_timer_t *b = _b;
-  f64 dt = b->exp_time - a->exp_time;
-  return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
-}
-
-void
-igmp_sort_timers (igmp_timer_t * timers)
-{
-  vlib_main_t *vm = vlib_get_main ();
-
-  qsort (timers, vec_len (timers), sizeof (igmp_timer_t), igmp_timer_compare);
-
-  vlib_process_signal_event (vm, igmp_timer_process_node.index,
-                            IGMP_PROCESS_EVENT_UPDATE_TIMER, 0);
-}
-
-void
-igmp_create_int_timer (f64 time, u32 sw_if_index,
-                      igmp_timer_function_t * func)
-{
-  igmp_main_t *im = &igmp_main;
-  igmp_timer_t *timer;
-
-  pool_get (im->timers, timer);
-  memset (timer, 0, sizeof (igmp_timer_t));
-  timer->func = func;
-  timer->exp_time = time;
-  timer->sw_if_index = sw_if_index;
-
-  igmp_sort_timers (im->timers);
-}
-
-void
-igmp_create_group_timer (f64 time, u32 sw_if_index, igmp_key_t * gkey,
-                        igmp_timer_function_t * func)
-{
-  igmp_main_t *im = &igmp_main;
-  igmp_timer_t *timer;
-
-  pool_get (im->timers, timer);
-  memset (timer, 0, sizeof (igmp_timer_t));
-  timer->func = func;
-  timer->exp_time = time;
-  timer->sw_if_index = sw_if_index;
-
-
-  ASSERT (gkey);
-  /* duplicate keys, to prevent segmentation fault if (S,G) is removed */
-  timer->data = clib_mem_alloc (sizeof (igmp_key_t));
-  clib_memcpy (&((igmp_key_t *) timer->data)[0], gkey, sizeof (igmp_key_t));
-
-  igmp_sort_timers (im->timers);
-}
-
-void
-igmp_create_src_timer (f64 time, u32 sw_if_index, igmp_key_t * gkey,
-                      igmp_key_t * skey, igmp_timer_function_t * func)
-{
-  igmp_main_t *im = &igmp_main;
-  igmp_timer_t *timer;
-
-  pool_get (im->timers, timer);
-  memset (timer, 0, sizeof (igmp_timer_t));
-  timer->func = func;
-  timer->exp_time = time;
-  timer->sw_if_index = sw_if_index;
-
-  ASSERT (gkey);
-  ASSERT (skey);
-  /* duplicate keys, to prevent segmentation fault if (S,G) is removed */
-  timer->data = clib_mem_alloc (sizeof (igmp_key_t) * 2);
-  clib_memcpy (&((igmp_key_t *) timer->data)[0], gkey, sizeof (igmp_key_t));
-  clib_memcpy (&((igmp_key_t *) timer->data)[1], skey, sizeof (igmp_key_t));
-
-  igmp_sort_timers (im->timers);
-}
-
-/** \brief igmp get next timer
-    @param im - igmp main
-
-    Get next timer.
-*/
-always_inline igmp_timer_t *
-igmp_get_next_timer (igmp_main_t * im)
-{
-  if (pool_elts (im->timers) > 0)
-    return vec_elt_at_index (im->timers, pool_elts (im->timers) - 1);
-  return NULL;
-}
-
-/*
-static void
-igmp_create_report_v2 (vlib_buffer_t * b, igmp_config_t * config)
-{
-  ip_csum_t sum;
-  u16 csum;
-  igmp_main_t *im = &igmp_main;
-  igmp_sg_t *sg;
-
-  sg = vec_elt_at_index (config->sg, im->next_index.sg_index);
-
-  igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b));
-  memset (igmp, 0, sizeof (igmp_message_t));
-
-  clib_memcpy (&igmp->dst, &sg->gaddr.ip4, sizeof (ip4_address_t));
-  igmp->header.type =
-    (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources) ?
-    IGMP_TYPE_leave_group_v2 : IGMP_TYPE_membership_report_v2;
-  sum = ip_incremental_checksum (0, igmp, sizeof (igmp_message_t));
-  csum = ~ip_csum_fold (sum);
-  igmp->header.checksum = csum;
+/* *INDENT-OFF* */
+/* General Query address */
+const static mfib_prefix_t mpfx_general_query = {
+  .fp_proto = FIB_PROTOCOL_IP4,
+  .fp_len = 32,
+  .fp_grp_addr = {
+    .ip4 = {
+      .as_u32 = IGMP_GENERAL_QUERY_ADDRESS,
+    },
+  },
+};
 
-  b->current_data += sizeof (igmp_message_t);
-  b->current_length += sizeof (igmp_message_t);
-}
-*/
+/* Report address */
+const static mfib_prefix_t mpfx_report = {
+  .fp_proto = FIB_PROTOCOL_IP4,
+  .fp_len = 32,
+  .fp_grp_addr = {
+    .ip4 = {
+      .as_u32 = IGMP_MEMBERSHIP_REPORT_ADDRESS,
+    },
+  },
+};
+/* *INDENT-ON* */
 
-/* TODO: divide (S,G)s to multiple reports...
- * - create report limited by <packet size|number of (S,G)s>?
- * - save loop state
- * - on next timer continue loop
- * - case of new query -> reset loop
+/**
+ * @brief igmp send query (igmp_timer_function_t)
+ *
+ *   Send an igmp query.
+ *   If the timer holds group key, send Group-Specific query,
+ *   else send General query.
  */
-
-/** \brief igmp create report all (v3)
-    @param b - vlib buffer
-    @param config - igmp configuration
-    @param group - igmp group
-
-    Create IGMPv3 report. If group is NULL, send all groups on interface.
-*/
-static void
-igmp_create_report_v3 (vlib_buffer_t * b, igmp_config_t * config,
-                      igmp_group_t * group)
-{
-  ip_csum_t sum;
-  u16 csum;
-  u32 len = 0;
-  int i;
-
-  igmp_src_t *src;
-
-  igmp_membership_group_v3_t *igmp_group;
-
-  len = sizeof (igmp_membership_report_v3_t);
-
-  igmp_membership_report_v3_t *igmp =
-    (igmp_membership_report_v3_t *) (vlib_buffer_get_current (b));
-  memset (igmp, 0, sizeof (igmp_membership_report_v3_t));
-
-  igmp->header.type = IGMP_TYPE_membership_report_v3;
-  igmp->n_groups =
-    clib_net_to_host_u16 ((group) ? 1 : pool_elts (config->groups));
-
-  /* get pointer to first group */
-  igmp_group = igmp->groups;
-
-  /* if group is not NULL, send the specified group */
-  if (group)
-    {
-      memset (igmp_group, 0, sizeof (igmp_membership_group_v3_t));
-      igmp_group->type = group->type;
-      igmp_group->n_src_addresses =
-       clib_host_to_net_u16 (pool_elts (group->srcs));
-      igmp_group->dst_address = group->addr.ip4;
-      i = 0;
-      /* *INDENT-OFF* */
-      pool_foreach (src, group->srcs, (
-       {
-         igmp_group->src_addresses[i++] = src->addr.ip4;
-       }));
-      /* *INDENT-ON* */
-      len += sizeof (ip4_address_t) * i;
-      len += sizeof (igmp_membership_group_v3_t);
-    }
-  else
-    {
-      /* *INDENT-OFF* */
-      pool_foreach (group, config->groups, (
-       {
-         memset (igmp_group, 0, sizeof (igmp_membership_group_v3_t));
-         igmp_group->type = group->type;
-         igmp_group->n_src_addresses =
-           clib_host_to_net_u16 (pool_elts (group->srcs));
-         igmp_group->dst_address = group->addr.ip4;
-         i = 0;
-         pool_foreach (src, group->srcs, (
-           {
-             igmp_group->src_addresses[i++] = src->addr.ip4;
-           }));
-         len += sizeof (ip4_address_t) * i;
-         len += sizeof (igmp_membership_group_v3_t);
-         igmp_group = group_ptr (igmp, len);
-       }));
-      /* *INDENT-ON* */
-    }
-
-  sum = ip_incremental_checksum (0, igmp, len);
-  csum = ~ip_csum_fold (sum);
-  igmp->header.checksum = csum;
-
-  b->current_data += len;
-  b->current_length += len;
-}
-
-/** \brief igmp create query (v3)
-    @param b - vlib buffer
-    @param config - configuration that sends the query
-    @param group - if not NULL, create Group-specific query
-
-    Create igmp v3 qeury inside vlib buffer b.
-    If group == NULL create general query,
-    else, create group specific query.
-*/
 static void
-igmp_create_query_v3 (vlib_buffer_t * b, igmp_config_t * config,
-                     igmp_group_t * group)
+igmp_send_general_query (u32 obj, void *dat)
 {
-  vlib_main_t *vm = vlib_get_main ();
-  ip_csum_t sum;
-  u16 csum;
-
-  igmp_membership_query_v3_t *igmp =
-    (igmp_membership_query_v3_t *) (vlib_buffer_get_current (b));
-  memset (igmp, 0, sizeof (igmp_membership_query_v3_t));
+  igmp_pkt_build_query_t bq;
+  igmp_config_t *config;
 
-  igmp->header.type = IGMP_TYPE_membership_query;
-  igmp->header.code = 100;
+  config = igmp_config_get (obj);
 
-  config->flags &= ~IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
-  igmp_create_int_timer (vlib_time_now (vm) + (f64) (igmp->header.code / 10),
-                        config->sw_if_index, igmp_query_resp_exp);
+  IGMP_DBG ("send-general-query: %U",
+           format_vnet_sw_if_index_name, vnet_get_main (),
+           config->sw_if_index);
 
-  if (PREDICT_FALSE (group != NULL))
-    clib_memcpy (&igmp->dst, &group->addr.ip4, sizeof (ip4_address_t));
+  igmp_timer_retire (&config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY]);
 
-  sum =
-    ip_incremental_checksum (0, igmp, sizeof (igmp_membership_query_v3_t));
-  csum = ~ip_csum_fold (sum);
-  igmp->header.checksum = csum;
+  igmp_pkt_build_query_init (&bq, config->sw_if_index);
+  igmp_pkt_query_v3_add_group (&bq, NULL, NULL);
+  igmp_pkt_query_v3_send (&bq);
 
-  b->current_data += sizeof (igmp_membership_query_v3_t);
-  b->current_length += sizeof (igmp_membership_query_v3_t);
+  /*
+   * re-schedule
+   */
+  config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY] =
+    igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_QUERY),
+                        igmp_config_index (config),
+                        igmp_send_general_query, NULL);
 }
 
-/** \brief igmp create ip4
-    @param b - vlib buffer
-    @param config - igmp configuration
-    @param group - igmp membership group
-    @param is_report - if zero create query, else create report
-
-    Create ip4 header in vlib buffer b.
-*/
 static void
-igmp_create_ip4 (vlib_buffer_t * b, igmp_config_t * config,
-                igmp_group_t * group, u8 is_report)
+igmp_send_state_change_group_report_v3 (u32 sw_if_index,
+                                       const igmp_group_t * group)
 {
-  ip_lookup_main_t *lm = &ip4_main.lookup_main;
-
-  ip4_header_t *ip4 = (ip4_header_t *) (vlib_buffer_get_current (b));
-  memset (ip4, 0, sizeof (ip4_header_t));
-  ip4->ip_version_and_header_length = 0x45;
-  ip4->ttl = 1;
-  ip4->protocol = 2;
-  ip4->tos = 0xc0;
-
-  u32 if_add_index =
-    lm->if_address_pool_index_by_sw_if_index[config->sw_if_index];
-  if (PREDICT_TRUE (if_add_index != ~0))
-    {
-      ip_interface_address_t *if_add =
-       pool_elt_at_index (lm->if_address_pool, if_add_index);
-      ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add);
-      clib_memcpy (&ip4->src_address, if_ip, sizeof (ip4_address_t));
-    }
-
-  if (is_report)
-    ip4->dst_address.as_u32 =
-      clib_host_to_net_u32 (IGMP_MEMBERSHIP_REPORT_ADDRESS);
-  else
-    {
-      if ((group != NULL))
-       clib_memcpy (&ip4->dst_address, &group->addr.ip4,
-                    sizeof (ip4_address_t));
-      else
-       ip4->dst_address.as_u32 =
-         clib_host_to_net_u32 (IGMP_GENERAL_QUERY_ADDRESS);
-    }
-
-  b->current_data += ip4_header_bytes (ip4);
-  b->current_length += ip4_header_bytes (ip4);
+  igmp_pkt_build_report_t br;
 
-  config->next_create_msg (b, config, group);
-  ip4->length = clib_host_to_net_u16 (b->current_length);
+  IGMP_DBG ("state-change-group: %U", format_igmp_key, group->key);
 
-  ip4->checksum = ip4_header_checksum (ip4);
+  igmp_pkt_build_report_init (&br, sw_if_index);
+  igmp_pkt_report_v3_add_group (&br,
+                               group,
+                               IGMP_MEMBERSHIP_GROUP_allow_new_sources);
+  igmp_pkt_report_v3_send (&br);
 }
 
-
-/** \brief igmp send message
-    @param vm - vlib main
-    @param node - vlib runtime node
-    @param im - igmp main
-    @param config - igmp configuration
-    @param group - igmp mebership group
-    @param is_report - 0 == qeury, else report
-
-    Send an igmp message. Get free vlib buffer fill it with igmp packet and transmit.
-*/
 static void
-igmp_send_msg (vlib_main_t * vm, vlib_node_runtime_t * node,
-              igmp_main_t * im, igmp_config_t * config, igmp_group_t * group,
-              u8 is_report)
-{
-  u32 *to_next = 0;
-  u32 next_index = ip4_rewrite_node.index;
-
-  u32 bi = 0;
-  vlib_buffer_alloc (vm, &bi, 1);
-
-  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
-  vlib_buffer_free_list_t *fl = vlib_buffer_get_free_list (vm,
-                                                          VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
-  vlib_buffer_init_for_free_list (b, fl);
-
-  b->current_data = 0;
-  b->current_length = 0;
-
-  igmp_create_ip4 (b, config, group, is_report);
-
-  b->current_data = 0;
-
-  b->total_length_not_including_first_buffer = 0;
-  b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
-  vnet_buffer (b)->sw_if_index[VLIB_RX] = (u32) ~ 0;
-  vnet_buffer (b)->ip.adj_index[VLIB_TX] = config->adj_index;
-
-  b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
-
-
-  vlib_frame_t *f = vlib_get_frame_to_node (vm, next_index);
-  to_next = vlib_frame_vector_args (f);
-  to_next[0] = bi;
-
-  f->n_vectors = 1;
-
-  vlib_buffer_t *c = vlib_buffer_copy (vm, b);
-  to_next += 1;
-  to_next[0] = vlib_get_buffer_index (vm, c);
-
-  vlib_put_frame_to_node (vm, next_index, f);
-}
-
-void
-igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im,
-                igmp_timer_t * timer)
-{
-  igmp_config_t *config;
-/* TODO: group-specific query: pass group key in timer */
-  igmp_group_t *group = NULL;
-
-  u32 sw_if_index = timer->sw_if_index;
-
-  pool_put (im->timers, timer);
-
-  config = igmp_config_lookup (im, sw_if_index);
-  if (!config)
-    return;
-
-  /* TODO: implement IGMPv2 */
-  config->next_create_msg = igmp_create_query_v3;
-  igmp_send_msg (vm, rt, im, config, group, /* is_report */ 0);
-
-  /* in case of group query we don't want to set up another qery timer */
-  if (PREDICT_TRUE (!group))
-    igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER, sw_if_index,
-                          igmp_send_query);
-}
-
-void
-igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
-                    igmp_main_t * im, igmp_timer_t * timer)
-{
-  igmp_config_t *config;
-/* TODO: group-specific query: pass group key in timer */
-  igmp_group_t *group = NULL;
-
-  u32 sw_if_index = timer->sw_if_index;
-
-  pool_put (im->timers, timer);
-
-  config = igmp_config_lookup (im, sw_if_index);
-  if (!config)
-    return;
-
-  /* if group != NULL this is a group-specific qeury timer */
-  if (PREDICT_FALSE (group != NULL))
-    {
-      if ((group->flags & IGMP_GROUP_FLAG_QUERY_RESP_RECVED) == 0)
-       {
-         igmp_clear_group (config, group);
-         return;
-       }
-    }
-  /* if report not received in max resp time clear igmp on interface */
-  if ((config->flags & IGMP_CONFIG_FLAG_QUERY_RESP_RECVED) == 0)
-    {
-      igmp_clear_config (config);
-    }
-}
-
-void
-igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt,
-                 igmp_main_t * im, igmp_timer_t * timer)
+igmp_resend_state_change_group_report_v3 (u32 gi, void *data)
 {
   igmp_config_t *config;
+  igmp_group_t *group;
 
-  u32 sw_if_index = timer->sw_if_index;
-
-  pool_put (im->timers, timer);
+  group = igmp_group_get (gi);
+  config = igmp_config_get (group->config);
 
-  config = igmp_config_lookup (im, sw_if_index);
-  if (!config)
-    return;
+  igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_RESEND_REPORT]);
+  igmp_send_state_change_group_report_v3 (config->sw_if_index, group);
 
-  if (config->flags & IGMP_CONFIG_FLAG_CAN_SEND_REPORT)
+  if (++group->n_reports_sent < config->robustness_var)
     {
-      /* TODO: implement IGMPv2 and IGMPv1 */
-      config->next_create_msg = igmp_create_report_v3;
-      /* pass NULL as group to send all groups at once */
-      igmp_send_msg (vm, rt, im, config, NULL, /* is_report */ 1);
-      /* WIP: unset flag after all reports sent */
-      config->flags &= ~IGMP_CONFIG_FLAG_CAN_SEND_REPORT;
+      group->timers[IGMP_GROUP_TIMER_RESEND_REPORT] =
+       igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_REPORT_INTERVAL),
+                            igmp_group_index (group),
+                            igmp_resend_state_change_group_report_v3, NULL);
     }
 }
 
-void
-igmp_send_state_changed (vlib_main_t * vm, vlib_node_runtime_t * rt,
-                        igmp_main_t * im, igmp_timer_t * timer)
+int
+igmp_listen (vlib_main_t * vm,
+            igmp_filter_mode_t mode,
+            u32 sw_if_index,
+            const ip46_address_t * saddrs, const ip46_address_t * gaddr)
 {
+  const ip46_address_t *saddr;
   igmp_config_t *config;
   igmp_group_t *group;
-  igmp_src_t *src;
-  igmp_key_t gkey;
-
-  u32 sw_if_index = timer->sw_if_index;
-  IGMP_DBG ("sw_if_index %d", sw_if_index);
 
-  ASSERT (timer->data);
-  clib_memcpy (&gkey, timer->data, sizeof (igmp_key_t));
-
-  pool_put (im->timers, timer);
+  /*
+   * RFC 3376 Section 2
+   " For a given combination of socket, interface, and multicast address,
+   only a single filter mode and source list can be in effect at any one
+   time.  However, either the filter mode or the source list, or both,
+   may be changed by subsequent IPMulticastListen requests that specify
+   the same socket, interface, and multicast address.  Each subsequent
+   request completely replaces any earlier request for the given socket,
+   interface and multicast address."
+   */
+  int rv = 0;
+  IGMP_DBG ("listen: (%U, %U) %U %U",
+           format_igmp_src_addr_list, saddrs,
+           format_igmp_key, gaddr,
+           format_vnet_sw_if_index_name, vnet_get_main (),
+           sw_if_index, format_igmp_filter_mode, mode);
+  /*
+   * find configuration, if it dosn't exist, then this interface is
+   * not IGMP enabled
+   */
+  config = igmp_config_lookup (sw_if_index);
 
-  config = igmp_config_lookup (im, sw_if_index);
   if (!config)
-    return;
-
-  group = igmp_group_lookup (config, &gkey);
-  if (!group)
-    return;
-
-  config->next_create_msg = igmp_create_report_v3;
-  igmp_send_msg (vm, rt, im, config, group, /* is_report */ 1);
-
-  IGMP_DBG ("group_type %u", group->type);
-
-  if (group->type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include)
     {
-      igmp_key_t new_gkey;
-      igmp_group_t *new_group;
-      igmp_src_t *new_src;
-
-      clib_memcpy (&new_gkey.data, &group->addr, sizeof (ip46_address_t));
-      new_gkey.group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
-
-      new_group = igmp_group_lookup (config, &new_gkey);
-      if (!new_group)
-       {
-         IGMP_DBG ("creating new group...");
-         pool_get (config->groups, new_group);
-         /* get valid pointer to old group */
-         group = igmp_group_lookup (config, &gkey);
-
-         memset (new_group, 0, sizeof (igmp_group_t));
-
-         clib_memcpy (&new_group->addr, &group->addr,
-                      sizeof (ip46_address_t));
-         new_group->n_srcs = 0;
-         new_group->type = new_gkey.group_type;
-
-         new_group->key = clib_mem_alloc (sizeof (igmp_key_t));
-         clib_memcpy (new_group->key, &new_gkey, sizeof (igmp_key_t));
-         new_group->igmp_src_by_key =
-           hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
-         hash_set_mem (config->igmp_group_by_key, new_group->key,
-                       new_group - config->groups);
-       }
-      /* *INDENT-OFF* */
-      /* loop through old group sources */
-      pool_foreach (src, group->srcs, (
-       {
-         /* add sources to new group */
-         new_src = igmp_src_lookup (new_group, src->key);
-         if (!new_src)
-           {
-             pool_get (new_group->srcs, new_src);
-             memset (new_src, 0, sizeof (igmp_src_t));
-             new_group->n_srcs += 1;
-             new_src->key = clib_mem_alloc (sizeof (igmp_key_t));
-             clib_memcpy (new_src->key, src->key, sizeof (igmp_key_t));
-             clib_memcpy (&new_src->addr, &src->addr,
-               sizeof (ip46_address_t));
-
-             hash_set_mem (new_group->igmp_src_by_key, new_src->key,
-                   new_src - new_group->srcs);
-           }
-       }));
-      /* *INDENT-ON* */
+      rv = VNET_API_ERROR_INVALID_INTERFACE;
+      goto error;
     }
-
-  /* remove group */
-  IGMP_DBG ("remove group");
-  igmp_clear_group (config, group);
-  if (pool_elts (config->groups) == 0)
+  if (config->mode != IGMP_MODE_HOST)
     {
-      hash_unset (im->igmp_config_by_sw_if_index, config->sw_if_index);
-      pool_put (im->configs, config);
+      rv = VNET_API_ERROR_INVALID_INTERFACE;
+      goto error;
     }
-}
-
-void
-igmp_src_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
-             igmp_main_t * im, igmp_timer_t * timer)
-{
-  igmp_config_t *config;
-  igmp_group_t *group;
-  igmp_src_t *src;
 
-  ASSERT (timer->data);
+  /* find igmp group, if it dosn't exist, create new */
+  group = igmp_group_lookup (config, gaddr);
 
-  igmp_key_t *gkey = (igmp_key_t *) & ((igmp_key_t *) timer->data)[0];
-  igmp_key_t *skey = (igmp_key_t *) & ((igmp_key_t *) timer->data)[1];
-
-  config = igmp_config_lookup (im, timer->sw_if_index);
-  if (!config)
-    goto done;
-  group = igmp_group_lookup (config, gkey);
   if (!group)
-    goto done;
-  src = igmp_src_lookup (group, skey);
-  if (!src)
-    goto done;
-  /* check if this timer is valid */
-  if (timer->exp_time != src->exp_time)
-    {
-      timer->exp_time = src->exp_time;
-      igmp_sort_timers (im->timers);
-      return;
-    }
-
-  ip46_address_t saddr;
-  ip46_address_t gaddr;
-  clib_memcpy (&saddr, skey->data, sizeof (ip46_address_t));
-  clib_memcpy (&gaddr, gkey->data, sizeof (ip46_address_t));
-
-  /* source timer expired, remove src */
-  igmp_listen (vm, 0, timer->sw_if_index, saddr, gaddr, 0);
-done:
-  clib_mem_free (timer->data);
-  pool_put (im->timers, timer);
-}
-
-/** \brief igmp timer process
-    @param vm - vlib main
-    @param rt - vlib runtime node
-    @param f - vlib frame
-
-    Handle igmp timers.
-*/
-static uword
-igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
-                   vlib_frame_t * f)
-{
-  igmp_main_t *im = &igmp_main;
-  uword *event_data = 0, event_type;
-  f64 time_start;
-  igmp_timer_t *timer = NULL;
-  while (1)
     {
-      /* suspend util timer expires */
-      if (NULL != timer)
-       vlib_process_wait_for_event_or_clock (vm,
-                                             timer->exp_time - time_start);
-      else
-       vlib_process_wait_for_event (vm);
-      time_start = vlib_time_now (vm);
-      event_type = vlib_process_get_events (vm, &event_data);
-      vec_reset_length (event_data);
-      if (event_type == IGMP_PROCESS_EVENT_UPDATE_TIMER)
-       goto next_timer;
-      IGMP_DBG ("time: %f", vlib_time_now (vm));
-      /* timer expired */
-      if (NULL != timer && timer->func != NULL)
-       timer->func (vm, rt, im, timer);
-    next_timer:
-      timer = igmp_get_next_timer (im);
+      group = igmp_group_alloc (config, gaddr, mode);
+
+      /* new group implies create all sources */
+      vec_foreach (saddr, saddrs)
+      {
+       igmp_group_src_update (group, saddr, IGMP_MODE_HOST);
+      }
+
+      /*
+       * Send state changed event report for the group.
+       *
+       * RFC3376 Section 5.1
+       *  "To cover the possibility of the State-Change Report being missed by
+       *   one or more multicast routers, it is retransmitted [Robustness
+       *   Variable] - 1 more times, at intervals chosen at random from the
+       *   range (0, [Unsolicited Report Interval])."
+       */
+      igmp_send_state_change_group_report_v3 (config->sw_if_index, group);
+
+      igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_RESEND_REPORT]);
+
+      group->n_reports_sent = 1;
+      group->timers[IGMP_GROUP_TIMER_RESEND_REPORT] =
+       igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_REPORT_INTERVAL),
+                            igmp_group_index (group),
+                            igmp_resend_state_change_group_report_v3, NULL);
     }
-  return 0;
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (igmp_timer_process_node) =
-{
-  .function = igmp_timer_process,
-  .type = VLIB_NODE_TYPE_PROCESS,
-  .name = "igmp-timer-process",
-  .n_next_nodes = IGMP_N_NEXT,
-  .next_nodes =  {
-    [IGMP_NEXT_IP4_REWRITE_MCAST_NODE] = "ip4-rewrite-mcast",
-    [IGMP_NEXT_IP6_REWRITE_MCAST_NODE] = "ip6-rewrite-mcast",
-  }
-};
-/* *INDENT-ON* */
-
-int
-igmp_listen (vlib_main_t * vm, u8 enable, u32 sw_if_index,
-            ip46_address_t saddr, ip46_address_t gaddr, u8 flags)
-{
-  igmp_main_t *im = &igmp_main;
-  igmp_config_t *config;
-  igmp_group_t *group;
-  igmp_src_t *src;
-  igmp_key_t skey;
-  igmp_key_t gkey;
-
-  igmp_membership_group_v3_type_t group_type =
-    (flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) ?
-    IGMP_MEMBERSHIP_GROUP_change_to_filter_include :
-    IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
-  int rv = 0;
-
-  /* set the lookup keys */
-  skey.group_type = 0;
-  gkey.group_type = group_type;
-  clib_memcpy (&skey.data, &saddr, sizeof (ip46_address_t));
-  clib_memcpy (&gkey.data, &gaddr, sizeof (ip46_address_t));
-
-  if (enable)
+  else
     {
-      /* find configuration, if it dosn't exist, create new */
-      config = igmp_config_lookup (im, sw_if_index);
-      if (!config)
-       {
-         pool_get (im->configs, config);
-         memset (config, 0, sizeof (igmp_config_t));
-         config->sw_if_index = sw_if_index;
-         config->igmp_group_by_key =
-           hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
-         /* use IGMPv3 by default */
-         config->igmp_ver = IGMP_V3;
-         config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
-         config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED | flags;
-
-         if ((flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) == 0)
-           {
-             /* create qery timer */
-             igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER,
-                                    sw_if_index, igmp_send_query);
-           }
-         config->adj_index =
-           adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
-                                  config->sw_if_index);
-         hash_set (im->igmp_config_by_sw_if_index,
-                   config->sw_if_index, config - im->configs);
-       }
-      else if ((config->flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED & flags)
-              == 0)
+      IGMP_DBG ("... update (%U, %U) %U %U",
+               format_igmp_src_addr_list, saddrs,
+               format_igmp_key, gaddr,
+               format_vnet_sw_if_index_name, vnet_get_main (),
+               sw_if_index, format_igmp_filter_mode, mode);
+
+      /*
+       * RFC 3367 Section 5.1
+       *
+       *   Old State         New State         State-Change Record Sent
+       *   ---------         ---------         ------------------------
+       *
+       * 1) INCLUDE (A)       INCLUDE (B)       ALLOW (B-A), BLOCK (A-B)
+       * 2) EXCLUDE (A)       EXCLUDE (B)       ALLOW (A-B), BLOCK (B-A)
+       * 3) INCLUDE (A)       EXCLUDE (B)       TO_EX (B)
+       * 4) EXCLUDE (A)       INCLUDE (B)       TO_IN (B)
+       *
+       * N.B. We do not split state-change records for pending transfer
+       * hence there is no merge logic required.
+       */
+
+      if (IGMP_FILTER_MODE_INCLUDE == mode)
        {
-         rv = -2;
-         goto error;
-       }
-      /* find igmp group, if it dosn't exist, create new */
-      group = igmp_group_lookup (config, &gkey);
-      if (!group)
-       {
-         pool_get (config->groups, group);
-         memset (group, 0, sizeof (igmp_group_t));
-         group->key = clib_mem_alloc (sizeof (igmp_key_t));
-         clib_memcpy (group->key, &gkey, sizeof (igmp_key_t));
-         clib_memcpy (&group->addr, &gaddr, sizeof (ip46_address_t));
-         group->igmp_src_by_key =
-           hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
-         group->n_srcs = 0;
-         group->type = gkey.group_type;
-         if (flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED)
+         ip46_address_t *added, *removed;
+         igmp_pkt_build_report_t br;
+
+         /*
+          * find the list of sources that have been added and removed from
+          * the include set
+          */
+         removed =
+           igmp_group_present_minus_new (group, IGMP_FILTER_MODE_INCLUDE,
+                                         saddrs);
+         added =
+           igmp_group_new_minus_present (group, IGMP_FILTER_MODE_INCLUDE,
+                                         saddrs);
+
+         if (!(vec_len (added) || vec_len (removed)))
+           /* no change => done */
+           goto error;
+
+         igmp_pkt_build_report_init (&br, config->sw_if_index);
+
+         if (vec_len (added))
            {
-             /* create state-changed report timer with zero timeout */
-             igmp_create_group_timer (0, sw_if_index, group->key,
-                                      igmp_send_state_changed);
+             igmp_pkt_report_v3_add_report (&br,
+                                            group->key,
+                                            added,
+                                            IGMP_MEMBERSHIP_GROUP_allow_new_sources);
            }
 
-         hash_set_mem (config->igmp_group_by_key, group->key,
-                       group - config->groups);
-       }
-      /* find source, if it dosn't exist, create new */
-      src = igmp_src_lookup (group, &skey);
-      if (!src)
-       {
-         pool_get (group->srcs, src);
-         memset (src, 0, sizeof (igmp_src_t));
-         group->n_srcs += 1;
-         src->key = clib_mem_alloc (sizeof (igmp_key_t));
-         clib_memcpy (src->key, &skey, sizeof (igmp_key_t));
-         clib_memcpy (&src->addr, &saddr, sizeof (ip46_address_t));
-         if ((flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) == 0)
+         if (vec_len (removed))
            {
-             /* arm source timer (after expiration remove (S,G)) */
-             igmp_event (im, config, group, src);
-             src->exp_time = vlib_time_now (vm) + IGMP_SRC_TIMER;
-             igmp_create_src_timer (src->exp_time, config->sw_if_index,
-                                    group->key, src->key, igmp_src_exp);
+             igmp_pkt_report_v3_add_report (&br,
+                                            group->key,
+                                            removed,
+                                            IGMP_MEMBERSHIP_GROUP_block_old_sources);
            }
 
-         hash_set_mem (group->igmp_src_by_key, src->key, src - group->srcs);
-       }
-      else
-       {
-         rv = -1;
-         goto error;
-       }
-    }
-  else
-    {
-      config = igmp_config_lookup (im, sw_if_index);
-      if (config)
-       {
-         gkey.group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
-         group = igmp_group_lookup (config, &gkey);
-         if (group)
-           {
-             src = igmp_src_lookup (group, &skey);
-             if (src)
-               {
-                 /* add source to block_all_sources group */
-                 igmp_key_t new_gkey;
-                 igmp_group_t *new_group;
+         IGMP_DBG ("... added %U", format_igmp_src_addr_list, added);
+         IGMP_DBG ("... removed %U", format_igmp_src_addr_list, removed);
 
-                 clib_memcpy (&new_gkey, &gkey, sizeof (igmp_key_t));
-                 new_gkey.group_type =
-                   IGMP_MEMBERSHIP_GROUP_block_old_sources;
-                 new_group = igmp_group_lookup (config, &new_gkey);
-                 if (!new_group)
-                   {
-                     pool_get (config->groups, new_group);
+         igmp_pkt_report_v3_send (&br);
 
-                     group = igmp_group_lookup (config, &gkey);
+         /*
+          * clear the group of the old sources and populate it with the new
+          * set requested
+          */
+         igmp_group_free_all_srcs (group);
 
-                     memset (new_group, 0, sizeof (igmp_group_t));
-                     new_group->key = clib_mem_alloc (sizeof (igmp_key_t));
-                     clib_memcpy (new_group->key, &new_gkey,
-                                  sizeof (igmp_key_t));
-                     clib_memcpy (&new_group->addr, &group->addr,
-                                  sizeof (ip46_address_t));
-                     new_group->igmp_src_by_key =
-                       hash_create_mem (0, sizeof (igmp_key_t),
-                                        sizeof (uword));
-                     new_group->n_srcs = 0;
-                     new_group->type = new_gkey.group_type;
-                     hash_set_mem (config->igmp_group_by_key, new_group->key,
-                                   new_group - config->groups);
-                   }
-                 igmp_src_t *new_src;
-                 new_src = igmp_src_lookup (new_group, &skey);
-                 if (!new_src)
-                   {
-                     pool_get (new_group->srcs, new_src);
-                     memset (new_src, 0, sizeof (igmp_src_t));
-                     new_group->n_srcs += 1;
-                     new_src->key = clib_mem_alloc (sizeof (igmp_key_t));
-                     clib_memcpy (new_src->key, src->key,
-                                  sizeof (igmp_key_t));
-                     clib_memcpy (&new_src->addr, &src->addr,
-                                  sizeof (ip46_address_t));
-                     hash_set_mem (new_group->igmp_src_by_key, new_src->key,
-                                   new_src - new_group->srcs);
-                   }
+         vec_foreach (saddr, saddrs)
+         {
+           igmp_group_src_update (group, saddr, IGMP_MODE_HOST);
+         }
 
-                 /* notify all registered api clients */
-                 if ((flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) == 0)
-                   igmp_event (im, config, new_group, new_src);
-                 else
-                   igmp_create_group_timer (0, sw_if_index, new_group->key,
-                                            igmp_send_state_changed);
-                 /* remove source form mode_is_filter_include group */
-                 hash_unset_mem (group->igmp_src_by_key, src->key);
-                 clib_mem_free (src->key);
-                 pool_put (group->srcs, src);
-                 group->n_srcs -= 1;
-                 if (group->n_srcs <= 0)
-                   igmp_clear_group (config, group);
-                 if (pool_elts (config->groups) <= 0)
-                   igmp_clear_config (config);
-               }
-             else
-               {
-                 rv = -1;
-                 goto error;
-               }
-           }
-         else
-           {
-             rv = -1;
-             goto error;
-           }
+         if (0 == igmp_group_n_srcs (group, mode))
+           igmp_group_clear (group);
+
+         vec_free (added);
+         vec_free (removed);
        }
       else
        {
-         rv = -1;
-         goto error;
+         /*
+          * The control plane is excluding some sources.
+          *  - First; check for those that are present in the include list
+          *  - Second; check add them to the exlude list
+          *
+          * TODO
+          */
        }
     }
 
 error:
-  return rv;
+  return (rv);
 }
 
 /** \brief igmp hardware interface link up down
@@ -921,26 +310,149 @@ error:
 
     If an interface goes down, remove its (S,G)s.
 */
-static clib_error_t *
-igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+static walk_rc_t
+igmp_sw_if_down (vnet_main_t * vnm, u32 sw_if_index, void *ctx)
 {
-  igmp_main_t *im = &igmp_main;
   igmp_config_t *config;
-  clib_error_t *error = NULL;
-  /* remove igmp from a down interface to prevent crashes... */
-  config =
-    igmp_config_lookup (im,
-                       vnet_get_hw_interface (vnm,
-                                              hw_if_index)->sw_if_index);
-  if (config)
+  config = igmp_config_lookup (sw_if_index);
+  IGMP_DBG ("down: %U",
+           format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index);
+  if (NULL != config)
     {
-      if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
-       igmp_clear_config (config);
+      igmp_clear_config (config);
     }
+
+  return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+  clib_error_t *error = NULL;
+  /* remove igmp state from down interfaces */
+  if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
+    vnet_hw_interface_walk_sw (vnm, hw_if_index, igmp_sw_if_down, NULL);
   return error;
 }
 
 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down);
+int
+igmp_enable_disable (u32 sw_if_index, u8 enable, igmp_mode_t mode)
+{
+  igmp_config_t *config;
+  igmp_main_t *im = &igmp_main;
+  u32 mfib_index;
+  IGMP_DBG ("%s:  %U", (enable ? "Enabled" : "Disabled"),
+           format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index);
+
+  /* *INDENT-OFF* */
+  fib_route_path_t for_us_path =
+    {
+      .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
+      .frp_addr = zero_addr,
+      .frp_sw_if_index = 0xffffffff,
+      .frp_fib_index = 0,
+      .frp_weight = 1,
+      .frp_flags = FIB_ROUTE_PATH_LOCAL,
+    };
+  fib_route_path_t via_itf_path =
+    {
+      .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
+      .frp_addr = zero_addr,
+      .frp_sw_if_index = sw_if_index,
+      .frp_fib_index = 0,
+      .frp_weight = 1,
+    };
+  /* *INDENT-ON* */
+  /* find configuration, if it dosn't exist, create new */
+  config = igmp_config_lookup (sw_if_index);
+  mfib_index = mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
+                                                    sw_if_index);
+  if (!config && enable)
+    {
+      u32 ii;
+
+      vec_validate_init_empty (im->igmp_config_by_sw_if_index,
+                              sw_if_index, ~0);
+      pool_get (im->configs, config);
+      memset (config, 0, sizeof (igmp_config_t));
+      config->sw_if_index = sw_if_index;
+      config->igmp_group_by_key =
+       hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
+      config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
+      config->mode = mode;
+
+      for (ii = 0; ii < IGMP_CONFIG_N_TIMERS; ii++)
+       config->timers[ii] = IGMP_TIMER_ID_INVALID;
+
+      if (IGMP_MODE_ROUTER == mode)
+       {
+         config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY] =
+           igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_QUERY),
+                                igmp_config_index (config),
+                                igmp_send_general_query, NULL);
+       }
+
+      config->adj_index =
+       adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
+                              config->sw_if_index);
+      im->igmp_config_by_sw_if_index[config->sw_if_index] =
+       (config - im->configs);
+      {
+       vec_validate (im->n_configs_per_mfib_index, mfib_index);
+       im->n_configs_per_mfib_index[mfib_index]++;
+       if (1 == im->n_configs_per_mfib_index[mfib_index])
+         {
+           /* first config in this FIB */
+           mfib_table_entry_path_update (mfib_index,
+                                         &mpfx_general_query,
+                                         MFIB_SOURCE_IGMP,
+                                         &for_us_path,
+                                         MFIB_ITF_FLAG_FORWARD);
+           mfib_table_entry_path_update (mfib_index,
+                                         &mpfx_report,
+                                         MFIB_SOURCE_IGMP,
+                                         &for_us_path,
+                                         MFIB_ITF_FLAG_FORWARD);
+         }
+       mfib_table_entry_path_update (mfib_index,
+                                     &mpfx_general_query,
+                                     MFIB_SOURCE_IGMP,
+                                     &via_itf_path, MFIB_ITF_FLAG_ACCEPT);
+       mfib_table_entry_path_update (mfib_index, &mpfx_report,
+                                     MFIB_SOURCE_IGMP, &via_itf_path,
+                                     MFIB_ITF_FLAG_ACCEPT);
+      }
+    }
+  else if (config && !enable)
+    {
+      vec_validate (im->n_configs_per_mfib_index, mfib_index);
+      im->n_configs_per_mfib_index[mfib_index]--;
+      if (0 == im->n_configs_per_mfib_index[mfib_index])
+       {
+         /* last config in this FIB */
+         mfib_table_entry_path_remove (mfib_index,
+                                       &mpfx_general_query,
+                                       MFIB_SOURCE_IGMP, &for_us_path);
+         mfib_table_entry_path_remove (mfib_index,
+                                       &mpfx_report,
+                                       MFIB_SOURCE_IGMP, &for_us_path);
+       }
+
+      mfib_table_entry_path_remove (mfib_index,
+                                   &mpfx_general_query,
+                                   MFIB_SOURCE_IGMP, &via_itf_path);
+      mfib_table_entry_path_remove (mfib_index,
+                                   &mpfx_report,
+                                   MFIB_SOURCE_IGMP, &via_itf_path);
+      igmp_clear_config (config);
+      im->igmp_config_by_sw_if_index[config->sw_if_index] = ~0;
+      hash_free (config->igmp_group_by_key);
+      pool_put (im->configs, config);
+    }
+
+  return (0);
+}
 
 /** \brief igmp initialization
     @param vm - vlib main
@@ -952,97 +464,24 @@ igmp_init (vlib_main_t * vm)
 {
   clib_error_t *error;
   igmp_main_t *im = &igmp_main;
-  int i;
+
   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
     return error;
-  im->igmp_config_by_sw_if_index = hash_create (0, sizeof (u32));
-  im->igmp_api_client_by_client_index = hash_create (0, sizeof (u32));
-  ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
-  igmp_type_info_t *ti;
-  igmp_report_type_info_t *rti;
-#define igmp_type(n,s)                         \
-do {                                           \
-  vec_add2 (im->type_infos, ti, 1);            \
-  ti->type = n;                                        \
-  ti->name = (u8 *) #s;                                \
-} while (0);
-#define igmp_report_type(n,s)                          \
-do {                                           \
-  vec_add2 (im->report_type_infos, rti, 1);            \
-  rti->type = n;                                       \
-  rti->name = (u8 *) #s;                               \
-} while (0);
-#include "igmp.def"
-#undef igmp_type
-#undef igmp_report_type
-  for (i = 0; i < vec_len (im->type_infos); i++)
-    {
-      ti = im->type_infos + i;
-      hash_set (im->type_info_by_type, ti->type, i);
-    }
-
-  for (i = 0; i < vec_len (im->report_type_infos); i++)
-    {
-      rti = im->report_type_infos + i;
-      hash_set (im->report_type_info_by_report_type, rti->type, i);
-    }
 
-  /* General Query address */
-  ip46_address_t addr0 = {
-    .as_u64[0] = 0,
-    .as_u64[1] = 0
-  };
-  addr0.ip4.as_u32 = clib_host_to_net_u32 (IGMP_GENERAL_QUERY_ADDRESS);
-
-  /* Report address */
-  ip46_address_t addr1 = {
-    .as_u64[0] = 0,
-    .as_u64[1] = 0
-  };
-  addr1.ip4.as_u32 = clib_host_to_net_u32 (IGMP_MEMBERSHIP_REPORT_ADDRESS);
-
-  fib_route_path_t path = {
-    .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
-    .frp_addr = zero_addr,
-    .frp_sw_if_index = 0xffffffff,
-    .frp_fib_index = 0,
-    .frp_weight = 0,
-    .frp_flags = FIB_ROUTE_PATH_LOCAL,
-  };
+  im->igmp_api_client_by_client_index = hash_create (0, sizeof (u32));
 
-  const mfib_prefix_t mpfx0 = {
-    .fp_proto = FIB_PROTOCOL_IP4,
-    .fp_len = 32,
-    .fp_grp_addr = addr0,
-  };
+  im->logger = vlib_log_register_class ("igmp", 0);
 
-  const mfib_prefix_t mpfx1 = {
-    .fp_proto = FIB_PROTOCOL_IP4,
-    .fp_len = 32,
-    .fp_grp_addr = addr1,
-  };
+  IGMP_DBG ("initialized");
 
-  /* configure MFIB to accept IGMPv3 general query
-   * and reports from all interfaces
-   */
-  mfib_table_entry_path_update (0, &mpfx0,
-                               MFIB_SOURCE_DEFAULT_ROUTE, &path,
-                               MFIB_ITF_FLAG_FORWARD);
-  mfib_table_entry_path_update (0, &mpfx1,
-                               MFIB_SOURCE_DEFAULT_ROUTE, &path,
-                               MFIB_ITF_FLAG_FORWARD);
-  mfib_table_entry_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE,
-                          0, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
-  mfib_table_entry_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE,
-                          0, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
   return (error);
 }
 
 VLIB_INIT_FUNCTION (igmp_init);
 /* *INDENT-OFF* */
 VLIB_PLUGIN_REGISTER () = {
-    .version = VPP_BUILD_VER,
-    .description = "IGMP messaging",
+  .version = VPP_BUILD_VER,
+  .description = "IGMP messaging",
 };
 /* *INDENT-ON* */