IGMP improvements
[vpp.git] / src / plugins / igmp / igmp_report.c
diff --git a/src/plugins/igmp/igmp_report.c b/src/plugins/igmp/igmp_report.c
new file mode 100644 (file)
index 0000000..328b890
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <igmp/igmp_report.h>
+#include <igmp/igmp_pkt.h>
+
+static ip46_address_t *
+igmp_group_mk_source_list (const igmp_membership_group_v3_t * r)
+{
+  ip46_address_t *srcs = NULL;
+  const ip4_address_t *s;
+  u16 ii, n;
+
+  n = clib_net_to_host_u16 (r->n_src_addresses);
+
+  if (0 == n)
+    return (NULL);
+
+  vec_validate (srcs, n - 1);
+  s = r->src_addresses;
+
+  for (ii = 0; ii < n; ii++)
+    {
+      srcs[ii].ip4 = *s;
+      s++;
+    }
+
+  return (srcs);
+}
+
+static void
+igmp_handle_group_update (igmp_config_t * config,
+                         const igmp_membership_group_v3_t * igmp_group)
+{
+  ip46_address_t *src, *srcs;
+  igmp_group_t *group;
+  ip46_address_t key = {
+    .ip4 = igmp_group->group_address,
+  };
+
+  srcs = igmp_group_mk_source_list (igmp_group);
+  group = igmp_group_lookup (config, &key);
+
+  IGMP_DBG (" ..group-update: %U (%U, %U)",
+           format_vnet_sw_if_index_name,
+           vnet_get_main (), config->sw_if_index,
+           format_igmp_key, &key, format_igmp_src_addr_list, srcs);
+
+  if (NULL == group)
+    {
+      group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
+    }
+
+  /* create or update all sources */
+  vec_foreach (src, srcs)
+  {
+    igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
+  }
+
+  vec_free (srcs);
+}
+
+static void
+igmp_handle_group_block (igmp_config_t * config,
+                        const igmp_membership_group_v3_t * igmp_group)
+{
+  ip46_address_t *s, *srcs;
+  igmp_pkt_build_query_t bq;
+  igmp_group_t *group;
+  ip46_address_t key = {
+    .ip4 = igmp_group->group_address,
+  };
+
+  srcs = igmp_group_mk_source_list (igmp_group);
+  group = igmp_group_lookup (config, &key);
+
+  IGMP_DBG (" ..group-block: %U (%U, %U)",
+           format_vnet_sw_if_index_name,
+           vnet_get_main (), config->sw_if_index,
+           format_igmp_key, &key, format_igmp_src_addr_list, srcs);
+
+  if (group)
+    {
+      igmp_src_t *src;
+      /*
+       * sned a group+source specific query
+       */
+      igmp_pkt_build_query_init (&bq, config->sw_if_index);
+      igmp_pkt_query_v3_add_group (&bq, group, srcs);
+      igmp_pkt_query_v3_send (&bq);
+
+      /*
+       * for each source left/blocked drop the source expire timer to the leave
+       * latency timer
+       */
+      vec_foreach (s, srcs)
+      {
+       src = igmp_src_lookup (group, s);
+       if (NULL != src)
+         igmp_src_blocked (src);
+      }
+    }
+  /*
+   * a block/leave from a group for which we have no state
+   */
+
+  vec_free (srcs);
+}
+
+static void
+igmp_handle_group (igmp_config_t * config,
+                  const igmp_membership_group_v3_t * igmp_group)
+{
+  IGMP_DBG ("rx-group-report: %U",
+           format_vnet_sw_if_index_name,
+           vnet_get_main (), config->sw_if_index);
+
+  switch (igmp_group->type)
+    {
+    case IGMP_MEMBERSHIP_GROUP_mode_is_include:
+    case IGMP_MEMBERSHIP_GROUP_change_to_include:
+    case IGMP_MEMBERSHIP_GROUP_allow_new_sources:
+      igmp_handle_group_update (config, igmp_group);
+      break;
+    case IGMP_MEMBERSHIP_GROUP_block_old_sources:
+      igmp_handle_group_block (config, igmp_group);
+      break;
+    case IGMP_MEMBERSHIP_GROUP_mode_is_exclude:
+    case IGMP_MEMBERSHIP_GROUP_change_to_exclude:
+      break;
+      /*
+       * all other types ignored
+       */
+    }
+}
+
+void
+igmp_handle_report (const igmp_report_args_t * args)
+{
+  const igmp_membership_group_v3_t *igmp_group;
+  igmp_config_t *config;
+  u16 n_groups, ii;
+
+  config = igmp_config_lookup (args->sw_if_index);
+
+  if (!config)
+    /*
+     * no IGMP config on the interface. quit
+     */
+    return;
+
+  if (IGMP_MODE_HOST == config->mode)
+    {
+      /*
+       * Hosts need not listen to the reports of other hosts.
+       * we're done here
+       */
+      return;
+    }
+
+  n_groups = clib_net_to_host_u16 (args->report[0].n_groups);
+  igmp_group = args->report[0].groups;
+
+  for (ii = 0; ii < n_groups; ii++)
+    {
+      igmp_handle_group (config, igmp_group);
+
+      igmp_group = group_cptr (igmp_group,
+                              igmp_membership_group_v3_length (igmp_group));
+    }
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */