2 *------------------------------------------------------------------
3 * Copyright (c) 2017 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *------------------------------------------------------------------
18 #include <vlib/vlib.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vpp/app/version.h>
21 #include <vnet/ip/ip.h>
22 #include <vnet/mfib/mfib_entry.h>
23 #include <vlib/unix/unix.h>
24 #include <vnet/adj/adj_mcast.h>
25 #include <vnet/fib/fib_entry.h>
26 #include <vnet/fib/fib_table.h>
27 #include <vnet/mfib/mfib_table.h>
29 #include <igmp/igmp.h>
34 igmp_main_t igmp_main;
36 /* clear all (S,G)s on specified config and remove this config from pool */
38 igmp_clear_config (igmp_config_t * config)
40 igmp_main_t *im = &igmp_main;
45 pool_foreach (sg, config->sg, (
47 clib_mem_free (sg->key);
50 pool_free (config->sg);
51 hash_free (config->igmp_sg_by_key);
53 hash_unset_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index);
54 pool_put (im->configs, config);
57 /* sort igmp timers, so that the first to expire is at end */
59 igmp_timer_compare (const void *_a, const void *_b)
61 const igmp_timer_t *a = _a;
62 const igmp_timer_t *b = _b;
63 f64 dt = b->exp_time - a->exp_time;
64 return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
68 igmp_sort_timers (igmp_timer_t * timers)
70 vlib_main_t *vm = vlib_get_main ();
72 qsort (timers, vec_len (timers), sizeof (igmp_timer_t), igmp_timer_compare);
74 vlib_process_signal_event (vm, igmp_timer_process_node.index,
75 IGMP_PROCESS_EVENT_UPDATE_TIMER, 0);
78 /* create new per interface timer
86 igmp_create_int_timer (f64 time, u32 sw_if_index,
87 igmp_timer_function_t * func)
89 igmp_main_t *im = &igmp_main;
92 pool_get (im->timers, timer);
93 memset (timer, 0, sizeof (igmp_timer_t));
95 timer->exp_time = time;
96 timer->sw_if_index = sw_if_index;
98 igmp_sort_timers (im->timers);
102 igmp_create_sg_timer (f64 time, u32 sw_if_index, igmp_sg_key_t * key,
103 igmp_timer_function_t * func)
105 igmp_main_t *im = &igmp_main;
108 pool_get (im->timers, timer);
109 memset (timer, 0, sizeof (igmp_timer_t));
111 timer->exp_time = time;
112 timer->sw_if_index = sw_if_index;
113 /* duplicate key, to prevent segmentation fault if (S,G) is removed */
114 timer->data = clib_mem_alloc (sizeof (igmp_sg_key_t));
115 clib_memcpy (timer->data, key, sizeof (igmp_sg_key_t));
117 igmp_sort_timers (im->timers);
120 /* get next timer to expire */
121 always_inline igmp_timer_t *
122 igmp_get_next_timer (igmp_main_t * im)
124 if (pool_elts (im->timers) > 0)
125 return vec_elt_at_index (im->timers, pool_elts (im->timers) - 1);
131 igmp_create_report_v2 (vlib_buffer_t * b, igmp_config_t * config)
135 igmp_main_t *im = &igmp_main;
138 sg = vec_elt_at_index (config->sg, im->next_index.sg_index);
140 igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b));
141 memset (igmp, 0, sizeof (igmp_message_t));
143 clib_memcpy (&igmp->dst, &sg->gaddr.ip4, sizeof (ip4_address_t));
145 (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources) ?
146 IGMP_TYPE_leave_group_v2 : IGMP_TYPE_membership_report_v2;
147 sum = ip_incremental_checksum (0, igmp, sizeof (igmp_message_t));
148 csum = ~ip_csum_fold (sum);
149 igmp->header.checksum = csum;
151 b->current_data += sizeof (igmp_message_t);
152 b->current_length += sizeof (igmp_message_t);
156 /* create IGMPv3 report with single (S,G)
157 * used to send state chenge reports
160 igmp_create_report_v31 (vlib_buffer_t * b, igmp_config_t * config)
164 igmp_main_t *im = &igmp_main;
168 sg = vec_elt_at_index (config->sg, im->next_index.sg_index);
170 len = sizeof (igmp_membership_report_v3_t);
171 igmp_membership_report_v3_t *igmp =
172 (igmp_membership_report_v3_t *) (vlib_buffer_get_current (b));
173 memset (igmp, 0, sizeof (igmp_membership_report_v3_t));
175 igmp->header.type = IGMP_TYPE_membership_report_v3;
176 igmp->n_groups = clib_host_to_net_u16 (1);
178 len += sizeof (igmp_membership_group_v3_t);
179 memset (igmp->groups, 0, sizeof (igmp_membership_group_v3_t));
180 igmp->groups[0].type = sg->group_type;
181 igmp->groups[0].n_aux_u32s = 0;
182 clib_memcpy (&igmp->groups[0].dst_address, &sg->gaddr.ip4,
183 sizeof (ip4_address_t));
185 igmp->groups[0].n_src_addresses = clib_host_to_net_u16 (1);
187 len += sizeof (ip4_address_t);
188 clib_memcpy (&igmp->groups[0].src_addresses[0], &sg->saddr.ip4,
189 sizeof (ip4_address_t));
191 sum = ip_incremental_checksum (0, igmp, len);
192 csum = ~ip_csum_fold (sum);
193 igmp->header.checksum = csum;
195 b->current_data += len;
196 b->current_length += len;
200 ip4_lookup (ip4_address_t * a, igmp_membership_report_v3_t * igmp, u16 n,
201 igmp_membership_group_v3_type_t type)
205 u32 l = sizeof (igmp_membership_report_v3_t);
207 for (i = 0; i < n; i++)
209 if ((!ip4_address_compare (a, &(group_ptr (igmp, l)->dst_address))) &&
210 (type == group_ptr (igmp, l)->type))
215 l += sizeof (igmp_membership_group_v3_t) +
216 clib_net_to_host_u16 (group_ptr (igmp, l)->n_src_addresses) *
217 sizeof (ip4_address_t);
223 /* create IGMPv3 report with all (S,G)s on config
224 * used to respond to general queries
227 igmp_create_report_v32 (vlib_buffer_t * b, igmp_config_t * config)
231 igmp_sg_t *sg0, *sg1;
233 u16 n_groups = 0, n_srcs = 0;
234 u32 grp_s = sizeof (igmp_membership_group_v3_t);
236 len = sizeof (igmp_membership_report_v3_t);
237 igmp_membership_report_v3_t *igmp =
238 (igmp_membership_report_v3_t *) (vlib_buffer_get_current (b));
239 memset (igmp, 0, sizeof (igmp_membership_report_v3_t));
241 igmp->header.type = IGMP_TYPE_membership_report_v3;
243 /* TODO: divide (S,G)s to multiple reports...
244 * - create report limited by <packet size|number of (S,G)s>?
246 * - on next timer continue loop
247 * - case of new query -> reset loop
250 pool_foreach (sg0, config->sg, (
252 if (ip4_lookup (&sg0->gaddr.ip4, igmp, n_groups, sg0->group_type))
254 memset (igmp + len, 0, grp_s);
255 clib_memcpy (&group_ptr (igmp, len)->dst_address, &sg0->gaddr.ip4, sizeof (ip4_address_t));
256 group_ptr (igmp, len)->type = sg0->group_type;
259 pool_foreach (sg1, config->sg, (
261 if ((!ip4_address_compare (&group_ptr (igmp, len - grp_s)->dst_address,
262 &sg1->gaddr.ip4)) && (group_ptr (igmp, len - grp_s)->type == (sg1->group_type)))
264 clib_memcpy (group_ptr (igmp, len + sizeof (ip4_address_t) * n_srcs),
265 &sg1->saddr.ip4, sizeof (ip4_address_t));
269 group_ptr (igmp, len - grp_s)->n_src_addresses = clib_host_to_net_u16 (n_srcs);
270 len += sizeof (ip4_address_t) * n_srcs;
275 igmp->n_groups = clib_host_to_net_u16 (n_groups);
277 sum = ip_incremental_checksum (0, igmp, len);
278 csum = ~ip_csum_fold (sum);
279 igmp->header.checksum = csum;
281 b->current_data += len;
282 b->current_length += len;
286 igmp_create_general_query_v3 (vlib_buffer_t * b, igmp_config_t * config)
288 vlib_main_t *vm = vlib_get_main ();
292 igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b));
293 memset (igmp, 0, sizeof (igmp_membership_query_v3_t));
295 igmp->header.type = IGMP_TYPE_membership_query;
296 igmp->header.code = 100;
298 config->flags &= ~IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
299 igmp_create_int_timer (vlib_time_now (vm) + (f64) (igmp->header.code / 10),
300 config->sw_if_index, igmp_query_resp_exp);
303 ip_incremental_checksum (0, igmp, sizeof (igmp_membership_query_v3_t));
304 csum = ~ip_csum_fold (sum);
305 igmp->header.checksum = csum;
307 b->current_data += sizeof (igmp_membership_query_v3_t);
308 b->current_length += sizeof (igmp_membership_query_v3_t);
313 igmp_create_ip4 (vlib_buffer_t * b, igmp_config_t * config, u8 is_report)
315 ip_lookup_main_t *lm = &ip4_main.lookup_main;
317 ip4_header_t *ip4 = (ip4_header_t *) (vlib_buffer_get_current (b));
318 memset (ip4, 0, sizeof (ip4_header_t));
319 ip4->ip_version_and_header_length = 0x45;
325 lm->if_address_pool_index_by_sw_if_index[config->sw_if_index];
326 if (PREDICT_TRUE (if_add_index != ~0))
328 ip_interface_address_t *if_add =
329 pool_elt_at_index (lm->if_address_pool, if_add_index);
330 ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add);
331 clib_memcpy (&ip4->src_address, if_ip, sizeof (ip4_address_t));
333 ip4->dst_address.as_u8[0] = 224;
334 ip4->dst_address.as_u8[1] = 0;
335 ip4->dst_address.as_u8[2] = 0;
336 ip4->dst_address.as_u8[3] = is_report ? 22 : 1;
338 b->current_data += ip4_header_bytes (ip4);
339 b->current_length += ip4_header_bytes (ip4);
341 config->next_create_msg (b, config);
342 ip4->length = clib_host_to_net_u16 (b->current_length);
344 ip4->checksum = ip4_header_checksum (ip4);
348 igmp_send_msg (vlib_main_t * vm, vlib_node_runtime_t * node,
349 igmp_main_t * im, igmp_config_t * config, u8 is_report)
351 u32 thread_index = vlib_get_thread_index ();
353 u32 next_index = IGMP_NEXT_IP4_REWRITE_MCAST_NODE;
355 u32 n_free_bufs = vec_len (im->buffers[thread_index]);
356 if (PREDICT_FALSE (n_free_bufs < 1))
358 vec_validate (im->buffers[thread_index], 1 + n_free_bufs - 1);
360 vlib_buffer_alloc (vm, &im->buffers[thread_index][n_free_bufs], 1);
361 _vec_len (im->buffers[thread_index]) = n_free_bufs;
365 u32 next0 = next_index;
366 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
368 if (n_left_to_next > 0)
370 vlib_buffer_t *b = 0;
375 u32 last_buf = vec_len (im->buffers[thread_index]) - 1;
376 bi = im->buffers[thread_index][last_buf];
377 b = vlib_get_buffer (vm, bi);
378 _vec_len (im->buffers[thread_index]) = last_buf;
380 if (PREDICT_FALSE (n_free_bufs == 0))
382 n_free_bufs += vlib_buffer_alloc (vm,
383 &im->buffers[thread_index]
385 _vec_len (im->buffers[thread_index]) = n_free_bufs;
389 b->current_length = 0;
391 igmp_create_ip4 (b, config, is_report);
395 b->total_length_not_including_first_buffer = 0;
396 b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
397 vnet_buffer (b)->sw_if_index[VLIB_RX] = (u32) ~ 0;
398 vnet_buffer (b)->ip.adj_index[VLIB_TX] = config->adj_index;
399 b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
406 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
407 n_left_to_next, bi, next0);
409 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
413 igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im,
414 igmp_timer_t * timer)
416 igmp_config_t *config;
418 u32 sw_if_index = timer->sw_if_index;
420 pool_put (im->timers, timer);
422 config = igmp_config_lookup (im, sw_if_index);
426 /* TODO: implement IGMPv2 */
427 config->next_create_msg = igmp_create_general_query_v3;
428 igmp_send_msg (vm, rt, im, config, /* is_report */ 0);
430 igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER, sw_if_index,
435 igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
436 igmp_main_t * im, igmp_timer_t * timer)
438 igmp_config_t *config;
440 u32 sw_if_index = timer->sw_if_index;
442 pool_put (im->timers, timer);
444 config = igmp_config_lookup (im, sw_if_index);
447 /* if report not reveived in max resp time clear igmp on interface */
448 if ((config->flags & IGMP_CONFIG_FLAG_QUERY_RESP_RECVED) == 0)
450 igmp_clear_config (config);
455 igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt,
456 igmp_main_t * im, igmp_timer_t * timer)
458 igmp_config_t *config;
460 u32 sw_if_index = timer->sw_if_index;
462 pool_put (im->timers, timer);
464 config = igmp_config_lookup (im, sw_if_index);
468 if (config->flags & IGMP_CONFIG_FLAG_CAN_SEND_REPORT)
470 /* TODO: implement IGMPv2 and IGMPv1 */
471 config->next_create_msg = igmp_create_report_v32;
472 igmp_send_msg (vm, rt, im, config, /* is_report */ 1);
473 /* WIP: unset flag after all reports sent */
474 config->flags &= ~IGMP_CONFIG_FLAG_CAN_SEND_REPORT;
479 igmp_send_state_changed (vlib_main_t * vm, vlib_node_runtime_t * rt,
480 igmp_main_t * im, igmp_timer_t * timer)
482 igmp_config_t *config;
485 pool_put (im->timers, timer);
487 config = vec_elt_at_index (im->configs, im->next_index.config_index);
488 sg = vec_elt_at_index (config->sg, im->next_index.sg_index);
490 config->next_create_msg = igmp_create_report_v31;
491 igmp_send_msg (vm, rt, im, config, /* is_report */ 1);
494 if (sg->group_type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include)
496 sg->group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
498 else if (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources)
500 /* remove API/CLI configured (S,G) */
501 hash_unset_mem (config->igmp_sg_by_key, sg->key);
502 clib_mem_free (sg->key);
503 pool_put (config->sg, sg);
504 if (pool_elts (config->sg) == 0)
506 hash_unset_mem (im->igmp_config_by_sw_if_index,
507 &config->sw_if_index);
508 pool_put (im->configs, config);
515 igmp_sg_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im,
516 igmp_timer_t * timer)
518 igmp_config_t *config;
521 igmp_sg_key_t *key = (igmp_sg_key_t *) timer->data;
523 config = igmp_config_lookup (im, timer->sw_if_index);
526 sg = igmp_sg_lookup (config, key);
530 /* check if this timer is valid */
531 if (timer->exp_time != sg->exp_time)
533 timer->exp_time = sg->exp_time;
534 igmp_sort_timers (im->timers);
538 /* source timer expired, remove (S,G) */
539 igmp_listen (vm, 0, timer->sw_if_index, key->saddr, key->gaddr, 0);
542 pool_put (im->timers, timer);
546 igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
549 igmp_main_t *im = &igmp_main;
550 uword *event_data = 0, event_type;
552 igmp_timer_t *timer = NULL;
557 vlib_process_wait_for_event_or_clock (vm,
558 timer->exp_time - time_start);
560 vlib_process_wait_for_event (vm);
562 time_start = vlib_time_now (vm);
564 event_type = vlib_process_get_events (vm, &event_data);
565 vec_reset_length (event_data);
567 if (event_type == IGMP_PROCESS_EVENT_UPDATE_TIMER)
570 IGMP_DBG ("time: %f", vlib_time_now (vm));
574 timer->func (vm, rt, im, timer);
577 timer = igmp_get_next_timer (im);
583 VLIB_REGISTER_NODE (igmp_timer_process_node) =
585 .function = igmp_timer_process,
586 .type = VLIB_NODE_TYPE_PROCESS,
587 .name = "igmp-timer-process",
588 .n_next_nodes = IGMP_N_NEXT,
590 [IGMP_NEXT_IP4_REWRITE_MCAST_NODE] = "ip4-rewrite-mcast",
591 [IGMP_NEXT_IP6_REWRITE_MCAST_NODE] = "ip6-rewrite-mcast",
597 igmp_listen (vlib_main_t * vm, u8 enable, u32 sw_if_index,
598 ip46_address_t saddr, ip46_address_t gaddr,
599 u8 cli_api_configured)
601 igmp_main_t *im = &igmp_main;
602 igmp_config_t *config;
607 /* set the lookup key */
608 clib_memcpy (&key.saddr, &saddr, sizeof (ip46_address_t));
609 clib_memcpy (&key.gaddr, &gaddr, sizeof (ip46_address_t));
613 config = igmp_config_lookup (im, sw_if_index);
616 pool_get (im->configs, config);
617 memset (config, 0, sizeof (igmp_config_t));
618 config->sw_if_index = sw_if_index;
619 config->igmp_sg_by_key =
620 hash_create_mem (0, sizeof (igmp_sg_key_t), sizeof (uword));
621 config->cli_api_configured = cli_api_configured;
622 /* use IGMPv3 by default */
623 config->igmp_ver = IGMP_V3;
624 config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
625 config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
626 if (!cli_api_configured)
628 igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER,
629 sw_if_index, igmp_send_query);
632 adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
633 config->sw_if_index);
634 hash_set_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index,
635 config - im->configs);
637 else if (config->cli_api_configured != cli_api_configured)
642 sg = igmp_sg_lookup (config, &key);
645 pool_get (config->sg, sg);
646 memset (sg, 0, sizeof (igmp_sg_t));
647 sg->key = clib_mem_alloc (sizeof (igmp_sg_key_t));
648 clib_memcpy (sg->key, &key, sizeof (igmp_sg_key_t));
649 clib_memcpy (&sg->saddr, &saddr, sizeof (ip46_address_t));
650 clib_memcpy (&sg->gaddr, &gaddr, sizeof (ip46_address_t));
651 sg->group_type = IGMP_MEMBERSHIP_GROUP_change_to_filter_include;
652 if (cli_api_configured)
654 /* create state-changed report timer with zero timeout */
655 igmp_create_int_timer (0, sw_if_index, igmp_send_state_changed);
659 sg->group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
660 sg->exp_time = vlib_time_now (vm) + IGMP_SG_TIMER;
661 igmp_create_sg_timer (sg->exp_time, config->sw_if_index,
662 sg->key, igmp_sg_exp);
663 /* notify all registered api clients */
664 igmp_event (im, config, sg);
666 hash_set_mem (config->igmp_sg_by_key, sg->key, sg - config->sg);
674 im->next_index.config_index = config - im->configs;
675 im->next_index.sg_index = sg - config->sg;
679 config = igmp_config_lookup (im, sw_if_index);
682 sg = igmp_sg_lookup (config, &key);
685 sg->group_type = IGMP_MEMBERSHIP_GROUP_block_old_sources;
686 im->next_index.config_index = config - im->configs;
687 im->next_index.sg_index = sg - config->sg;
688 /* notify all registered api clients */
689 if (!cli_api_configured)
690 igmp_event (im, config, sg);
692 igmp_create_int_timer (0, sw_if_index,
693 igmp_send_state_changed);
712 static clib_error_t *
713 igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
715 igmp_main_t *im = &igmp_main;
716 igmp_config_t *config;
717 clib_error_t *error = NULL;
719 /* remove igmp from a down interface to prevent crashes... */
721 igmp_config_lookup (im,
722 vnet_get_hw_interface (vnm,
723 hw_if_index)->sw_if_index);
726 if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
727 igmp_clear_config (config);
732 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down);
734 static clib_error_t *
735 igmp_init (vlib_main_t * vm)
738 igmp_main_t *im = &igmp_main;
739 vlib_thread_main_t *tm = vlib_get_thread_main ();
742 if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
745 im->igmp_config_by_sw_if_index =
746 hash_create_mem (0, sizeof (u32), sizeof (uword));
747 im->igmp_api_client_by_client_index =
748 hash_create_mem (0, sizeof (u32), sizeof (uword));
750 vec_validate_aligned (im->buffers, tm->n_vlib_mains - 1,
751 CLIB_CACHE_LINE_BYTES);
753 ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
755 igmp_type_info_t *ti;
756 igmp_report_type_info_t *rti;
757 #define igmp_type(n,s) \
759 vec_add2 (im->type_infos, ti, 1); \
761 ti->name = (u8 *) #s; \
764 #define igmp_report_type(n,s) \
766 vec_add2 (im->report_type_infos, rti, 1); \
768 rti->name = (u8 *) #s; \
774 #undef igmp_report_type
776 for (i = 0; i < vec_len (im->type_infos); i++)
778 ti = im->type_infos + i;
779 hash_set (im->type_info_by_type, ti->type, i);
782 for (i = 0; i < vec_len (im->report_type_infos); i++)
784 rti = im->report_type_infos + i;
785 hash_set (im->report_type_info_by_report_type, rti->type, i);
788 /* General Query address */
789 ip46_address_t addr0;
790 addr0.ip4.as_u8[0] = 224;
791 addr0.ip4.as_u8[1] = 0;
792 addr0.ip4.as_u8[2] = 0;
793 addr0.ip4.as_u8[3] = 1;
795 ip46_address_t addr1;
796 addr1.ip4.as_u8[0] = 224;
797 addr1.ip4.as_u8[1] = 0;
798 addr1.ip4.as_u8[2] = 0;
799 addr1.ip4.as_u8[3] = 22;
801 fib_route_path_t path = {
802 .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
803 .frp_addr = zero_addr,
804 .frp_sw_if_index = 0xffffffff,
807 .frp_flags = FIB_ROUTE_PATH_LOCAL,
809 const mfib_prefix_t mpfx0 = {
810 .fp_proto = FIB_PROTOCOL_IP4,
812 .fp_grp_addr = addr0,
814 const mfib_prefix_t mpfx1 = {
815 .fp_proto = FIB_PROTOCOL_IP4,
817 .fp_grp_addr = addr1,
819 /* configure MFIB to accept IGMPv3 general query and reports from all interfaces */
820 mfib_table_entry_path_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE, &path,
821 MFIB_ITF_FLAG_FORWARD);
822 mfib_table_entry_path_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE, &path,
823 MFIB_ITF_FLAG_FORWARD);
825 mfib_table_entry_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE, 0,
826 MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
827 mfib_table_entry_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE, 0,
828 MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
833 VLIB_INIT_FUNCTION (igmp_init);
836 VLIB_PLUGIN_REGISTER () = {
837 .version = VPP_BUILD_VER,
838 .description = "IGMP messaging",
843 * fd.io coding-style-patch-verification: ON
846 * eval: (c-set-style "gnu")