X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Figmp%2Figmp.c;h=5cb8f02e9f53114caa9349896ecdfe6d6df7081b;hb=947ea62;hp=d71e77a74cdc9a7027c5fa45de67b2d0fa4e4c3f;hpb=7b867a8e491357058d37838091ed67a2e77bce2c;p=vpp.git diff --git a/src/plugins/igmp/igmp.c b/src/plugins/igmp/igmp.c index d71e77a74cd..5cb8f02e9f5 100644 --- a/src/plugins/igmp/igmp.c +++ b/src/plugins/igmp/igmp.c @@ -27,816 +27,461 @@ #include #include +#include +#include #include #include igmp_main_t igmp_main; -/* clear all (S,G)s on specified config and remove this config from pool */ -void -igmp_clear_config (igmp_config_t * config) -{ - igmp_main_t *im = &igmp_main; - igmp_sg_t *sg; - - ASSERT (config); - /* *INDENT-OFF* */ - pool_foreach (sg, config->sg, ( - { - clib_mem_free (sg->key); - })); - /* *INDENT-ON* */ - pool_free (config->sg); - hash_free (config->igmp_sg_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 */ -int -igmp_timer_compare (const void *_a, const void *_b) -{ - const igmp_timer_t *a = _a; - const igmp_timer_t *b = _b; - f64 dt = b->exp_time - a->exp_time; - return dt < 0 ? -1 : (dt > 0 ? +1 : 0); -} - -void -igmp_sort_timers (igmp_timer_t * timers) -{ - vlib_main_t *vm = vlib_get_main (); - - qsort (timers, vec_len (timers), sizeof (igmp_timer_t), igmp_timer_compare); +/* *INDENT-OFF* */ +/* General Query address */ +const static mfib_prefix_t mpfx_general_query = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_grp_addr = { + .ip4 = { + .as_u32 = IGMP_GENERAL_QUERY_ADDRESS, + }, + }, +}; - vlib_process_signal_event (vm, igmp_timer_process_node.index, - IGMP_PROCESS_EVENT_UPDATE_TIMER, 0); -} +/* Report address */ +const static mfib_prefix_t mpfx_report = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_grp_addr = { + .ip4 = { + .as_u32 = IGMP_MEMBERSHIP_REPORT_ADDRESS, + }, + }, +}; +/* *INDENT-ON* */ -/* create new per interface timer +/** + * @brief igmp send query (igmp_timer_function_t) * - * - delayed reports - * - query msg - * - query resp - */ - -void -igmp_create_int_timer (f64 time, u32 sw_if_index, - igmp_timer_function_t * func) -{ - igmp_main_t *im = &igmp_main; - igmp_timer_t *timer; - - pool_get (im->timers, timer); - memset (timer, 0, sizeof (igmp_timer_t)); - timer->func = func; - timer->exp_time = time; - timer->sw_if_index = sw_if_index; - - igmp_sort_timers (im->timers); -} - -void -igmp_create_sg_timer (f64 time, u32 sw_if_index, igmp_sg_key_t * key, - 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; - /* 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)); - - igmp_sort_timers (im->timers); -} - -/* get next timer to expire */ -always_inline igmp_timer_t * -igmp_get_next_timer (igmp_main_t * im) -{ - if (pool_elts (im->timers) > 0) - return vec_elt_at_index (im->timers, pool_elts (im->timers) - 1); - return NULL; -} - -/* -static void -igmp_create_report_v2 (vlib_buffer_t * b, igmp_config_t * config) -{ - ip_csum_t sum; - u16 csum; - igmp_main_t *im = &igmp_main; - igmp_sg_t *sg; - - sg = vec_elt_at_index (config->sg, im->next_index.sg_index); - - igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b)); - memset (igmp, 0, sizeof (igmp_message_t)); - - clib_memcpy (&igmp->dst, &sg->gaddr.ip4, sizeof (ip4_address_t)); - igmp->header.type = - (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources) ? - IGMP_TYPE_leave_group_v2 : IGMP_TYPE_membership_report_v2; - sum = ip_incremental_checksum (0, igmp, sizeof (igmp_message_t)); - csum = ~ip_csum_fold (sum); - igmp->header.checksum = csum; - - b->current_data += sizeof (igmp_message_t); - b->current_length += sizeof (igmp_message_t); -} -*/ - -/* create IGMPv3 report with single (S,G) - * used to send state chenge reports + * Send an igmp query. + * If the timer holds group key, send Group-Specific query, + * else send General query. */ static void -igmp_create_report_v31 (vlib_buffer_t * b, igmp_config_t * config) +igmp_send_general_query (u32 obj, void *dat) { - ip_csum_t sum; - u16 csum; - igmp_main_t *im = &igmp_main; - igmp_sg_t *sg; - u32 len = 0; - - sg = vec_elt_at_index (config->sg, im->next_index.sg_index); - - 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); - - 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); + igmp_pkt_build_query_t bq; + igmp_config_t *config; - len += sizeof (ip4_address_t); - clib_memcpy (&igmp->groups[0].src_addresses[0], &sg->saddr.ip4, - sizeof (ip4_address_t)); + config = igmp_config_get (obj); - sum = ip_incremental_checksum (0, igmp, len); - csum = ~ip_csum_fold (sum); - igmp->header.checksum = csum; + IGMP_DBG ("send-general-query: %U", + format_vnet_sw_if_index_name, vnet_get_main (), + config->sw_if_index); - b->current_data += len; - b->current_length += len; -} + igmp_timer_retire (&config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY]); -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 ((!ip4_address_compare (a, &group_ptr (igmp, l)->dst_address)) && - (type == group_ptr (igmp, l)->type)) - { - 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_pkt_build_query_init (&bq, config->sw_if_index); + igmp_pkt_query_v3_add_group (&bq, NULL, NULL); + igmp_pkt_query_v3_send (&bq); - return rv; + /* + * re-schedule + */ + config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY] = + igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_QUERY), + igmp_config_index (config), + igmp_send_general_query, NULL); } -/* 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) +igmp_send_state_change_group_report_v3 (u32 sw_if_index, + const igmp_group_t * group) { - 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, ( - { - 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, ( - { - 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))) - { - clib_memcpy (group_ptr (igmp, len + sizeof (ip4_address_t) * n_srcs), - &sg1->saddr.ip4, sizeof (ip4_address_t)); - n_srcs++; - } - })); - 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); + igmp_pkt_build_report_t br; - sum = ip_incremental_checksum (0, igmp, len); - csum = ~ip_csum_fold (sum); - igmp->header.checksum = csum; + IGMP_DBG ("state-change-group: %U", format_igmp_key, group->key); - b->current_data += len; - b->current_length += len; + igmp_pkt_build_report_init (&br, sw_if_index); + igmp_pkt_report_v3_add_group (&br, + group, + IGMP_MEMBERSHIP_GROUP_allow_new_sources); + igmp_pkt_report_v3_send (&br); } static void -igmp_create_general_query_v3 (vlib_buffer_t * b, igmp_config_t * config) +igmp_resend_state_change_group_report_v3 (u32 gi, void *data) { - 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)); - memset (igmp, 0, sizeof (igmp_membership_query_v3_t)); - - igmp->header.type = IGMP_TYPE_membership_query; - igmp->header.code = 100; - - config->flags &= ~IGMP_CONFIG_FLAG_QUERY_RESP_RECVED; - igmp_create_int_timer (vlib_time_now (vm) + (f64) (igmp->header.code / 10), - config->sw_if_index, igmp_query_resp_exp); - - sum = - ip_incremental_checksum (0, igmp, sizeof (igmp_membership_query_v3_t)); - csum = ~ip_csum_fold (sum); - igmp->header.checksum = csum; + igmp_config_t *config; + igmp_group_t *group; - b->current_data += sizeof (igmp_membership_query_v3_t); - b->current_length += sizeof (igmp_membership_query_v3_t); -} + group = igmp_group_get (gi); + config = igmp_config_get (group->config); + igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_RESEND_REPORT]); + igmp_send_state_change_group_report_v3 (config->sw_if_index, group); -static void -igmp_create_ip4 (vlib_buffer_t * b, igmp_config_t * config, u8 is_report) -{ - ip_lookup_main_t *lm = &ip4_main.lookup_main; - - ip4_header_t *ip4 = (ip4_header_t *) (vlib_buffer_get_current (b)); - memset (ip4, 0, sizeof (ip4_header_t)); - ip4->ip_version_and_header_length = 0x45; - ip4->ttl = 1; - ip4->protocol = 2; - ip4->tos = 0xc0; - - u32 if_add_index = - lm->if_address_pool_index_by_sw_if_index[config->sw_if_index]; - if (PREDICT_TRUE (if_add_index != ~0)) + if (++group->n_reports_sent < config->robustness_var) { - ip_interface_address_t *if_add = - pool_elt_at_index (lm->if_address_pool, if_add_index); - ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add); - clib_memcpy (&ip4->src_address, if_ip, sizeof (ip4_address_t)); + group->timers[IGMP_GROUP_TIMER_RESEND_REPORT] = + igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_REPORT_INTERVAL), + igmp_group_index (group), + igmp_resend_state_change_group_report_v3, NULL); } - 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; - - b->current_data += ip4_header_bytes (ip4); - b->current_length += ip4_header_bytes (ip4); - - config->next_create_msg (b, config); - ip4->length = clib_host_to_net_u16 (b->current_length); - - ip4->checksum = ip4_header_checksum (ip4); } -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) +int +igmp_listen (vlib_main_t * vm, + igmp_filter_mode_t mode, + u32 sw_if_index, + const ip46_address_t * saddrs, const ip46_address_t * gaddr) { - u32 thread_index = vlib_get_thread_index (); - u32 *to_next; - u32 next_index = IGMP_NEXT_IP4_REWRITE_MCAST_NODE; + const ip46_address_t *saddr; + igmp_config_t *config; + igmp_group_t *group; + + /* + * RFC 3376 Section 2 + " For a given combination of socket, interface, and multicast address, + only a single filter mode and source list can be in effect at any one + time. However, either the filter mode or the source list, or both, + may be changed by subsequent IPMulticastListen requests that specify + the same socket, interface, and multicast address. Each subsequent + request completely replaces any earlier request for the given socket, + interface and multicast address." + */ + int rv = 0; + IGMP_DBG ("listen: (%U, %U) %U %U", + format_igmp_src_addr_list, saddrs, + format_igmp_key, gaddr, + format_vnet_sw_if_index_name, vnet_get_main (), + sw_if_index, format_igmp_filter_mode, mode); + /* + * find configuration, if it dosn't exist, then this interface is + * not IGMP enabled + */ + config = igmp_config_lookup (sw_if_index); - u32 n_free_bufs = vec_len (im->buffers[thread_index]); - if (PREDICT_FALSE (n_free_bufs < 1)) + if (!config) { - vec_validate (im->buffers[thread_index], 1 + n_free_bufs - 1); - n_free_bufs += - vlib_buffer_alloc (vm, &im->buffers[thread_index][n_free_bufs], 1); - _vec_len (im->buffers[thread_index]) = n_free_bufs; + rv = VNET_API_ERROR_INVALID_INTERFACE; + goto error; } - - u32 n_left_to_next; - u32 next0 = next_index; - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - if (n_left_to_next > 0) + if (config->mode != IGMP_MODE_HOST) { - vlib_buffer_t *b = 0; - u32 bi = 0; - - if (n_free_bufs) - { - u32 last_buf = vec_len (im->buffers[thread_index]) - 1; - bi = im->buffers[thread_index][last_buf]; - b = vlib_get_buffer (vm, bi); - _vec_len (im->buffers[thread_index]) = last_buf; - n_free_bufs--; - if (PREDICT_FALSE (n_free_bufs == 0)) - { - n_free_bufs += vlib_buffer_alloc (vm, - &im->buffers[thread_index] - [n_free_bufs], 1); - _vec_len (im->buffers[thread_index]) = n_free_bufs; - } - - b->current_data = 0; - b->current_length = 0; - - igmp_create_ip4 (b, config, is_report); - - b->current_data = 0; - - b->total_length_not_including_first_buffer = 0; - b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID; - vnet_buffer (b)->sw_if_index[VLIB_RX] = (u32) ~ 0; - vnet_buffer (b)->ip.adj_index[VLIB_TX] = config->adj_index; - b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; - } - - to_next[0] = bi; - to_next += 1; - n_left_to_next -= 1; - - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, bi, next0); + rv = VNET_API_ERROR_INVALID_INTERFACE; + goto error; } - vlib_put_next_frame (vm, node, next_index, n_left_to_next); -} - -void -igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, - igmp_timer_t * timer) -{ - igmp_config_t *config; - - u32 sw_if_index = timer->sw_if_index; - - pool_put (im->timers, timer); - - config = igmp_config_lookup (im, sw_if_index); - if (!config) - return; - - /* TODO: implement IGMPv2 */ - config->next_create_msg = igmp_create_general_query_v3; - igmp_send_msg (vm, rt, im, config, /* is_report */ 0); - igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER, sw_if_index, - igmp_send_query); -} - -void -igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, - igmp_main_t * im, igmp_timer_t * timer) -{ - igmp_config_t *config; + /* find igmp group, if it dosn't exist, create new */ + group = igmp_group_lookup (config, gaddr); - u32 sw_if_index = timer->sw_if_index; - - pool_put (im->timers, timer); - - config = igmp_config_lookup (im, sw_if_index); - if (!config) - return; - /* if report not reveived in max resp time clear igmp on interface */ - if ((config->flags & IGMP_CONFIG_FLAG_QUERY_RESP_RECVED) == 0) + if (!group) { - igmp_clear_config (config); + group = igmp_group_alloc (config, gaddr, mode); + + /* new group implies create all sources */ + vec_foreach (saddr, saddrs) + { + igmp_group_src_update (group, saddr, IGMP_MODE_HOST); + } + + /* + * Send state changed event report for the group. + * + * RFC3376 Section 5.1 + * "To cover the possibility of the State-Change Report being missed by + * one or more multicast routers, it is retransmitted [Robustness + * Variable] - 1 more times, at intervals chosen at random from the + * range (0, [Unsolicited Report Interval])." + */ + igmp_send_state_change_group_report_v3 (config->sw_if_index, group); + + igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_RESEND_REPORT]); + + group->n_reports_sent = 1; + group->timers[IGMP_GROUP_TIMER_RESEND_REPORT] = + igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_REPORT_INTERVAL), + igmp_group_index (group), + igmp_resend_state_change_group_report_v3, NULL); } -} - -void -igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt, - igmp_main_t * im, igmp_timer_t * timer) -{ - igmp_config_t *config; - - u32 sw_if_index = timer->sw_if_index; - - pool_put (im->timers, timer); - - config = igmp_config_lookup (im, sw_if_index); - if (!config) - return; - - if (config->flags & IGMP_CONFIG_FLAG_CAN_SEND_REPORT) + else { - /* TODO: implement IGMPv2 and IGMPv1 */ - config->next_create_msg = igmp_create_report_v32; - igmp_send_msg (vm, rt, im, config, /* is_report */ 1); - /* WIP: unset flag after all reports sent */ - config->flags &= ~IGMP_CONFIG_FLAG_CAN_SEND_REPORT; - } -} + IGMP_DBG ("... update (%U, %U) %U %U", + format_igmp_src_addr_list, saddrs, + format_igmp_key, gaddr, + format_vnet_sw_if_index_name, vnet_get_main (), + sw_if_index, format_igmp_filter_mode, mode); + + /* + * RFC 3367 Section 5.1 + * + * Old State New State State-Change Record Sent + * --------- --------- ------------------------ + * + * 1) INCLUDE (A) INCLUDE (B) ALLOW (B-A), BLOCK (A-B) + * 2) EXCLUDE (A) EXCLUDE (B) ALLOW (A-B), BLOCK (B-A) + * 3) INCLUDE (A) EXCLUDE (B) TO_EX (B) + * 4) EXCLUDE (A) INCLUDE (B) TO_IN (B) + * + * N.B. We do not split state-change records for pending transfer + * hence there is no merge logic required. + */ + + if (IGMP_FILTER_MODE_INCLUDE == mode) + { + ip46_address_t *added, *removed; + igmp_pkt_build_report_t br; + + /* + * find the list of sources that have been added and removed from + * the include set + */ + removed = + igmp_group_present_minus_new (group, IGMP_FILTER_MODE_INCLUDE, + saddrs); + added = + igmp_group_new_minus_present (group, IGMP_FILTER_MODE_INCLUDE, + saddrs); + + if (!(vec_len (added) || vec_len (removed))) + /* no change => done */ + goto error; + + igmp_pkt_build_report_init (&br, config->sw_if_index); + + if (vec_len (added)) + { + igmp_pkt_report_v3_add_report (&br, + group->key, + added, + IGMP_MEMBERSHIP_GROUP_allow_new_sources); + } -void -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; + if (vec_len (removed)) + { + igmp_pkt_report_v3_add_report (&br, + group->key, + removed, + IGMP_MEMBERSHIP_GROUP_block_old_sources); + } - pool_put (im->timers, timer); + IGMP_DBG ("... added %U", format_igmp_src_addr_list, added); + IGMP_DBG ("... removed %U", format_igmp_src_addr_list, removed); - config = vec_elt_at_index (im->configs, im->next_index.config_index); - sg = vec_elt_at_index (config->sg, im->next_index.sg_index); + igmp_pkt_report_v3_send (&br); - config->next_create_msg = igmp_create_report_v31; - igmp_send_msg (vm, rt, im, config, /* is_report */ 1); + /* + * clear the group of the old sources and populate it with the new + * set requested + */ + igmp_group_free_all_srcs (group); + vec_foreach (saddr, saddrs) + { + igmp_group_src_update (group, saddr, IGMP_MODE_HOST); + } - 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) - { - /* 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) + if (0 == igmp_group_n_srcs (group, mode)) + igmp_group_clear (group); + + vec_free (added); + vec_free (removed); + } + else { - hash_unset_mem (im->igmp_config_by_sw_if_index, - &config->sw_if_index); - pool_put (im->configs, config); + /* + * The control plane is excluding some sources. + * - First; check for those that are present in the include list + * - Second; check add them to the exlude list + * + * TODO + */ } } +error: + return (rv); } -void -igmp_sg_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im, - igmp_timer_t * timer) +/** \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 walk_rc_t +igmp_sw_if_down (vnet_main_t * vnm, u32 sw_if_index, void *ctx) { igmp_config_t *config; - igmp_sg_t *sg; - - igmp_sg_key_t *key = (igmp_sg_key_t *) timer->data; - - config = igmp_config_lookup (im, timer->sw_if_index); - if (!config) - goto done; - sg = igmp_sg_lookup (config, key); - if (!sg) - goto done; - - /* check if this timer is valid */ - if (timer->exp_time != sg->exp_time) + config = igmp_config_lookup (sw_if_index); + IGMP_DBG ("down: %U", + format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index); + if (NULL != config) { - timer->exp_time = sg->exp_time; - igmp_sort_timers (im->timers); - return; + igmp_clear_config (config); } - /* source timer expired, remove (S,G) */ - igmp_listen (vm, 0, timer->sw_if_index, key->saddr, key->gaddr, 0); - -done: - pool_put (im->timers, timer); + return (WALK_CONTINUE); } -static uword -igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, - vlib_frame_t * f) +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; - uword *event_data = 0, event_type; - f64 time_start; - u8 enabled = 0; - igmp_timer_t *timer = NULL; - - while (1) - { - if (enabled) - 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; - - DBG ("time: %f", vlib_time_now (vm)); - - /* timer expired */ - timer->func (vm, rt, im, timer); - - next_timer: - timer = igmp_get_next_timer (im); - if (timer == NULL) - enabled = 0; - else - enabled = 1; - } - return 0; + clib_error_t *error = NULL; + /* remove igmp state from down interfaces */ + if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP)) + vnet_hw_interface_walk_sw (vnm, hw_if_index, igmp_sw_if_down, NULL); + return error; } -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (igmp_timer_process_node) = -{ - .function = igmp_timer_process, - .type = VLIB_NODE_TYPE_PROCESS, - .name = "igmp-timer-process", - .n_next_nodes = IGMP_N_NEXT, - .next_nodes = { - [IGMP_NEXT_IP4_REWRITE_MCAST_NODE] = "ip4-rewrite-mcast", - [IGMP_NEXT_IP6_REWRITE_MCAST_NODE] = "ip6-rewrite-mcast", - } -}; -/* *INDENT-ON* */ - +VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down); 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) +igmp_enable_disable (u32 sw_if_index, u8 enable, igmp_mode_t mode) { - igmp_main_t *im = &igmp_main; igmp_config_t *config; - igmp_sg_t *sg; - igmp_sg_key_t key; - 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)); + igmp_main_t *im = &igmp_main; + u32 mfib_index; + IGMP_DBG ("%s: %U", (enable ? "Enabled" : "Disabled"), + format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index); - if (enable) + /* *INDENT-OFF* */ + fib_route_path_t for_us_path = { - 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->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) - { - 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); - } - else if (config->cli_api_configured != cli_api_configured) - { - rv = -2; - goto error; - } - sg = igmp_sg_lookup (config, &key); - if (!sg) - { - 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; - if (cli_api_configured) - { - /* create state-changed report timer with zero timeout */ - igmp_create_int_timer (0, sw_if_index, igmp_send_state_changed); - } - else - { - 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); - } - hash_set_mem (config->igmp_sg_by_key, sg->key, sg - config->sg); - } - else + .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4), + .frp_addr = zero_addr, + .frp_sw_if_index = 0xffffffff, + .frp_fib_index = 0, + .frp_weight = 1, + .frp_flags = FIB_ROUTE_PATH_LOCAL, + }; + fib_route_path_t via_itf_path = + { + .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4), + .frp_addr = zero_addr, + .frp_sw_if_index = sw_if_index, + .frp_fib_index = 0, + .frp_weight = 1, + }; + /* *INDENT-ON* */ + /* find configuration, if it dosn't exist, create new */ + config = igmp_config_lookup (sw_if_index); + mfib_index = mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index); + if (!config && enable) + { + u32 ii; + + vec_validate_init_empty (im->igmp_config_by_sw_if_index, + sw_if_index, ~0); + pool_get (im->configs, config); + memset (config, 0, sizeof (igmp_config_t)); + config->sw_if_index = sw_if_index; + config->igmp_group_by_key = + hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword)); + config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + config->mode = mode; + + for (ii = 0; ii < IGMP_CONFIG_N_TIMERS; ii++) + config->timers[ii] = IGMP_TIMER_ID_INVALID; + + if (IGMP_MODE_ROUTER == mode) { - rv = -1; - goto error; + config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY] = + igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_QUERY), + igmp_config_index (config), + igmp_send_general_query, NULL); } - im->next_index.config_index = config - im->configs; - im->next_index.sg_index = sg - config->sg; + config->adj_index = + adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4, + config->sw_if_index); + im->igmp_config_by_sw_if_index[config->sw_if_index] = + (config - im->configs); + { + vec_validate (im->n_configs_per_mfib_index, mfib_index); + im->n_configs_per_mfib_index[mfib_index]++; + if (1 == im->n_configs_per_mfib_index[mfib_index]) + { + /* first config in this FIB */ + mfib_table_entry_path_update (mfib_index, + &mpfx_general_query, + MFIB_SOURCE_IGMP, + &for_us_path, + MFIB_ITF_FLAG_FORWARD); + mfib_table_entry_path_update (mfib_index, + &mpfx_report, + MFIB_SOURCE_IGMP, + &for_us_path, + MFIB_ITF_FLAG_FORWARD); + } + mfib_table_entry_path_update (mfib_index, + &mpfx_general_query, + MFIB_SOURCE_IGMP, + &via_itf_path, MFIB_ITF_FLAG_ACCEPT); + mfib_table_entry_path_update (mfib_index, &mpfx_report, + MFIB_SOURCE_IGMP, &via_itf_path, + MFIB_ITF_FLAG_ACCEPT); + } } - else + else if (config && !enable) { - config = igmp_config_lookup (im, sw_if_index); - if (config) - { - sg = igmp_sg_lookup (config, &key); - if (sg) - { - 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); - else - igmp_create_int_timer (0, sw_if_index, - igmp_send_state_changed); - } - else - { - rv = -1; - goto error; - } - } - else + vec_validate (im->n_configs_per_mfib_index, mfib_index); + im->n_configs_per_mfib_index[mfib_index]--; + if (0 == im->n_configs_per_mfib_index[mfib_index]) { - rv = -1; - goto error; + /* last config in this FIB */ + mfib_table_entry_path_remove (mfib_index, + &mpfx_general_query, + MFIB_SOURCE_IGMP, &for_us_path); + mfib_table_entry_path_remove (mfib_index, + &mpfx_report, + MFIB_SOURCE_IGMP, &for_us_path); } - } - -error: - return rv; -} - -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, - vnet_get_hw_interface (vnm, - hw_if_index)->sw_if_index); - if (config) - { - if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0) - igmp_clear_config (config); + mfib_table_entry_path_remove (mfib_index, + &mpfx_general_query, + MFIB_SOURCE_IGMP, &via_itf_path); + mfib_table_entry_path_remove (mfib_index, + &mpfx_report, + MFIB_SOURCE_IGMP, &via_itf_path); + igmp_clear_config (config); + im->igmp_config_by_sw_if_index[config->sw_if_index] = ~0; + hash_free (config->igmp_group_by_key); + pool_put (im->configs, config); } - return error; + + return (0); } -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) { + clib_error_t *error; igmp_main_t *im = &igmp_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - int i; - - - 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) \ -do { \ - vec_add2 (im->type_infos, ti, 1); \ - ti->type = n; \ - ti->name = (u8 *) #s; \ -} while (0); + if ((error = vlib_call_init_function (vm, ip4_lookup_init))) + return error; -#define igmp_report_type(n,s) \ -do { \ - vec_add2 (im->report_type_infos, rti, 1); \ - rti->type = n; \ - rti->name = (u8 *) #s; \ -} while (0); + im->igmp_api_client_by_client_index = hash_create (0, sizeof (u32)); -#include "igmp.def" + im->logger = vlib_log_register_class ("igmp", 0); -#undef igmp_type -#undef igmp_report_type + IGMP_DBG ("initialized"); - for (i = 0; i < vec_len (im->type_infos); i++) - { - ti = im->type_infos + i; - hash_set (im->type_info_by_type, ti->type, i); - } - - for (i = 0; i < vec_len (im->report_type_infos); i++) - { - rti = im->report_type_infos + i; - hash_set (im->report_type_info_by_report_type, rti->type, i); - } - - /* General Query address */ - ip46_address_t addr0; - addr0.ip4.as_u8[0] = 224; - addr0.ip4.as_u8[1] = 0; - addr0.ip4.as_u8[2] = 0; - addr0.ip4.as_u8[3] = 1; - /* 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; - - fib_route_path_t path = { - .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4), - .frp_addr = zero_addr, - .frp_sw_if_index = 0xffffffff, - .frp_fib_index = 0, - .frp_weight = 0, - .frp_flags = FIB_ROUTE_PATH_LOCAL, - }; - 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, - MFIB_ITF_FLAG_FORWARD); - mfib_table_entry_path_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE, &path, - MFIB_ITF_FLAG_FORWARD); - - mfib_table_entry_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE, 0, - MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF); - mfib_table_entry_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE, 0, - MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF); - - return 0; + return (error); } VLIB_INIT_FUNCTION (igmp_init); - /* *INDENT-OFF* */ VLIB_PLUGIN_REGISTER () = { - .version = VPP_BUILD_VER, - .description = "IGMP messaging", + .version = VPP_BUILD_VER, + .description = "IGMP messaging", }; /* *INDENT-ON* */