aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Grajciar <jgrajcia@cisco.com>2018-10-04 11:05:35 +0200
committerNeale Ranns <nranns@cisco.com>2018-10-16 08:28:15 +0000
commit97748cae2e6261d8fdc7c331a4d82828ac51ed81 (patch)
tree503ec3556042aef7d0b1575baca06fd239d09828
parent02a95ce8d53caeba07a3c027b12339f44f3093b8 (diff)
IGMP: proxy device
Create 'proxy device' per VRF and add one upstream and one or many downstream interfaces. Change-Id: I1cef05fb01e73a5b483c7a2f4debaaeffe2c8155 Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
-rw-r--r--src/plugins/igmp/CMakeLists.txt1
-rw-r--r--src/plugins/igmp/igmp.api42
-rw-r--r--src/plugins/igmp/igmp.c13
-rw-r--r--src/plugins/igmp/igmp.h11
-rw-r--r--src/plugins/igmp/igmp_api.c57
-rw-r--r--src/plugins/igmp/igmp_cli.c132
-rw-r--r--src/plugins/igmp/igmp_config.h7
-rw-r--r--src/plugins/igmp/igmp_format.c10
-rw-r--r--src/plugins/igmp/igmp_format.h2
-rw-r--r--src/plugins/igmp/igmp_group.c17
-rw-r--r--src/plugins/igmp/igmp_proxy.c397
-rw-r--r--src/plugins/igmp/igmp_proxy.h62
-rw-r--r--src/plugins/igmp/igmp_report.c2
-rw-r--r--src/plugins/igmp/igmp_src.c2
-rw-r--r--src/plugins/igmp/igmp_src.h6
-rw-r--r--test/test_igmp.py87
-rw-r--r--test/vpp_papi_provider.py12
17 files changed, 848 insertions, 12 deletions
diff --git a/src/plugins/igmp/CMakeLists.txt b/src/plugins/igmp/CMakeLists.txt
index 089046e9720..725f5185569 100644
--- a/src/plugins/igmp/CMakeLists.txt
+++ b/src/plugins/igmp/CMakeLists.txt
@@ -26,6 +26,7 @@ add_vpp_plugin(igmp
igmp_pkt.c
igmp_ssm_range.c
igmp_format.c
+ igmp_proxy.c
API_FILES
igmp.api
diff --git a/src/plugins/igmp/igmp.api b/src/plugins/igmp/igmp.api
index 2a2e68030f6..db47905ae6f 100644
--- a/src/plugins/igmp/igmp.api
+++ b/src/plugins/igmp/igmp.api
@@ -99,6 +99,48 @@ autoreply define igmp_enable_disable
};
/**
+ * @brief
+ * Add/del proxy device on specified VRF.
+ * Interface must be IGMP enabled in HOST mode.
+ *
+ * @param client_index - opaque cookie to identify the sender
+ * @param context - sender context, to match reply w/ request
+ * @param add - add (1) del (0)
+ * @param vrf_id - VRF id
+ * @param sw_if_index - upstream interface sw index
+ */
+autoreply define igmp_proxy_device_add_del
+{
+ u32 client_index;
+ u32 context;
+
+ u8 add;
+ u32 vrf_id;
+ u32 sw_if_index;
+};
+
+/**
+ * @brief
+ * Add/del downstream interface to/from proxy device.
+ * Interface must be IGMP enabled in ROUTER mode.
+ *
+ * @param client_index - opaque cookie to identify the sender
+ * @param context - sender context, to match reply w/ request
+ * @param add - add (1) del (0)
+ * @param vrf_id - VRF id
+ * @param sw_if_index - downstream interface sw index
+ */
+autoreply define igmp_proxy_device_add_del_interface
+{
+ u32 client_index;
+ u32 context;
+
+ u8 add;
+ u32 vrf_id;
+ u32 sw_if_index;
+};
+
+/**
* @brief dump (S,G)s from interface
* @param client_index - opaque cookie to identify the sender
* @param context - sender context, to match reply w/ request
diff --git a/src/plugins/igmp/igmp.c b/src/plugins/igmp/igmp.c
index 85ba35c31d6..61a9970af14 100644
--- a/src/plugins/igmp/igmp.c
+++ b/src/plugins/igmp/igmp.c
@@ -381,6 +381,7 @@ igmp_enable_disable (u32 sw_if_index, u8 enable, igmp_mode_t mode)
hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
config->mode = mode;
+ config->proxy_device_id = ~0;
for (ii = 0; ii < IGMP_CONFIG_N_TIMERS; ii++)
config->timers[ii] = IGMP_TIMER_ID_INVALID;
@@ -445,6 +446,18 @@ igmp_enable_disable (u32 sw_if_index, u8 enable, igmp_mode_t mode)
mfib_table_entry_path_remove (mfib_index,
&mpfx_report,
MFIB_SOURCE_IGMP, &via_itf_path);
+
+ /*
+ * remove interface from proxy device
+ * if this device is upstream, delete proxy device
+ */
+ if (config->mode == IGMP_MODE_ROUTER)
+ igmp_proxy_device_add_del_interface (config->proxy_device_id,
+ config->sw_if_index, 0);
+ else if (config->mode == IGMP_MODE_HOST)
+ igmp_proxy_device_add_del (config->proxy_device_id,
+ config->sw_if_index, 0);
+
igmp_clear_config (config);
im->igmp_config_by_sw_if_index[config->sw_if_index] = ~0;
hash_free (config->igmp_group_by_key);
diff --git a/src/plugins/igmp/igmp.h b/src/plugins/igmp/igmp.h
index 626d9879158..9f9b611a649 100644
--- a/src/plugins/igmp/igmp.h
+++ b/src/plugins/igmp/igmp.h
@@ -28,6 +28,7 @@
#include <igmp/igmp_timer.h>
#include <igmp/igmp_group.h>
#include <igmp/igmp_config.h>
+#include <igmp/igmp_proxy.h>
/**
* RFC 3376 Section 8.1
@@ -101,6 +102,16 @@ typedef struct igmp_main_t_
* pool of sources
*/
igmp_src_t *srcs;
+
+ /**
+ * per-vrf DB of proxy devices
+ */
+ u32 *igmp_proxy_device_by_vrf_id;
+
+ /**
+ * pool of proxy devices
+ */
+ igmp_proxy_device_t *proxy_devices;
} igmp_main_t;
extern igmp_main_t igmp_main;
diff --git a/src/plugins/igmp/igmp_api.c b/src/plugins/igmp/igmp_api.c
index 06bf26bbc17..5d836967b4b 100644
--- a/src/plugins/igmp/igmp_api.c
+++ b/src/plugins/igmp/igmp_api.c
@@ -50,15 +50,17 @@
#define IGMP_MSG_ID(_id) (_id + igmp_main.msg_id_base)
-#define foreach_igmp_plugin_api_msg \
-_(IGMP_LISTEN, igmp_listen) \
-_(IGMP_ENABLE_DISABLE, igmp_enable_disable) \
-_(IGMP_DUMP, igmp_dump) \
-_(IGMP_CLEAR_INTERFACE, igmp_clear_interface) \
-_(IGMP_CLEAR_INTERFACE, igmp_clear_interface) \
-_(IGMP_GROUP_PREFIX_SET, igmp_group_prefix_set) \
-_(IGMP_GROUP_PREFIX_DUMP, igmp_group_prefix_dump) \
-_(WANT_IGMP_EVENTS, want_igmp_events) \
+#define foreach_igmp_plugin_api_msg \
+_(IGMP_LISTEN, igmp_listen) \
+_(IGMP_ENABLE_DISABLE, igmp_enable_disable) \
+_(IGMP_PROXY_DEVICE_ADD_DEL, igmp_proxy_device_add_del) \
+_(IGMP_PROXY_DEVICE_ADD_DEL_INTERFACE, igmp_proxy_device_add_del_interface) \
+_(IGMP_DUMP, igmp_dump) \
+_(IGMP_CLEAR_INTERFACE, igmp_clear_interface) \
+_(IGMP_CLEAR_INTERFACE, igmp_clear_interface) \
+_(IGMP_GROUP_PREFIX_SET, igmp_group_prefix_set) \
+_(IGMP_GROUP_PREFIX_DUMP, igmp_group_prefix_dump) \
+_(WANT_IGMP_EVENTS, want_igmp_events) \
static void
vl_api_igmp_listen_t_handler (vl_api_igmp_listen_t * mp)
@@ -121,6 +123,43 @@ vl_api_igmp_enable_disable_t_handler (vl_api_igmp_enable_disable_t * mp)
}
static void
+vl_api_igmp_proxy_device_add_del_t_handler (vl_api_igmp_proxy_device_add_del_t
+ * mp)
+{
+ vl_api_igmp_proxy_device_add_del_reply_t *rmp;
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ rv =
+ igmp_proxy_device_add_del (ntohl (mp->vrf_id), ntohl (mp->sw_if_index),
+ mp->add);
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (IGMP_MSG_ID (VL_API_IGMP_PROXY_DEVICE_ADD_DEL_REPLY));
+}
+
+static void
+ vl_api_igmp_proxy_device_add_del_interface_t_handler
+ (vl_api_igmp_proxy_device_add_del_interface_t * mp)
+{
+ vl_api_igmp_proxy_device_add_del_interface_reply_t *rmp;
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ rv =
+ igmp_proxy_device_add_del_interface (ntohl (mp->vrf_id),
+ ntohl (mp->sw_if_index), mp->add);
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (IGMP_MSG_ID
+ (VL_API_IGMP_PROXY_DEVICE_ADD_DEL_INTERFACE_REPLY));
+}
+
+static void
send_igmp_details (unix_shared_memory_queue_t * q, igmp_main_t * im,
igmp_config_t * config, igmp_group_t * group,
igmp_src_t * src, u32 context)
diff --git a/src/plugins/igmp/igmp_cli.c b/src/plugins/igmp/igmp_cli.c
index e943fbb8349..6247f9a9388 100644
--- a/src/plugins/igmp/igmp_cli.c
+++ b/src/plugins/igmp/igmp_cli.c
@@ -220,6 +220,134 @@ VLIB_CLI_COMMAND (igmp_enable_command, static) = {
/* *INDENT-ON* */
static clib_error_t *
+igmp_proxy_device_add_del_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_main_t *vnm = vnet_get_main ();
+ clib_error_t *error = NULL;
+ u32 sw_if_index = ~0;
+ u32 vrf_id = ~0;
+ u8 add = 1;
+ int rv;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return error;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "add"))
+ add = 1;
+ else if (unformat (line_input, "del"))
+ add = 0;
+ else if (unformat (line_input, "vrf-id %u", &vrf_id))
+ ;
+ else if (unformat (line_input, "%U",
+ unformat_vnet_sw_interface, vnm, &sw_if_index));
+ else
+ {
+ error =
+ clib_error_return (0, "unknown input '%U'", format_unformat_error,
+ line_input);
+ goto done;
+ }
+ }
+
+ if (~0 == sw_if_index)
+ {
+ error = clib_error_return (0, "interface must be specified");
+ goto done;
+ }
+
+ if (~0 == vrf_id)
+ {
+ error = clib_error_return (0, "VRF must be specified");
+ goto done;
+ }
+
+ rv = igmp_proxy_device_add_del (vrf_id, sw_if_index, add);
+
+ if (0 != rv)
+ error = clib_error_return (0, "result: %d", rv);
+
+done:
+ unformat_free (line_input);
+ return error;
+}
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (igmp_proxy_device_add_del_command, static) = {
+ .path = "igmp proxy-dev",
+ .short_help = "igmp proxy-dev <add|del> vrf-id <table-id> <interface>",
+ .function = igmp_proxy_device_add_del_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+igmp_proxy_device_add_del_interface_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_main_t *vnm = vnet_get_main ();
+ clib_error_t *error = NULL;
+ u32 sw_if_index = ~0;
+ u32 vrf_id = ~0;
+ u8 add = 1;
+ int rv;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return error;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "add"))
+ add = 1;
+ else if (unformat (line_input, "del"))
+ add = 0;
+ else if (unformat (line_input, "vrf-id %u", &vrf_id))
+ ;
+ else if (unformat (line_input, "%U",
+ unformat_vnet_sw_interface, vnm, &sw_if_index));
+ else
+ {
+ error =
+ clib_error_return (0, "unknown input '%U'", format_unformat_error,
+ line_input);
+ goto done;
+ }
+ }
+
+ if (~0 == sw_if_index)
+ {
+ error = clib_error_return (0, "interface must be specified");
+ goto done;
+ }
+
+ if (~0 == vrf_id)
+ {
+ error = clib_error_return (0, "VRF must be specified");
+ goto done;
+ }
+
+ rv = igmp_proxy_device_add_del_interface (vrf_id, sw_if_index, add);
+
+ if (0 != rv)
+ error = clib_error_return (0, "result: %d", rv);
+
+done:
+ unformat_free (line_input);
+ return error;
+}
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (igmp_proxy_device_add_del_interface_command, static) = {
+ .path = "igmp proxy-dev itf",
+ .short_help = "igmp proxy-dev itf <add|del> vrf-id <table-id> <interface>",
+ .function = igmp_proxy_device_add_del_interface_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
igmp_show_command_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
{
@@ -233,9 +361,9 @@ igmp_show_command_fn (vlib_main_t * vm, unformat_input_t * input,
/* *INDENT-OFF* */
pool_foreach (config, im->configs,
({
- vlib_cli_output (vm, "interface: %U mode:%U",
+ vlib_cli_output (vm, "interface: %U mode: %U %U",
format_vnet_sw_if_index_name, vnm, config->sw_if_index,
- format_igmp_mode, config->mode);
+ format_igmp_mode, config->mode, format_igmp_proxy_device_id, config->proxy_device_id);
FOR_EACH_GROUP (group, config,
({
diff --git a/src/plugins/igmp/igmp_config.h b/src/plugins/igmp/igmp_config.h
index a9f48a768fa..0da2525a851 100644
--- a/src/plugins/igmp/igmp_config.h
+++ b/src/plugins/igmp/igmp_config.h
@@ -53,7 +53,7 @@ typedef struct igmp_config_t_
adj_index_t adj_index;
/**
- * @param moe - host or router
+ * @param mode - host or router
*/
igmp_mode_t mode;
@@ -71,6 +71,11 @@ typedef struct igmp_config_t_
* A vector of scheduled query-response timers
*/
igmp_timer_id_t timers[IGMP_CONFIG_N_TIMERS];
+
+ /**
+ * ID of a proxy device this configuration is on
+ */
+ u32 proxy_device_id;
} igmp_config_t;
#define FOR_EACH_GROUP(_group, _config, _body) \
diff --git a/src/plugins/igmp/igmp_format.c b/src/plugins/igmp/igmp_format.c
index 08b5f68a74d..7606fc5bf5d 100644
--- a/src/plugins/igmp/igmp_format.c
+++ b/src/plugins/igmp/igmp_format.c
@@ -209,6 +209,16 @@ format_igmp_key (u8 * s, va_list * args)
return (s);
}
+u8 *
+format_igmp_proxy_device_id (u8 * s, va_list * args)
+{
+ u32 id = va_arg (*args, u32);
+
+ s = (id == ~0) ? s : format (s, "proxy device: %u", id);
+
+ return (s);
+}
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/plugins/igmp/igmp_format.h b/src/plugins/igmp/igmp_format.h
index 84862bb3afc..b7e6c954ac9 100644
--- a/src/plugins/igmp/igmp_format.h
+++ b/src/plugins/igmp/igmp_format.h
@@ -36,6 +36,8 @@ extern u8 *format_igmp_src_addr_list (u8 * s, va_list * args);
extern u8 *format_igmp_key (u8 * s, va_list * args);
+extern u8 *format_igmp_proxy_device_id (u8 * s, va_list * args);
+
#endif /* IGMP_FORMAT_H */
/*
diff --git a/src/plugins/igmp/igmp_group.c b/src/plugins/igmp/igmp_group.c
index fe023a46167..9f50ad9b05a 100644
--- a/src/plugins/igmp/igmp_group.c
+++ b/src/plugins/igmp/igmp_group.c
@@ -74,6 +74,14 @@ igmp_group_clear (igmp_group_t * group)
config = igmp_config_get (group->config);
+ /* If interface is in ROUTER mode and IGMP proxy is enabled
+ * remove mfib path.
+ */
+ if (config->mode == IGMP_MODE_ROUTER)
+ {
+ igmp_proxy_device_mfib_path_add_del (group, /* add */ 0);
+ }
+
IGMP_DBG ("clear-group: %U %U",
format_igmp_key, group->key,
format_vnet_sw_if_index_name,
@@ -116,6 +124,15 @@ igmp_group_alloc (igmp_config_t * config,
group->timers[ii] = IGMP_TIMER_ID_INVALID;
hash_set_mem (config->igmp_group_by_key, group->key, group - im->groups);
+
+ /* If interface is in ROUTER mode and IGMP proxy is enabled
+ * add mfib path.
+ */
+ if (config->mode == IGMP_MODE_ROUTER)
+ {
+ igmp_proxy_device_mfib_path_add_del (group, /* add */ 1);
+ }
+
return (group);
}
diff --git a/src/plugins/igmp/igmp_proxy.c b/src/plugins/igmp/igmp_proxy.c
new file mode 100644
index 00000000000..4a439d650c9
--- /dev/null
+++ b/src/plugins/igmp/igmp_proxy.c
@@ -0,0 +1,397 @@
+/*
+ *------------------------------------------------------------------
+ * 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 <vlib/vlib.h>
+#include <vnet/mfib/mfib_entry.h>
+#include <vnet/mfib/mfib_table.h>
+
+#include <igmp/igmp_proxy.h>
+#include <igmp/igmp.h>
+#include <igmp/igmp_pkt.h>
+
+void
+igmp_proxy_device_mfib_path_add_del (igmp_group_t * group, u8 add)
+{
+ igmp_config_t *config;
+ u32 mfib_index;
+
+ config = igmp_config_get (group->config);
+ mfib_index =
+ mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
+ config->sw_if_index);
+
+ /* *INDENT-OFF* */
+ mfib_prefix_t mpfx_group_addr = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_grp_addr = {
+ .ip4 = (*group->key).ip4,
+ },
+ };
+ fib_route_path_t via_itf_path =
+ {
+ .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
+ .frp_addr = zero_addr,
+ .frp_sw_if_index = config->sw_if_index,
+ .frp_fib_index = 0,
+ .frp_weight = 1,
+ };
+ /* *INDENT-ON* */
+
+ if (add)
+ mfib_table_entry_path_update (mfib_index, &mpfx_group_addr,
+ MFIB_SOURCE_IGMP, &via_itf_path,
+ MFIB_ITF_FLAG_FORWARD);
+ else
+ mfib_table_entry_path_remove (mfib_index, &mpfx_group_addr,
+ MFIB_SOURCE_IGMP, &via_itf_path);
+}
+
+igmp_proxy_device_t *
+igmp_proxy_device_lookup (u32 vrf_id)
+{
+ igmp_main_t *im = &igmp_main;
+
+ if (vec_len (im->igmp_proxy_device_by_vrf_id) > vrf_id)
+ {
+ u32 index;
+ index = im->igmp_proxy_device_by_vrf_id[vrf_id];
+ if (index != ~0)
+ return (vec_elt_at_index (im->proxy_devices, index));
+ }
+ return NULL;
+}
+
+int
+igmp_proxy_device_add_del (u32 vrf_id, u32 sw_if_index, u8 add)
+{
+ igmp_main_t *im = &igmp_main;
+ igmp_proxy_device_t *proxy_device;
+ igmp_config_t *config;
+ u32 mfib_index;
+
+ /* check VRF id */
+ mfib_index =
+ mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+ if (mfib_index == ~0)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+ if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+
+ /* check IGMP configuration */
+ config = igmp_config_lookup (sw_if_index);
+ if (!config)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+ if (config->mode != IGMP_MODE_HOST)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+
+ proxy_device = igmp_proxy_device_lookup (vrf_id);
+ if (!proxy_device && add)
+ {
+ vec_validate_init_empty (im->igmp_proxy_device_by_vrf_id, vrf_id, ~0);
+ pool_get (im->proxy_devices, proxy_device);
+ im->igmp_proxy_device_by_vrf_id[vrf_id] =
+ proxy_device - im->proxy_devices;
+ memset (proxy_device, 0, sizeof (igmp_proxy_device_t));
+ proxy_device->vrf_id = vrf_id;
+ proxy_device->upstream_if = sw_if_index;
+ config->proxy_device_id = vrf_id;
+ /* lock mfib table */
+ mfib_table_lock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
+ }
+ else if (proxy_device && !add)
+ {
+ while (vec_len (proxy_device->downstream_ifs) > 0)
+ {
+ igmp_proxy_device_add_del_interface (vrf_id,
+ proxy_device->downstream_ifs
+ [0], 0);
+ }
+ vec_free (proxy_device->downstream_ifs);
+ proxy_device->downstream_ifs = NULL;
+ im->igmp_proxy_device_by_vrf_id[vrf_id] = ~0;
+ pool_put (im->proxy_devices, proxy_device);
+ config->proxy_device_id = ~0;
+ /* clear proxy database */
+ igmp_clear_config (config);
+ /* unlock mfib table */
+ mfib_table_unlock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
+ }
+ else
+ return -1;
+
+ return 0;
+}
+
+int
+igmp_proxy_device_add_del_interface (u32 vrf_id, u32 sw_if_index, u8 add)
+{
+ igmp_proxy_device_t *proxy_device;
+ u32 index;
+ u32 mfib_index;
+
+ proxy_device = igmp_proxy_device_lookup (vrf_id);
+ if (!proxy_device)
+ return -1;
+
+ /* check VRF id */
+ mfib_index =
+ mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+ if (mfib_index == ~0)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+ if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+
+ /* check IGMP configuration */
+ igmp_config_t *config;
+ config = igmp_config_lookup (sw_if_index);
+ if (!config)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+ if (config->mode != IGMP_MODE_ROUTER)
+ return VNET_API_ERROR_INVALID_INTERFACE;
+
+ if (add)
+ {
+ if (proxy_device->downstream_ifs)
+ {
+ index = vec_search (proxy_device->downstream_ifs, sw_if_index);
+ if (index != ~0)
+ return -1;
+ }
+ vec_add1 (proxy_device->downstream_ifs, sw_if_index);
+ config->proxy_device_id = vrf_id;
+ }
+ else
+ {
+ if (!proxy_device->downstream_ifs)
+ return -2;
+ index = vec_search (proxy_device->downstream_ifs, sw_if_index);
+ if (index == ~0)
+ return -3;
+ /* remove (S,G)s belonging to this interface from proxy database */
+ igmp_proxy_device_merge_config (config, /* block */ 1);
+ vec_del1 (proxy_device->downstream_ifs, index);
+ config->proxy_device_id = ~0;
+ }
+
+ return 0;
+}
+
+void
+igmp_proxy_device_block_src (igmp_config_t * config, igmp_group_t * group,
+ igmp_src_t * src)
+{
+ igmp_proxy_device_t *proxy_device;
+ igmp_config_t *proxy_config;
+ igmp_group_t *proxy_group;
+ igmp_src_t *proxy_src;
+ u8 *ref;
+
+ proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
+ if (!proxy_device)
+ return;
+
+ proxy_config = igmp_config_lookup (proxy_device->upstream_if);
+ ASSERT (proxy_config);
+
+ proxy_group = igmp_group_lookup (proxy_config, group->key);
+ if (proxy_group == NULL)
+ return;
+
+ proxy_src = igmp_src_lookup (proxy_group, src->key);
+ if (proxy_src == NULL)
+ return;
+
+ if (vec_len (proxy_src->referance_by_config_index) <= group->config)
+ {
+ IGMP_DBG ("proxy block src: invalid config %u", group->config);
+ return;
+ }
+ proxy_src->referance_by_config_index[group->config] = 0;
+ vec_foreach (ref, proxy_src->referance_by_config_index)
+ {
+ if ((*ref) > 0)
+ return;
+ }
+
+ /* build "Block Old Sources" report */
+ igmp_pkt_build_report_t br;
+ ip46_address_t *srcaddrs = NULL;
+
+ igmp_pkt_build_report_init (&br, proxy_config->sw_if_index);
+ vec_add1 (srcaddrs, *proxy_src->key);
+ igmp_pkt_report_v3_add_report (&br, proxy_group->key, srcaddrs,
+ IGMP_MEMBERSHIP_GROUP_block_old_sources);
+ igmp_pkt_report_v3_send (&br);
+
+
+ igmp_group_src_remove (proxy_group, proxy_src);
+ igmp_src_free (proxy_src);
+
+ if (igmp_group_n_srcs (proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
+ {
+ igmp_proxy_device_mfib_path_add_del (proxy_group, 0);
+ igmp_proxy_device_mfib_path_add_del (group, 0);
+ igmp_group_clear (proxy_group);
+ }
+}
+
+always_inline void
+igmp_proxy_device_merge_src (igmp_group_t * proxy_group, igmp_src_t * src,
+ ip46_address_t ** srcaddrs, u8 block)
+{
+ igmp_src_t *proxy_src;
+ u32 d_config;
+
+ proxy_src = igmp_src_lookup (proxy_group, src->key);
+
+ if (proxy_src == NULL)
+ {
+ if (block)
+ return;
+ /* store downstream config index */
+ d_config = igmp_group_get (src->group)->config;
+
+ proxy_src =
+ igmp_src_alloc (igmp_group_index (proxy_group), src->key,
+ IGMP_MODE_HOST);
+
+ hash_set_mem (proxy_group->igmp_src_by_key
+ [proxy_group->router_filter_mode], proxy_src->key,
+ igmp_src_index (proxy_src));
+
+ vec_validate_init_empty (proxy_src->referance_by_config_index, d_config,
+ 0);
+ proxy_src->referance_by_config_index[d_config] = 1;
+ vec_add1 (*srcaddrs, *proxy_src->key);
+ }
+ else
+ {
+ if (block)
+ {
+ d_config = igmp_group_get (src->group)->config;
+ if (vec_len (proxy_src->referance_by_config_index) <= d_config)
+ {
+ IGMP_DBG ("proxy block src: invalid config %u", d_config);
+ return;
+ }
+ proxy_src->referance_by_config_index[d_config] = 0;
+ u8 *ref;
+ vec_foreach (ref, proxy_src->referance_by_config_index)
+ {
+ if ((*ref) > 0)
+ return;
+ }
+
+ vec_add1 (*srcaddrs, *proxy_src->key);
+
+ igmp_group_src_remove (proxy_group, proxy_src);
+ igmp_src_free (proxy_src);
+
+ if (igmp_group_n_srcs (proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
+ {
+ igmp_proxy_device_mfib_path_add_del (proxy_group, 0);
+ igmp_group_clear (proxy_group);
+ }
+ return;
+ }
+ d_config = igmp_group_get (src->group)->config;
+ vec_validate (proxy_src->referance_by_config_index, d_config);
+ proxy_src->referance_by_config_index[d_config] = 1;
+ return;
+ }
+}
+
+always_inline igmp_group_t *
+igmp_proxy_device_merge_group (igmp_proxy_device_t * proxy_device,
+ igmp_group_t * group,
+ ip46_address_t ** srcaddrs, u8 block)
+{
+ igmp_config_t *proxy_config;
+ igmp_group_t *proxy_group;
+ igmp_src_t *src;
+
+ proxy_config = igmp_config_lookup (proxy_device->upstream_if);
+ ASSERT (proxy_config);
+
+ proxy_group = igmp_group_lookup (proxy_config, group->key);
+ if (!proxy_group)
+ {
+ if (block)
+ return NULL;
+ u32 tmp = igmp_group_index (group);
+ proxy_group =
+ igmp_group_alloc (proxy_config, group->key,
+ group->router_filter_mode);
+ igmp_proxy_device_mfib_path_add_del (proxy_group, 1);
+ group = igmp_group_get (tmp);
+ }
+ if (block)
+ {
+ igmp_proxy_device_mfib_path_add_del (group, 0);
+ }
+
+ /* *INDENT-OFF* */
+ FOR_EACH_SRC (src, group, group->router_filter_mode,
+ ({
+ igmp_proxy_device_merge_src (proxy_group, src, srcaddrs, block);
+ }));
+ /* *INDENT-ON* */
+ return proxy_group;
+}
+
+void
+igmp_proxy_device_merge_config (igmp_config_t * config, u8 block)
+{
+ igmp_proxy_device_t *proxy_device;
+ igmp_group_t *group;
+ igmp_group_t *proxy_group;
+ ip46_address_t *srcaddrs = NULL;
+ igmp_pkt_build_report_t br;
+
+ proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
+ if (!proxy_device)
+ return;
+
+ igmp_pkt_build_report_init (&br, proxy_device->upstream_if);
+
+ /* *INDENT-OFF* */
+ FOR_EACH_GROUP(group, config,
+ ({
+ proxy_group = igmp_proxy_device_merge_group (proxy_device, group, &srcaddrs, block);
+
+ if ((vec_len(srcaddrs) > 0) && proxy_group)
+ {
+ igmp_pkt_report_v3_add_report (&br, proxy_group->key, srcaddrs,
+ block ? IGMP_MEMBERSHIP_GROUP_block_old_sources :
+ IGMP_MEMBERSHIP_GROUP_allow_new_sources);
+ }
+ vec_free (srcaddrs);
+ }));
+ /* *INDENT-ON* */
+
+ igmp_pkt_report_v3_send (&br);
+
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/igmp/igmp_proxy.h b/src/plugins/igmp/igmp_proxy.h
new file mode 100644
index 00000000000..6d49159518b
--- /dev/null
+++ b/src/plugins/igmp/igmp_proxy.h
@@ -0,0 +1,62 @@
+/*
+ *------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+ */
+
+#ifndef _IGMP_PROXY_H_
+#define _IGMP_PROXY_H_
+
+#include <igmp/igmp_types.h>
+#include <igmp/igmp_config.h>
+
+typedef struct
+{
+ /* VRF index */
+ u32 vrf_id;
+
+ /* upstrema interface */
+ u32 upstream_if;
+
+ /* downstream interfaces */
+ u32 *downstream_ifs;
+} igmp_proxy_device_t;
+
+/**
+ * @brief IGMP proxy device add/del
+ * @param vrf_id - VRF id
+ * @param sw_if_index - upstream interface
+ * @param add - add/del
+ *
+ * Add/del IGMP proxy device. Interface must be IGMP enabled in HOST mode.
+ */
+int igmp_proxy_device_add_del (u32 vfr_id, u32 sw_if_index, u8 add);
+
+/**
+ * @brief IGMP proxy device add/del interface
+ * @param vrf_id - VRF id
+ * @param sw_if_index - downstream interface
+ * @param add - add/del
+ *
+ * Add/del IGMP enabled interface in ROUTER mode to proxy device.
+ */
+int igmp_proxy_device_add_del_interface (u32 vrf_id, u32 sw_if_index, u8 add);
+
+void igmp_proxy_device_merge_config (igmp_config_t *config, u8 block);
+
+void igmp_proxy_device_block_src (igmp_config_t *config, igmp_group_t *group, igmp_src_t *src);
+
+void igmp_proxy_device_mfib_path_add_del (igmp_group_t *group, u8 add);
+
+#endif /* IGMP_PROXY_H */
diff --git a/src/plugins/igmp/igmp_report.c b/src/plugins/igmp/igmp_report.c
index e0c127d8015..7c08f342199 100644
--- a/src/plugins/igmp/igmp_report.c
+++ b/src/plugins/igmp/igmp_report.c
@@ -196,6 +196,8 @@ igmp_handle_report (const igmp_report_args_t * args)
igmp_group = group_cptr (igmp_group,
igmp_membership_group_v3_length (igmp_group));
}
+
+ igmp_proxy_device_merge_config (config, 0);
}
/*
diff --git a/src/plugins/igmp/igmp_src.c b/src/plugins/igmp/igmp_src.c
index c70514a8693..0bdde83214c 100644
--- a/src/plugins/igmp/igmp_src.c
+++ b/src/plugins/igmp/igmp_src.c
@@ -58,6 +58,8 @@ igmp_src_exp (u32 obj, void *dat)
igmp_event (IGMP_FILTER_MODE_EXCLUDE,
config->sw_if_index, src->key, group->key);
+
+ igmp_proxy_device_block_src (config, group, src);
}
igmp_group_src_remove (group, src);
diff --git a/src/plugins/igmp/igmp_src.h b/src/plugins/igmp/igmp_src.h
index 6b27cb6de10..86a043fa5c8 100644
--- a/src/plugins/igmp/igmp_src.h
+++ b/src/plugins/igmp/igmp_src.h
@@ -65,6 +65,12 @@ typedef struct igmp_src_t_
* Timers
*/
u32 timers[IGMP_SRC_N_TIMERS];
+
+ /**
+ * Tells us which configurations
+ * have this source.
+ */
+ u8 *referance_by_config_index;
} igmp_src_t;
extern void igmp_src_free (igmp_src_t * src);
diff --git a/test/test_igmp.py b/test/test_igmp.py
index 128ac5e8eff..da2fa9a304e 100644
--- a/test/test_igmp.py
+++ b/test/test_igmp.py
@@ -651,6 +651,93 @@ class TestIgmp(VppTestCase):
0,
IGMP_MODE.ROUTER)
+ def _create_igmpv3_pck(self, itf, rtype, maddr, srcaddrs):
+ p = (Ether(dst=itf.local_mac, src=itf.remote_mac) /
+ IP(src=itf.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1,
+ options=[IPOption(copy_flag=1, optclass="control",
+ option="router_alert")]) /
+ IGMPv3(type="Version 3 Membership Report") /
+ IGMPv3mr(numgrp=1) /
+ IGMPv3gr(rtype=rtype,
+ maddr=maddr, srcaddrs=srcaddrs))
+ return p
+
+ def test_igmp_proxy_device(self):
+ """ IGMP proxy device """
+ self.pg2.admin_down()
+ self.pg2.unconfig_ip4()
+ self.pg2.set_table_ip4(0)
+ self.pg2.config_ip4()
+ self.pg2.admin_up()
+
+ self.vapi.cli('test igmp timers query 10 src 3 leave 1')
+
+ # enable IGMP
+ self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
+ self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1,
+ IGMP_MODE.ROUTER)
+ self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1,
+ IGMP_MODE.ROUTER)
+
+ # create IGMP proxy device
+ self.vapi.igmp_proxy_device_add_del(0, self.pg0.sw_if_index, 1)
+ self.vapi.igmp_proxy_device_add_del_interface(0,
+ self.pg1.sw_if_index, 1)
+ self.vapi.igmp_proxy_device_add_del_interface(0,
+ self.pg2.sw_if_index, 1)
+
+ # send join on pg1. join should be proxied by pg0
+ p_j = self._create_igmpv3_pck(self.pg1, "Allow New Sources",
+ "239.1.1.1", ["10.1.1.1", "10.1.1.2"])
+ self.send(self.pg1, p_j)
+
+ capture = self.pg0.get_capture(1, timeout=1)
+ self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1",
+ ["10.1.1.1", "10.1.1.2"]), "Allow New Sources")])
+ self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
+
+ # send join on pg2. join should be proxied by pg0.
+ # the group should contain only 10.1.1.3 as
+ # 10.1.1.1 was already reported
+ p_j = self._create_igmpv3_pck(self.pg2, "Allow New Sources",
+ "239.1.1.1", ["10.1.1.1", "10.1.1.3"])
+ self.send(self.pg2, p_j)
+
+ capture = self.pg0.get_capture(1, timeout=1)
+ self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1",
+ ["10.1.1.3"]), "Allow New Sources")])
+ self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
+
+ # send leave on pg2. leave for 10.1.1.3 should be proxyed
+ # as pg2 was the only interface interested in 10.1.1.3
+ p_l = self._create_igmpv3_pck(self.pg2, "Block Old Sources",
+ "239.1.1.1", ["10.1.1.3"])
+ self.send(self.pg2, p_l)
+
+ capture = self.pg0.get_capture(1, timeout=2)
+ self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1",
+ ["10.1.1.3"]), "Block Old Sources")])
+ self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
+
+ # disable igmp on pg1 (also removes interface from proxy device)
+ # proxy leave for 10.1.1.2. pg2 is still interested in 10.1.1.1
+ self.pg_enable_capture(self.pg_interfaces)
+ self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0,
+ IGMP_MODE.ROUTER)
+
+ capture = self.pg0.get_capture(1, timeout=1)
+ self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1",
+ ["10.1.1.2"]), "Block Old Sources")])
+ self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
+
+ # disable IGMP on pg0 and pg1.
+ # disabling IGMP on pg0 (proxy device upstream interface)
+ # removes this proxy device
+ self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
+ self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0,
+ IGMP_MODE.ROUTER)
+ self.assertFalse(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 1f922a07b2b..4e7809f6960 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -3637,6 +3637,18 @@ class VppPapiProvider(object):
'mode': host,
'sw_if_index': sw_if_index})
+ def igmp_proxy_device_add_del(self, vrf_id, sw_if_index, add):
+ """ Add/del IGMP proxy device """
+ return self.api(self.papi.igmp_proxy_device_add_del,
+ {'vrf_id': vrf_id, 'sw_if_index': sw_if_index,
+ 'add': add})
+
+ def igmp_proxy_device_add_del_interface(self, vrf_id, sw_if_index, add):
+ """ Add/del interface to/from IGMP proxy device """
+ return self.api(self.papi.igmp_proxy_device_add_del_interface,
+ {'vrf_id': vrf_id, 'sw_if_index': sw_if_index,
+ 'add': add})
+
def igmp_listen(self, filter, sw_if_index, saddrs, gaddr):
""" Listen for new (S,G) on specified interface