#include <vnet/mfib/mfib_table.h>
#include <igmp/igmp.h>
+#include <igmp/igmp_format.h>
+#include <igmp/igmp_pkt.h>
#include <limits.h>
#include <float.h>
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 <packet size|number of (S,G)s>?
- * - 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 doesn'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 doesn'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 exclude 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;
- igmp_timer_t *timer = NULL;
-
- while (1)
- {
- 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;
-
- 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);
- }
- 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 doesn'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);
+ clib_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;
+ config->proxy_device_id = ~0;
+
+ 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;
+ 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);
+
+ /*
+ * remove interface from proxy device
+ * if this device is upstream, delete proxy device
+ */
+ if (config->mode == IGMP_MODE_ROUTER)
+ igmp_proxy_device_add_del_interface (config->proxy_device_id,
+ config->sw_if_index, 0);
+ else if (config->mode == IGMP_MODE_HOST)
+ igmp_proxy_device_add_del (config->proxy_device_id,
+ config->sw_if_index, 0);
- /* 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)
+ 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);
+ }
+ else
{
- if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
- igmp_clear_config (config);
+ return -1;
}
- 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;
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) \
-do { \
- vec_add2 (im->type_infos, ti, 1); \
- 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"
+ im->igmp_api_client_by_client_index = hash_create (0, sizeof (u32));
-#undef igmp_type
-#undef igmp_report_type
+ im->logger = vlib_log_register_class ("igmp", 0);
- 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);
+ IGMP_DBG ("initialized");
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* */