From b1d2aa8bca3a6e1811fb67dde9c8862e13216824 Mon Sep 17 00:00:00 2001 From: Jakub Grajciar Date: Mon, 23 Apr 2018 14:40:59 +0200 Subject: 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/igmp.c | 715 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 475 insertions(+), 240 deletions(-) (limited to 'src/plugins/igmp/igmp.c') 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, -- cgit 1.2.3-korg