From b1d2aa8bca3a6e1811fb67dde9c8862e13216824 Mon Sep 17 00:00:00 2001 From: Jakub Grajciar Date: Mon, 23 Apr 2018 14:40:59 +0200 Subject: [PATCH] igmp: data structure refactoring Improve igmp membership report performance, introduce group and source specific timers. (side effect compatible with Group-specific query). Change-Id: Ie3dd2c0dabe5f7138c2f8029e6bbbbfcb5e4904f Signed-off-by: Jakub Grajciar --- src/plugins/igmp/cli.c | 14 +- src/plugins/igmp/igmp.c | 715 +++++++++++++++++++++++++++++--------------- src/plugins/igmp/igmp.h | 295 +++++++++++++++--- src/plugins/igmp/igmp_api.c | 46 +-- src/plugins/igmp/input.c | 100 ++++--- 5 files changed, 820 insertions(+), 350 deletions(-) diff --git a/src/plugins/igmp/cli.c b/src/plugins/igmp/cli.c index a69070f23ce..6ed5c5ed900 100644 --- a/src/plugins/igmp/cli.c +++ b/src/plugins/igmp/cli.c @@ -166,19 +166,21 @@ igmp_show_command_fn (vlib_main_t * vm, unformat_input_t * input, igmp_main_t *im = &igmp_main; vnet_main_t *vnm = vnet_get_main (); igmp_config_t *config; - igmp_sg_t *sg; + igmp_group_t *group; + igmp_src_t *src; /* *INDENT-OFF* */ pool_foreach (config, im->configs, ( { vlib_cli_output (vm, "interface: %U", format_vnet_sw_if_index_name, vnm, config->sw_if_index); - pool_foreach (sg, config->sg, ( + pool_foreach (group, config->groups, ( { - vlib_cli_output (vm, "\t(S,G): %U:%U:%U", format_ip46_address, - &sg->saddr, ip46_address_is_ip4 (&sg->saddr), - format_ip46_address, &sg->gaddr, ip46_address_is_ip4 - (&sg->gaddr), format_igmp_report_type, sg->group_type); + vlib_cli_output (vm, "\t%U:%U", format_igmp_report_type, group->type, format_ip46_address, &group->addr, ip46_address_is_ip4 (&group->addr)); + pool_foreach (src, group->srcs, ( + { + vlib_cli_output (vm, "\t\t%U", format_ip46_address, &src->addr, ip46_address_is_ip4 (&src->addr)); + })); })); })); /* *INDENT-ON* */ diff --git a/src/plugins/igmp/igmp.c b/src/plugins/igmp/igmp.c index 55a5d0e0a87..5ff88e58ec3 100644 --- a/src/plugins/igmp/igmp.c +++ b/src/plugins/igmp/igmp.c @@ -33,28 +33,57 @@ igmp_main_t igmp_main; -/* clear all (S,G)s on specified config and remove this config from pool */ +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_sg_t *sg; + igmp_group_t *group; ASSERT (config); /* *INDENT-OFF* */ - pool_foreach (sg, config->sg, ( + pool_foreach (group, config->groups, ( { - clib_mem_free (sg->key); + igmp_clear_group (config, group); })); /* *INDENT-ON* */ - pool_free (config->sg); - hash_free (config->igmp_sg_by_key); + pool_free (config->groups); + hash_free (config->igmp_group_by_key); hash_unset_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index); pool_put (im->configs, config); } -/* sort igmp timers, so that the first to expire is at end */ +/** \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) { @@ -75,13 +104,6 @@ igmp_sort_timers (igmp_timer_t * timers) IGMP_PROCESS_EVENT_UPDATE_TIMER, 0); } -/* create new per interface timer - * - * - delayed reports - * - query msg - * - query resp - */ - void igmp_create_int_timer (f64 time, u32 sw_if_index, igmp_timer_function_t * func) @@ -99,8 +121,30 @@ igmp_create_int_timer (f64 time, u32 sw_if_index, } void -igmp_create_sg_timer (f64 time, u32 sw_if_index, igmp_sg_key_t * key, - igmp_timer_function_t * func) +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; @@ -110,14 +154,22 @@ igmp_create_sg_timer (f64 time, u32 sw_if_index, igmp_sg_key_t * key, timer->func = func; timer->exp_time = time; timer->sw_if_index = sw_if_index; - /* duplicate key, to prevent segmentation fault if (S,G) is removed */ - timer->data = clib_mem_alloc (sizeof (igmp_sg_key_t)); - clib_memcpy (timer->data, key, sizeof (igmp_sg_key_t)); + + 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); } -/* get next timer to expire */ +/** \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) { @@ -153,126 +205,85 @@ igmp_create_report_v2 (vlib_buffer_t * b, igmp_config_t * config) } */ -/* create IGMPv3 report with single (S,G) - * used to send state chenge reports +/* TODO: divide (S,G)s to multiple reports... + * - create report limited by ? + * - save loop state + * - on next timer continue loop + * - case of new query -> reset loop */ + +/** \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_v31 (vlib_buffer_t * b, igmp_config_t * config) +igmp_create_report_v3 (vlib_buffer_t * b, igmp_config_t * config, + igmp_group_t * group) { ip_csum_t sum; u16 csum; - igmp_main_t *im = &igmp_main; - igmp_sg_t *sg; u32 len = 0; + int i; - sg = vec_elt_at_index (config->sg, im->next_index.sg_index); + 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_host_to_net_u16 (1); + igmp->n_groups = + clib_net_to_host_u16 ((group) ? 1 : pool_elts (config->groups)); - len += sizeof (igmp_membership_group_v3_t); - memset (igmp->groups, 0, sizeof (igmp_membership_group_v3_t)); - igmp->groups[0].type = sg->group_type; - igmp->groups[0].n_aux_u32s = 0; - clib_memcpy (&igmp->groups[0].dst_address, &sg->gaddr.ip4, - sizeof (ip4_address_t)); - - igmp->groups[0].n_src_addresses = clib_host_to_net_u16 (1); - - len += sizeof (ip4_address_t); - clib_memcpy (&igmp->groups[0].src_addresses[0], &sg->saddr.ip4, - sizeof (ip4_address_t)); - - sum = ip_incremental_checksum (0, igmp, len); - csum = ~ip_csum_fold (sum); - igmp->header.checksum = csum; + /* get pointer to first group */ + igmp_group = igmp->groups; - b->current_data += len; - b->current_length += len; -} - -u8 -ip4_lookup (ip4_address_t * a, igmp_membership_report_v3_t * igmp, u16 n, - igmp_membership_group_v3_type_t type) -{ - u16 i; - u8 rv = 0; - u32 l = sizeof (igmp_membership_report_v3_t); - - for (i = 0; i < n; i++) + /* if group is not NULL, send the specified group */ + if (group) { - if ((!ip4_address_compare (a, &(group_ptr (igmp, l)->dst_address))) && - (type == group_ptr (igmp, l)->type)) + 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, ( { - rv = 1; - break; - } - l += sizeof (igmp_membership_group_v3_t) + - clib_net_to_host_u16 (group_ptr (igmp, l)->n_src_addresses) * - sizeof (ip4_address_t); + igmp_group->src_addresses[i++] = src->addr.ip4; + })); + /* *INDENT-ON* */ + len += sizeof (ip4_address_t) * i; + len += sizeof (igmp_membership_group_v3_t); } - - return rv; -} - -/* create IGMPv3 report with all (S,G)s on config - * used to respond to general queries - */ -static void -igmp_create_report_v32 (vlib_buffer_t * b, igmp_config_t * config) -{ - ip_csum_t sum; - u16 csum; - igmp_sg_t *sg0, *sg1; - u32 len = 0; - u16 n_groups = 0, n_srcs = 0; - u32 grp_s = sizeof (igmp_membership_group_v3_t); - - 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; - -/* TODO: divide (S,G)s to multiple reports... - * - create report limited by ? - * - save loop state - * - on next timer continue loop - * - case of new query -> reset loop - */ - /* *INDENT-OFF* */ - pool_foreach (sg0, config->sg, ( + else { - if (ip4_lookup (&sg0->gaddr.ip4, igmp, n_groups, sg0->group_type)) - continue; - memset (igmp + len, 0, grp_s); - clib_memcpy (&group_ptr (igmp, len)->dst_address, &sg0->gaddr.ip4, sizeof (ip4_address_t)); - group_ptr (igmp, len)->type = sg0->group_type; - len += grp_s; - n_srcs = 0; - pool_foreach (sg1, config->sg, ( + /* *INDENT-OFF* */ + pool_foreach (group, config->groups, ( { - if ((!ip4_address_compare (&group_ptr (igmp, len - grp_s)->dst_address, - &sg1->gaddr.ip4)) && (group_ptr (igmp, len - grp_s)->type == (sg1->group_type))) + 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, ( { - clib_memcpy (group_ptr (igmp, len + sizeof (ip4_address_t) * n_srcs), - &sg1->saddr.ip4, sizeof (ip4_address_t)); - n_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); })); - group_ptr (igmp, len - grp_s)->n_src_addresses = clib_host_to_net_u16 (n_srcs); - len += sizeof (ip4_address_t) * n_srcs; - n_groups++; - })); - /* *INDENT-ON* */ - - igmp->n_groups = clib_host_to_net_u16 (n_groups); + /* *INDENT-ON* */ + } sum = ip_incremental_checksum (0, igmp, len); csum = ~ip_csum_fold (sum); @@ -282,14 +293,25 @@ igmp_create_report_v32 (vlib_buffer_t * b, igmp_config_t * config) 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_general_query_v3 (vlib_buffer_t * b, igmp_config_t * config) +igmp_create_query_v3 (vlib_buffer_t * b, igmp_config_t * config, + igmp_group_t * group) { vlib_main_t *vm = vlib_get_main (); ip_csum_t sum; u16 csum; - igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b)); + 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->header.type = IGMP_TYPE_membership_query; @@ -299,6 +321,9 @@ igmp_create_general_query_v3 (vlib_buffer_t * b, igmp_config_t * config) igmp_create_int_timer (vlib_time_now (vm) + (f64) (igmp->header.code / 10), config->sw_if_index, igmp_query_resp_exp); + if (PREDICT_FALSE (group != NULL)) + clib_memcpy (&igmp->dst, &group->addr.ip4, sizeof (ip4_address_t)); + sum = ip_incremental_checksum (0, igmp, sizeof (igmp_membership_query_v3_t)); csum = ~ip_csum_fold (sum); @@ -308,9 +333,17 @@ igmp_create_general_query_v3 (vlib_buffer_t * b, igmp_config_t * config) b->current_length += sizeof (igmp_membership_query_v3_t); } +/** \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, u8 is_report) +igmp_create_ip4 (vlib_buffer_t * b, igmp_config_t * config, + igmp_group_t * group, u8 is_report) { ip_lookup_main_t *lm = &ip4_main.lookup_main; @@ -330,23 +363,44 @@ igmp_create_ip4 (vlib_buffer_t * b, igmp_config_t * config, u8 is_report) ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add); clib_memcpy (&ip4->src_address, if_ip, sizeof (ip4_address_t)); } - ip4->dst_address.as_u8[0] = 224; - ip4->dst_address.as_u8[1] = 0; - ip4->dst_address.as_u8[2] = 0; - ip4->dst_address.as_u8[3] = is_report ? 22 : 1; + + 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); - config->next_create_msg (b, config); + config->next_create_msg (b, config, group); ip4->length = clib_host_to_net_u16 (b->current_length); ip4->checksum = ip4_header_checksum (ip4); } + +/** \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, u8 is_report) + igmp_main_t * im, igmp_config_t * config, igmp_group_t * group, + u8 is_report) { u32 thread_index = vlib_get_thread_index (); u32 *to_next; @@ -388,7 +442,7 @@ igmp_send_msg (vlib_main_t * vm, vlib_node_runtime_t * node, b->current_data = 0; b->current_length = 0; - igmp_create_ip4 (b, config, is_report); + igmp_create_ip4 (b, config, group, is_report); b->current_data = 0; @@ -414,6 +468,8 @@ 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; @@ -424,11 +480,13 @@ igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, return; /* TODO: implement IGMPv2 */ - config->next_create_msg = igmp_create_general_query_v3; - igmp_send_msg (vm, rt, im, config, /* is_report */ 0); + config->next_create_msg = igmp_create_query_v3; + igmp_send_msg (vm, rt, im, config, group, /* is_report */ 0); - igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER, sw_if_index, - igmp_send_query); + /* 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 @@ -436,6 +494,8 @@ 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; @@ -444,7 +504,17 @@ igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, config = igmp_config_lookup (im, sw_if_index); if (!config) return; - /* if report not reveived in max resp time clear igmp on interface */ + + /* 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); @@ -468,8 +538,9 @@ igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt, if (config->flags & IGMP_CONFIG_FLAG_CAN_SEND_REPORT) { /* TODO: implement IGMPv2 and IGMPv1 */ - config->next_create_msg = igmp_create_report_v32; - igmp_send_msg (vm, rt, im, config, /* is_report */ 1); + 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; } @@ -480,68 +551,144 @@ igmp_send_state_changed (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, igmp_timer_t * timer) { igmp_config_t *config; - igmp_sg_t *sg; + 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); - config = vec_elt_at_index (im->configs, im->next_index.config_index); - sg = vec_elt_at_index (config->sg, im->next_index.sg_index); + config = igmp_config_lookup (im, sw_if_index); + if (!config) + return; - config->next_create_msg = igmp_create_report_v31; - igmp_send_msg (vm, rt, im, config, /* is_report */ 1); + 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); - if (sg->group_type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include) - { - sg->group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include; - } - else if (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources) + IGMP_DBG ("group_type %u", group->type); + + if (group->type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include) { - /* remove API/CLI configured (S,G) */ - hash_unset_mem (config->igmp_sg_by_key, sg->key); - clib_mem_free (sg->key); - pool_put (config->sg, sg); - if (pool_elts (config->sg) == 0) + 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) { - hash_unset_mem (im->igmp_config_by_sw_if_index, - &config->sw_if_index); - pool_put (im->configs, config); + 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* */ } + /* remove group */ + IGMP_DBG ("remove group"); + igmp_clear_group (config, group); + if (pool_elts (config->groups) == 0) + { + hash_unset_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index); + pool_put (im->configs, config); + } } void -igmp_sg_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, - igmp_timer_t * timer) +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_sg_t *sg; + igmp_group_t *group; + igmp_src_t *src; + + ASSERT (timer->data); - igmp_sg_key_t *key = (igmp_sg_key_t *) timer->data; + 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; - sg = igmp_sg_lookup (config, key); - if (!sg) + 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 != sg->exp_time) + if (timer->exp_time != src->exp_time) { - timer->exp_time = sg->exp_time; + timer->exp_time = src->exp_time; igmp_sort_timers (im->timers); return; } - /* source timer expired, remove (S,G) */ - igmp_listen (vm, 0, timer->sw_if_index, key->saddr, key->gaddr, 0); + 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) @@ -550,29 +697,23 @@ igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, 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 (vm, rt, im, timer); - next_timer: timer = igmp_get_next_timer (im); } @@ -600,97 +741,184 @@ igmp_listen (vlib_main_t * vm, u8 enable, u32 sw_if_index, { igmp_main_t *im = &igmp_main; igmp_config_t *config; - igmp_sg_t *sg; - igmp_sg_key_t key; + igmp_group_t *group; + igmp_src_t *src; + igmp_key_t skey; + igmp_key_t gkey; + + igmp_membership_group_v3_type_t group_type = + (cli_api_configured) ? + IGMP_MEMBERSHIP_GROUP_change_to_filter_include : + IGMP_MEMBERSHIP_GROUP_mode_is_filter_include; int rv = 0; - /* set the lookup key */ - clib_memcpy (&key.saddr, &saddr, sizeof (ip46_address_t)); - clib_memcpy (&key.gaddr, &gaddr, sizeof (ip46_address_t)); + /* 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) { + /* 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_sg_by_key = - hash_create_mem (0, sizeof (igmp_sg_key_t), sizeof (uword)); + config->igmp_group_by_key = + hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword)); config->cli_api_configured = cli_api_configured; /* use IGMPv3 by default */ config->igmp_ver = IGMP_V3; config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED; + if (!cli_api_configured) { + /* 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_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index, - config - im->configs); + hash_set_mem (im->igmp_config_by_sw_if_index, + &config->sw_if_index, config - im->configs); } else if (config->cli_api_configured != cli_api_configured) { rv = -2; goto error; } - sg = igmp_sg_lookup (config, &key); - if (!sg) + /* find igmp group, if it dosn't exist, create new */ + group = igmp_group_lookup (config, &gkey); + if (!group) { - pool_get (config->sg, sg); - memset (sg, 0, sizeof (igmp_sg_t)); - sg->key = clib_mem_alloc (sizeof (igmp_sg_key_t)); - clib_memcpy (sg->key, &key, sizeof (igmp_sg_key_t)); - clib_memcpy (&sg->saddr, &saddr, sizeof (ip46_address_t)); - clib_memcpy (&sg->gaddr, &gaddr, sizeof (ip46_address_t)); - sg->group_type = IGMP_MEMBERSHIP_GROUP_change_to_filter_include; + 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 (cli_api_configured) { /* create state-changed report timer with zero timeout */ - igmp_create_int_timer (0, sw_if_index, igmp_send_state_changed); + igmp_create_group_timer (0, sw_if_index, group->key, + igmp_send_state_changed); } - else + + 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 (!cli_api_configured) { - sg->group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include; - sg->exp_time = vlib_time_now (vm) + IGMP_SG_TIMER; - igmp_create_sg_timer (sg->exp_time, config->sw_if_index, - sg->key, igmp_sg_exp); - /* notify all registered api clients */ - igmp_event (im, config, sg); + /* 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); } - hash_set_mem (config->igmp_sg_by_key, sg->key, sg - config->sg); + + hash_set_mem (group->igmp_src_by_key, src->key, src - group->srcs); } else { rv = -1; goto error; } - - im->next_index.config_index = config - im->configs; - im->next_index.sg_index = sg - config->sg; } else { config = igmp_config_lookup (im, sw_if_index); if (config) { - sg = igmp_sg_lookup (config, &key); - if (sg) + gkey.group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include; + group = igmp_group_lookup (config, &gkey); + if (group) { - sg->group_type = IGMP_MEMBERSHIP_GROUP_block_old_sources; - im->next_index.config_index = config - im->configs; - im->next_index.sg_index = sg - config->sg; - /* notify all registered api clients */ - if (!cli_api_configured) - igmp_event (im, config, sg); + 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; + + 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); + + group = igmp_group_lookup (config, &gkey); + + 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); + } + + /* notify all registered api clients */ + if (!cli_api_configured) + 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 - igmp_create_int_timer (0, sw_if_index, - igmp_send_state_changed); + { + rv = -1; + goto error; + } } else { @@ -709,13 +937,19 @@ error: return rv; } +/** \brief igmp hardware interface link up down + @param vnm - vnet main + @param hw_if_index - interface hw_if_index + @param flags - hw interface flags + + 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) { 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, @@ -731,6 +965,11 @@ igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down); +/** \brief igmp initialization + @param vm - vlib main + + initialize igmp plugin. Initialize igmp_main, set mfib to allow igmp traffic. +*/ static clib_error_t * igmp_init (vlib_main_t * vm) { @@ -738,20 +977,15 @@ igmp_init (vlib_main_t * vm) igmp_main_t *im = &igmp_main; vlib_thread_main_t *tm = vlib_get_thread_main (); int i; - if ((error = vlib_call_init_function (vm, ip4_lookup_init))) return error; - im->igmp_config_by_sw_if_index = hash_create_mem (0, sizeof (u32), sizeof (uword)); im->igmp_api_client_by_client_index = hash_create_mem (0, sizeof (u32), sizeof (uword)); - vec_validate_aligned (im->buffers, tm->n_vlib_mains - 1, CLIB_CACHE_LINE_BYTES); - 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) \ @@ -760,19 +994,15 @@ do { \ 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; @@ -786,17 +1016,18 @@ do { \ } /* General Query address */ - ip46_address_t addr0; - addr0.ip4.as_u8[0] = 224; - addr0.ip4.as_u8[1] = 0; - addr0.ip4.as_u8[2] = 0; - addr0.ip4.as_u8[3] = 1; + 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; - addr1.ip4.as_u8[0] = 224; - addr1.ip4.as_u8[1] = 0; - addr1.ip4.as_u8[2] = 0; - addr1.ip4.as_u8[3] = 22; + 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), @@ -806,32 +1037,36 @@ do { \ .frp_weight = 0, .frp_flags = FIB_ROUTE_PATH_LOCAL, }; + const mfib_prefix_t mpfx0 = { .fp_proto = FIB_PROTOCOL_IP4, .fp_len = 32, .fp_grp_addr = addr0, }; + const mfib_prefix_t mpfx1 = { .fp_proto = FIB_PROTOCOL_IP4, .fp_len = 32, .fp_grp_addr = addr1, }; - /* configure MFIB to accept IGMPv3 general query and reports from all interfaces */ - mfib_table_entry_path_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE, &path, + + /* 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_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); - + 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, diff --git a/src/plugins/igmp/igmp.h b/src/plugins/igmp/igmp.h index f5fb958fcad..932d02947e8 100644 --- a/src/plugins/igmp/igmp.h +++ b/src/plugins/igmp/igmp.h @@ -25,10 +25,10 @@ #include #define IGMP_QUERY_TIMER (60) -#define IGMP_SG_TIMER (3 * IGMP_QUERY_TIMER) +#define IGMP_SRC_TIMER (3 * IGMP_QUERY_TIMER) #define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2) -#define ENABLE_IGMP_DBG 0 +#define ENABLE_IGMP_DBG 1 #if ENABLE_IGMP_DBG == 1 #define IGMP_DBG(...) clib_warning(__VA_ARGS__) @@ -36,6 +36,12 @@ #define IGMP_DBG(...) #endif /* ENABLE_IGMP_DBG */ +/** General Query address - 224.0.0.1 */ +#define IGMP_GENERAL_QUERY_ADDRESS (0xE0000001) +/** Membership Report address - 224.0.0.22 */ +#define IGMP_MEMBERSHIP_REPORT_ADDRESS (0xE0000016) + +/** helper macro to get igmp mebership group from pointer plus offset */ #define group_ptr(p, l) ((igmp_membership_group_v3_t *)((char*)p + l)) enum @@ -43,6 +49,7 @@ enum IGMP_PROCESS_EVENT_UPDATE_TIMER = 1, } igmp_process_event_t; +/*! Igmp versions */ typedef enum { IGMP_V1, @@ -54,34 +61,85 @@ struct igmp_config_t_; typedef struct igmp_config_t_ igmp_config_t; -/* populate supplied bufefr with IGMP message */ -typedef void (create_msg_t) (vlib_buffer_t * b, igmp_config_t * config); +struct igmp_group_t_; -typedef struct igmp_index_t_ -{ - u32 config_index; - u32 sg_index; -} igmp_index_t; +typedef struct igmp_group_t_ igmp_group_t; -typedef struct igmp_sg_key_t_ -{ - ip46_address_t gaddr; - ip46_address_t saddr; -} igmp_sg_key_t; +/** \brief create message + @param b - vlib buffer + @param config - igmp configuration + @param group - igmp group -typedef struct igmp_sg_t_ + Populate supplied bufefr with IGMP message. +*/ +typedef void (create_msg_t) (vlib_buffer_t * b, igmp_config_t * config, + igmp_group_t * group); + +/** \brief igmp key + @param data - key data + @param group_type - membership group type +*/ +typedef struct { - ip46_address_t gaddr; - ip46_address_t saddr; + u64 data[2]; /*!< ip46_address_t.as_u64 */ + u64 group_type; /*!< zero in case of source key */ +} igmp_key_t; + +/** \brief igmp source + @param addr - ip4/6 source address + @param exp_time - expiration time + @param key - pointer to key +*/ +typedef struct +{ + ip46_address_t addr; - igmp_membership_group_v3_type_t group_type; + f64 exp_time; + + igmp_key_t *key; +} igmp_src_t; + +/** \brief igmp group + @param addr - ip4/6 group address + @param exp_time - expiration time + @param key - pointer to key + @param type - membership group type + @param n_srcs - number of sources + @param flags - igmp group flags + @param igmp_src_by_key - source by key hash + @param srcs - pool of sources +*/ +typedef struct igmp_group_t_ +{ + ip46_address_t addr; - /* check if expired (S,G) timer is valid */ f64 exp_time; - igmp_sg_key_t *key; -} igmp_sg_t; + igmp_key_t *key; + igmp_membership_group_v3_type_t type; + + u16 n_srcs; + + u8 flags; +/** reponse to query was received */ +#define IGMP_GROUP_FLAG_QUERY_RESP_RECVED (1 << 0) + + uword *igmp_src_by_key; + igmp_src_t *srcs; +} igmp_group_t; + +/** \brief igmp configuration + @param sw_if_index - interface sw_if_index + @param adj_index - adjacency index + @param cli_api_configured - if zero, an igmp report was received + @param next_create_msg - specify next igmp message + @param igmp_ver - igmp version + @param robustness_var - robustness variable + @param flags - igmp configuration falgs + @param igmp_group_by_key - group by key hash + @param groups - pool of groups +*/ typedef struct igmp_config_t_ { u32 sw_if_index; @@ -100,16 +158,19 @@ typedef struct igmp_config_t_ #define IGMP_CONFIG_FLAG_QUERY_RESP_RECVED (1 << 0) #define IGMP_CONFIG_FLAG_CAN_SEND_REPORT (1 << 1) - uword *igmp_sg_by_key; + uword *igmp_group_by_key; - /* pool of (S,G)s per interface */ - igmp_sg_t *sg; + igmp_group_t *groups; } igmp_config_t; struct igmp_timer_t_; typedef struct igmp_timer_t_ igmp_timer_t; +/** \brief igmp api client + @param client_index - client index + @param pid - pid +*/ typedef struct igmp_api_client_t_ { u32 client_index; @@ -128,30 +189,35 @@ typedef struct igmp_membership_group_v3_type_t type; } igmp_report_type_info_t; +/** \brief igmp main + @param msg_id_base - API message ID base + @param igmp_api_client_by_client_index - get api client by client_index + @param api_clients - pool of api clients registered for join/leave notifications + @param igmp_config_by_sw_if_index - get config index by config key + @param configs - pool of igmp configurations + @param buffers - buffer cache + @param timers - pool of igmp timers + @param type_infos - igmp type info + @param report_type_infos - igmp report type info + @param type_info_by_type - + @param report_type_info_by_report_type - + @param general_query_address - 224.0.0.1 + @param membership_report_address - 224.0.0.22 +*/ typedef struct igmp_main_t_ { - /** API message ID base */ u16 msg_id_base; - /* get api client by client_index */ uword *igmp_api_client_by_client_index; - /** pool of api clients registered for join/leave notifications */ igmp_api_client_t *api_clients; - /* get config index by config key */ uword *igmp_config_by_sw_if_index; - /** pool of igmp configurations */ igmp_config_t *configs; - /** buffer cache */ u32 **buffers; - /* next report/deletion */ - igmp_index_t next_index; - - /** pool of igmp timers */ igmp_timer_t *timers; igmp_type_info_t *type_infos; @@ -159,15 +225,26 @@ typedef struct igmp_main_t_ uword *type_info_by_type; uword *report_type_info_by_report_type; - } igmp_main_t; extern igmp_main_t igmp_main; +/** \brief igmp timer function + @param vm - vlib main + @param rt - vlib runtime node + @param im - igmp main + @param timer - igmp timer +*/ typedef void (igmp_timer_function_t) (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, igmp_timer_t * timer); +/** \brief igmp timer + @param exp_time - expiration time + @param func - function to call on timer expiration + @param sw_if_index - interface sw_if_index + @param data - custom data +*/ typedef struct igmp_timer_t_ { f64 exp_time; @@ -182,29 +259,114 @@ extern vlib_node_registration_t igmp_input_node; extern vlib_node_registration_t igmp_parse_query_node; extern vlib_node_registration_t igmp_parse_report_node; +/** \brief igmp listen + @param vm - vlib main + @param enable - 0 == remove (S,G), else add (S,G) + @param sw_if_index - interface sw_if_index + @param saddr - source address + @param gaddr - group address + @param cli_api_configured - if zero, an igmp report has been received on interface + + Add/del (S,G) on an interface. If user configured, + send a status change report from the interface. + If a report was received on interface notify registered api clients. +*/ int igmp_listen (vlib_main_t * vm, u8 enable, u32 sw_if_index, ip46_address_t saddr, ip46_address_t gaddr, u8 cli_api_configured); +/** \brief igmp clear config + @param config - igmp configuration + + Clear all (S,G)s on specified config and remove this config from pool. +*/ void igmp_clear_config (igmp_config_t * config); +/** \brief igmp clear group + @param config - igmp configuration + @param group - the group to be removed + + Remove this group from interface (specified by configuration). +*/ +void igmp_clear_group (igmp_config_t * config, igmp_group_t * group); + +/** \brief igmp sort timers + @param timers - pool of igmp timers + + Sort igmp timers, so that the first to expire is at end. +*/ void igmp_sort_timers (igmp_timer_t * timers); +/** \brief igmp create int timer + @param time - expiration time (at this time the timer will expire) + @param sw_if_index - interface sw_if_index + @param func - function to all after timer expiration + + + Creates new interface timer. Delayed reports, query msg, query resp. +*/ void igmp_create_int_timer (f64 time, u32 sw_if_index, igmp_timer_function_t * func); -void igmp_create_sg_timer (f64 time, u32 sw_if_index, igmp_sg_key_t * key, - igmp_timer_function_t * func); +/** \brief igmp create group timer + @param time - expiration time (at this time the timer will expire) + @param sw_if_index - interface sw_if_index + @param gkey - key to find the group by + @param func - function to all after timer expiration + + Creates new group timer. +*/ +void igmp_create_group_timer (f64 time, u32 sw_if_index, igmp_key_t * gkey, + igmp_timer_function_t * func); + +/** \brief igmp create group timer + @param time - expiration time (at this time the timer will expire) + @param sw_if_index - interface sw_if_index + @param gkey - key to find the group by + @param skey - key to find the source by + @param func - function to all after timer expiration + + Creates new source timer. +*/ +void igmp_create_src_timer (f64 time, u32 sw_if_index, igmp_key_t * gkey, + igmp_key_t * skey, igmp_timer_function_t * func); + +/** \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. +*/ void igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, igmp_timer_t * timer); + +/** \brief igmp query response expiration (igmp_timer_function_t) + + If a response to a query didn't come in time, remove (S,G)s. +*/ void igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, igmp_timer_t * timer); + +/** \brief igmp send report (igmp_timer_function_t) + + Send igmp membership report. +*/ void igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, igmp_timer_t * timer); + +/** \brief igmp send state changed (igmp_timer_function_t) + + Send report if an (S,G) filter has changed. +*/ void igmp_send_state_changed (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, igmp_timer_t * timer); -void igmp_sg_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, - igmp_main_t * im, igmp_timer_t * timer); + +/** \brief igmp source expiration (igmp_timer_function_t) + + Remove expired (S,G) from group. +*/ +void igmp_src_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, + igmp_main_t * im, igmp_timer_t * timer); static inline igmp_type_info_t * igmp_get_type_info (igmp_main_t * im, u32 type) @@ -224,7 +386,16 @@ igmp_get_report_type_info (igmp_main_t * im, u8 report_type) return p ? vec_elt_at_index (im->report_type_infos, p[0]) : 0; } -void igmp_event (igmp_main_t * im, igmp_config_t * config, igmp_sg_t * sg); +/** \brief igmp event + @param im - igmp main + @param config - igmp configuration + @param group - igmp group + @param src - source + + Notify registered api clients of (S,G) filter update. +*/ +void igmp_event (igmp_main_t * im, igmp_config_t * config, + igmp_group_t * group, igmp_src_t * src); typedef enum { @@ -233,7 +404,10 @@ typedef enum IGMP_N_NEXT, } igmp_next_t; - +/** \brief igmp config lookup + @param im - igmp main + @param sw_if_index - interface sw_if_index +*/ always_inline igmp_config_t * igmp_config_lookup (igmp_main_t * im, u32 sw_if_index) { @@ -247,21 +421,48 @@ igmp_config_lookup (igmp_main_t * im, u32 sw_if_index) return config; } -always_inline igmp_sg_t * -igmp_sg_lookup (igmp_config_t * config, igmp_sg_key_t * key) +/** \brief igmp group lookup + @param config - igmp configuration + @param key - igmp key +*/ +always_inline igmp_group_t * +igmp_group_lookup (igmp_config_t * config, igmp_key_t * key) { uword *p; - igmp_sg_t *sg = NULL; + igmp_group_t *group = NULL; if (!config) return NULL; - p = hash_get_mem (config->igmp_sg_by_key, key); + p = hash_get_mem (config->igmp_group_by_key, key); + if (p) + group = vec_elt_at_index (config->groups, p[0]); + + return group; +} + +/** \brief igmp group lookup + @param group - igmp group + @param key - igmp key +*/ +always_inline igmp_src_t * +igmp_src_lookup (igmp_group_t * group, igmp_key_t * key) +{ + uword *p; + igmp_src_t *src = NULL; + if (!group) + return NULL; + + p = hash_get_mem (group->igmp_src_by_key, key); if (p) - sg = vec_elt_at_index (config->sg, p[0]); + src = vec_elt_at_index (group->srcs, p[0]); - return sg; + return src; } +/** \brief igmp group lookup + @param im - igmp main + @param client_index - client index +*/ always_inline igmp_api_client_t * igmp_api_client_lookup (igmp_main_t * im, u32 client_index) { diff --git a/src/plugins/igmp/igmp_api.c b/src/plugins/igmp/igmp_api.c index 256f9245e35..7044e7f8c76 100644 --- a/src/plugins/igmp/igmp_api.c +++ b/src/plugins/igmp/igmp_api.c @@ -107,7 +107,8 @@ vl_api_igmp_enable_disable_t_handler (vl_api_igmp_enable_disable_t * mp) static void send_igmp_details (unix_shared_memory_queue_t * q, igmp_main_t * im, - igmp_config_t * config, igmp_sg_t * sg, u32 context) + igmp_config_t * config, igmp_group_t * group, + igmp_src_t * src, u32 context) { vl_api_igmp_details_t *mp; @@ -117,8 +118,8 @@ send_igmp_details (unix_shared_memory_queue_t * q, igmp_main_t * im, mp->_vl_msg_id = htons (VL_API_IGMP_DETAILS + im->msg_id_base); mp->context = context; mp->sw_if_index = htonl (config->sw_if_index); - clib_memcpy (mp->saddr, &sg->saddr.ip4, sizeof (u8) * 4); - clib_memcpy (mp->gaddr, &sg->gaddr.ip4, sizeof (u8) * 4); + clib_memcpy (mp->saddr, &src->addr.ip4, sizeof (u8) * 4); + clib_memcpy (mp->gaddr, &group->addr.ip4, sizeof (u8) * 4); vl_msg_api_send_shmem (q, (u8 *) & mp); } @@ -128,7 +129,8 @@ vl_api_igmp_dump_t_handler (vl_api_igmp_dump_t * mp) { igmp_main_t *im = &igmp_main; igmp_config_t *config; - igmp_sg_t *sg; + igmp_group_t *group; + igmp_src_t *src; unix_shared_memory_queue_t *q = vl_api_client_index_to_input_queue (mp->client_index); @@ -140,9 +142,12 @@ vl_api_igmp_dump_t_handler (vl_api_igmp_dump_t * mp) /* *INDENT-OFF* */ pool_foreach (config, im->configs, ( { - pool_foreach (sg, config->sg, ( + pool_foreach (group, config->groups, ( { - send_igmp_details (q, im, config, sg, mp->context); + pool_foreach (src, group->srcs, ( + { + send_igmp_details (q, im, config, group, src, mp->context); + })); })); })); /* *INDENT-ON* */ @@ -152,9 +157,12 @@ vl_api_igmp_dump_t_handler (vl_api_igmp_dump_t * mp) if (config) { /* *INDENT-OFF* */ - pool_foreach (sg, config->sg, ( + pool_foreach (group, config->groups, ( { - send_igmp_details (q, im, config, sg, mp->context); + pool_foreach (src, group->srcs, ( + { + send_igmp_details (q, im, config, group, src, mp->context); + })); })); /* *INDENT-ON* */ } @@ -235,7 +243,8 @@ done:; void send_igmp_event (unix_shared_memory_queue_t * q, u32 context, - igmp_main_t * im, igmp_config_t * config, igmp_sg_t * sg) + igmp_main_t * im, igmp_config_t * config, + igmp_group_t * group, igmp_src_t * src) { vl_api_igmp_event_t *mp = vl_msg_api_alloc (sizeof (*mp)); memset (mp, 0, sizeof (*mp)); @@ -243,16 +252,17 @@ send_igmp_event (unix_shared_memory_queue_t * q, u32 context, mp->_vl_msg_id = ntohs ((VL_API_IGMP_EVENT) + im->msg_id_base); mp->context = context; mp->sw_if_index = htonl (config->sw_if_index); - clib_memcpy (&mp->saddr, &sg->saddr.ip4, sizeof (ip4_address_t)); - clib_memcpy (&mp->gaddr, &sg->gaddr.ip4, sizeof (ip4_address_t)); + clib_memcpy (&mp->saddr, &src->addr.ip4, sizeof (ip4_address_t)); + clib_memcpy (&mp->gaddr, &group->addr.ip4, sizeof (ip4_address_t)); mp->is_join = - (sg->group_type == IGMP_MEMBERSHIP_GROUP_mode_is_filter_include) ? 1 : 0; + (group->type == IGMP_MEMBERSHIP_GROUP_mode_is_filter_include) ? 1 : 0; vl_msg_api_send_shmem (q, (u8 *) & mp); } void -igmp_event (igmp_main_t * im, igmp_config_t * config, igmp_sg_t * sg) +igmp_event (igmp_main_t * im, igmp_config_t * config, igmp_group_t * group, + igmp_src_t * src) { igmp_api_client_t *api_client; unix_shared_memory_queue_t *q; @@ -261,15 +271,13 @@ igmp_event (igmp_main_t * im, igmp_config_t * config, igmp_sg_t * sg) ({ q = vl_api_client_index_to_input_queue (api_client->client_index); if (q) - send_igmp_event (q, 0, im, config, sg); + send_igmp_event (q, 0, im, config, group, src); })); /* *INDENT-ON* */ - if (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources) + if (group->type == IGMP_MEMBERSHIP_GROUP_block_old_sources) { - hash_unset_mem (config->igmp_sg_by_key, sg->key); - clib_mem_free (sg->key); - pool_put (config->sg, sg); - if (pool_elts (config->sg) == 0) + igmp_clear_group (config, group); + if (pool_elts (config->groups) == 0) { hash_unset_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index); diff --git a/src/plugins/igmp/input.c b/src/plugins/igmp/input.c index ec0ae36c5a6..4322f50b70c 100644 --- a/src/plugins/igmp/input.c +++ b/src/plugins/igmp/input.c @@ -354,11 +354,14 @@ igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node, u32 n_left_from, *from, *to_next; igmp_input_next_t next_index; igmp_config_t *config; - igmp_sg_t *sg; - igmp_membership_group_v3_t *group; - ip4_address_t *src; - igmp_sg_key_t key; - memset (&key, 0, sizeof (igmp_sg_key_t)); + igmp_group_t *group; + igmp_src_t *src; + igmp_membership_group_v3_t *igmp_group; + ip4_address_t *src_addr; + igmp_key_t gkey; + igmp_key_t skey; + memset (&gkey, 0, sizeof (igmp_key_t)); + memset (&skey, 0, sizeof (igmp_key_t)); ip46_address_t saddr; memset (&saddr, 0, sizeof (ip46_address_t)); ip46_address_t gaddr; @@ -420,77 +423,98 @@ igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node, int i, j = 0; for (i = 0; i < clib_net_to_host_u16 (igmp->n_groups); i++) { - group = group_ptr (igmp, len); - src = group->src_addresses; - if (group->type == IGMP_MEMBERSHIP_GROUP_mode_is_filter_include) + igmp_group = group_ptr (igmp, len); + src_addr = igmp_group->src_addresses; + if (igmp_group->type == + IGMP_MEMBERSHIP_GROUP_mode_is_filter_include) { - for (j = 0; - j < clib_net_to_host_u16 (group->n_src_addresses); j++) + ip46_address_set_ip4 ((ip46_address_t *) & gkey.data, + &igmp_group->dst_address); + + gkey.group_type = + IGMP_MEMBERSHIP_GROUP_mode_is_filter_include; + + group = igmp_group_lookup (config, &gkey); + if (group) { - /* update (S,G) expiration timer */ - key.saddr.ip4 = *src; - key.gaddr.ip4 = group->dst_address; - sg = igmp_sg_lookup (config, &key); - if (sg) - sg->exp_time = vlib_time_now (vm) + IGMP_SG_TIMER; - src++; + for (j = 0; + j < + clib_net_to_host_u16 (igmp_group->n_src_addresses); + j++) + { + /* update (S,G) expiration timer */ + ip46_address_set_ip4 ((ip46_address_t *) & + skey.data, src_addr); + src = igmp_src_lookup (group, &skey); + if (src) + src->exp_time = + vlib_time_now (vm) + IGMP_SRC_TIMER; + src_addr++; + } } } - else if (group->type == + else if (igmp_group->type == IGMP_MEMBERSHIP_GROUP_mode_is_filter_exclude) { for (j = 0; - j < clib_net_to_host_u16 (group->n_src_addresses); j++) + j < clib_net_to_host_u16 (igmp_group->n_src_addresses); + j++) { /* nothing for now... */ - src++; + src_addr++; } } - else if (group->type == + else if (igmp_group->type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include) { for (j = 0; - j < clib_net_to_host_u16 (group->n_src_addresses); j++) + j < clib_net_to_host_u16 (igmp_group->n_src_addresses); + j++) { /* add new (S,G) to interface */ - saddr.ip4 = *src; - gaddr.ip4 = group->dst_address; + saddr.ip4 = *src_addr; + gaddr.ip4 = igmp_group->dst_address; igmp_listen (vm, 1, sw_if_index, saddr, gaddr, 0); - src++; + src_addr++; } } - else if (group->type == + else if (igmp_group->type == IGMP_MEMBERSHIP_GROUP_change_to_filter_exclude) { for (j = 0; - j < clib_net_to_host_u16 (group->n_src_addresses); j++) + j < clib_net_to_host_u16 (igmp_group->n_src_addresses); + j++) { /* remove (S,G) from interface */ - saddr.ip4 = *src; - gaddr.ip4 = group->dst_address; + saddr.ip4 = *src_addr; + gaddr.ip4 = igmp_group->dst_address; igmp_listen (vm, 0, sw_if_index, saddr, gaddr, 0); - src++; + src_addr++; } } - else if (group->type == IGMP_MEMBERSHIP_GROUP_allow_new_sources) + else if (igmp_group->type == + IGMP_MEMBERSHIP_GROUP_allow_new_sources) { for (j = 0; - j < clib_net_to_host_u16 (group->n_src_addresses); j++) + j < clib_net_to_host_u16 (igmp_group->n_src_addresses); + j++) { /* nothing for now... */ - src++; + src_addr++; } } - else if (group->type == IGMP_MEMBERSHIP_GROUP_block_old_sources) + else if (igmp_group->type == + IGMP_MEMBERSHIP_GROUP_block_old_sources) { for (j = 0; - j < clib_net_to_host_u16 (group->n_src_addresses); j++) + j < clib_net_to_host_u16 (igmp_group->n_src_addresses); + j++) { /* remove (S,G) from interface */ - saddr.ip4 = *src; - gaddr.ip4 = group->dst_address; + saddr.ip4 = *src_addr; + gaddr.ip4 = igmp_group->dst_address; igmp_listen (vm, 0, sw_if_index, saddr, gaddr, 0); - src++; + src_addr++; } } /* -- 2.16.6