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;
37 igmp_clear_group (igmp_config_t * config, igmp_group_t * group)
44 IGMP_DBG ("group_type %u, sw_if_index %d", group->type,
48 pool_foreach (src, group->srcs, (
50 clib_mem_free (src->key);
53 pool_free (group->srcs);
54 hash_free (group->igmp_src_by_key);
56 hash_unset_mem (config->igmp_group_by_key, group->key);
57 clib_mem_free (group->key);
58 pool_put (config->groups, group);
62 igmp_clear_config (igmp_config_t * config)
64 igmp_main_t *im = &igmp_main;
69 pool_foreach (group, config->groups, (
71 igmp_clear_group (config, group);
74 pool_free (config->groups);
75 hash_free (config->igmp_group_by_key);
77 hash_unset (im->igmp_config_by_sw_if_index, config->sw_if_index);
78 pool_put (im->configs, config);
81 /** \brief igmp timer compare
82 @param _a - igmp timer
83 @param _b - igmp timer
85 Compare function for igmp_timer_t sorting.
88 igmp_timer_compare (const void *_a, const void *_b)
90 const igmp_timer_t *a = _a;
91 const igmp_timer_t *b = _b;
92 f64 dt = b->exp_time - a->exp_time;
93 return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
97 igmp_sort_timers (igmp_timer_t * timers)
99 vlib_main_t *vm = vlib_get_main ();
101 qsort (timers, vec_len (timers), sizeof (igmp_timer_t), igmp_timer_compare);
103 vlib_process_signal_event (vm, igmp_timer_process_node.index,
104 IGMP_PROCESS_EVENT_UPDATE_TIMER, 0);
108 igmp_create_int_timer (f64 time, u32 sw_if_index,
109 igmp_timer_function_t * func)
111 igmp_main_t *im = &igmp_main;
114 pool_get (im->timers, timer);
115 memset (timer, 0, sizeof (igmp_timer_t));
117 timer->exp_time = time;
118 timer->sw_if_index = sw_if_index;
120 igmp_sort_timers (im->timers);
124 igmp_create_group_timer (f64 time, u32 sw_if_index, igmp_key_t * gkey,
125 igmp_timer_function_t * func)
127 igmp_main_t *im = &igmp_main;
130 pool_get (im->timers, timer);
131 memset (timer, 0, sizeof (igmp_timer_t));
133 timer->exp_time = time;
134 timer->sw_if_index = sw_if_index;
138 /* duplicate keys, to prevent segmentation fault if (S,G) is removed */
139 timer->data = clib_mem_alloc (sizeof (igmp_key_t));
140 clib_memcpy (&((igmp_key_t *) timer->data)[0], gkey, sizeof (igmp_key_t));
142 igmp_sort_timers (im->timers);
146 igmp_create_src_timer (f64 time, u32 sw_if_index, igmp_key_t * gkey,
147 igmp_key_t * skey, igmp_timer_function_t * func)
149 igmp_main_t *im = &igmp_main;
152 pool_get (im->timers, timer);
153 memset (timer, 0, sizeof (igmp_timer_t));
155 timer->exp_time = time;
156 timer->sw_if_index = sw_if_index;
160 /* duplicate keys, to prevent segmentation fault if (S,G) is removed */
161 timer->data = clib_mem_alloc (sizeof (igmp_key_t) * 2);
162 clib_memcpy (&((igmp_key_t *) timer->data)[0], gkey, sizeof (igmp_key_t));
163 clib_memcpy (&((igmp_key_t *) timer->data)[1], skey, sizeof (igmp_key_t));
165 igmp_sort_timers (im->timers);
168 /** \brief igmp get next timer
169 @param im - igmp main
173 always_inline igmp_timer_t *
174 igmp_get_next_timer (igmp_main_t * im)
176 if (pool_elts (im->timers) > 0)
177 return vec_elt_at_index (im->timers, pool_elts (im->timers) - 1);
183 igmp_create_report_v2 (vlib_buffer_t * b, igmp_config_t * config)
187 igmp_main_t *im = &igmp_main;
190 sg = vec_elt_at_index (config->sg, im->next_index.sg_index);
192 igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b));
193 memset (igmp, 0, sizeof (igmp_message_t));
195 clib_memcpy (&igmp->dst, &sg->gaddr.ip4, sizeof (ip4_address_t));
197 (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources) ?
198 IGMP_TYPE_leave_group_v2 : IGMP_TYPE_membership_report_v2;
199 sum = ip_incremental_checksum (0, igmp, sizeof (igmp_message_t));
200 csum = ~ip_csum_fold (sum);
201 igmp->header.checksum = csum;
203 b->current_data += sizeof (igmp_message_t);
204 b->current_length += sizeof (igmp_message_t);
208 /* TODO: divide (S,G)s to multiple reports...
209 * - create report limited by <packet size|number of (S,G)s>?
211 * - on next timer continue loop
212 * - case of new query -> reset loop
215 /** \brief igmp create report all (v3)
216 @param b - vlib buffer
217 @param config - igmp configuration
218 @param group - igmp group
220 Create IGMPv3 report. If group is NULL, send all groups on interface.
223 igmp_create_report_v3 (vlib_buffer_t * b, igmp_config_t * config,
224 igmp_group_t * group)
233 igmp_membership_group_v3_t *igmp_group;
235 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 clib_net_to_host_u16 ((group) ? 1 : pool_elts (config->groups));
245 /* get pointer to first group */
246 igmp_group = igmp->groups;
248 /* if group is not NULL, send the specified group */
251 memset (igmp_group, 0, sizeof (igmp_membership_group_v3_t));
252 igmp_group->type = group->type;
253 igmp_group->n_src_addresses =
254 clib_host_to_net_u16 (pool_elts (group->srcs));
255 igmp_group->dst_address = group->addr.ip4;
258 pool_foreach (src, group->srcs, (
260 igmp_group->src_addresses[i++] = src->addr.ip4;
263 len += sizeof (ip4_address_t) * i;
264 len += sizeof (igmp_membership_group_v3_t);
269 pool_foreach (group, config->groups, (
271 memset (igmp_group, 0, sizeof (igmp_membership_group_v3_t));
272 igmp_group->type = group->type;
273 igmp_group->n_src_addresses =
274 clib_host_to_net_u16 (pool_elts (group->srcs));
275 igmp_group->dst_address = group->addr.ip4;
277 pool_foreach (src, group->srcs, (
279 igmp_group->src_addresses[i++] = src->addr.ip4;
281 len += sizeof (ip4_address_t) * i;
282 len += sizeof (igmp_membership_group_v3_t);
283 igmp_group = group_ptr (igmp, len);
288 sum = ip_incremental_checksum (0, igmp, len);
289 csum = ~ip_csum_fold (sum);
290 igmp->header.checksum = csum;
292 b->current_data += len;
293 b->current_length += len;
296 /** \brief igmp create query (v3)
297 @param b - vlib buffer
298 @param config - configuration that sends the query
299 @param group - if not NULL, create Group-specific query
301 Create igmp v3 qeury inside vlib buffer b.
302 If group == NULL create general query,
303 else, create group specific query.
306 igmp_create_query_v3 (vlib_buffer_t * b, igmp_config_t * config,
307 igmp_group_t * group)
309 vlib_main_t *vm = vlib_get_main ();
313 igmp_membership_query_v3_t *igmp =
314 (igmp_membership_query_v3_t *) (vlib_buffer_get_current (b));
315 memset (igmp, 0, sizeof (igmp_membership_query_v3_t));
317 igmp->header.type = IGMP_TYPE_membership_query;
318 igmp->header.code = 100;
320 config->flags &= ~IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
321 igmp_create_int_timer (vlib_time_now (vm) + (f64) (igmp->header.code / 10),
322 config->sw_if_index, igmp_query_resp_exp);
324 if (PREDICT_FALSE (group != NULL))
325 clib_memcpy (&igmp->dst, &group->addr.ip4, sizeof (ip4_address_t));
328 ip_incremental_checksum (0, igmp, sizeof (igmp_membership_query_v3_t));
329 csum = ~ip_csum_fold (sum);
330 igmp->header.checksum = csum;
332 b->current_data += sizeof (igmp_membership_query_v3_t);
333 b->current_length += sizeof (igmp_membership_query_v3_t);
336 /** \brief igmp create ip4
337 @param b - vlib buffer
338 @param config - igmp configuration
339 @param group - igmp membership group
340 @param is_report - if zero create query, else create report
342 Create ip4 header in vlib buffer b.
345 igmp_create_ip4 (vlib_buffer_t * b, igmp_config_t * config,
346 igmp_group_t * group, u8 is_report)
348 ip_lookup_main_t *lm = &ip4_main.lookup_main;
350 ip4_header_t *ip4 = (ip4_header_t *) (vlib_buffer_get_current (b));
351 memset (ip4, 0, sizeof (ip4_header_t));
352 ip4->ip_version_and_header_length = 0x45;
358 lm->if_address_pool_index_by_sw_if_index[config->sw_if_index];
359 if (PREDICT_TRUE (if_add_index != ~0))
361 ip_interface_address_t *if_add =
362 pool_elt_at_index (lm->if_address_pool, if_add_index);
363 ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add);
364 clib_memcpy (&ip4->src_address, if_ip, sizeof (ip4_address_t));
368 ip4->dst_address.as_u32 =
369 clib_host_to_net_u32 (IGMP_MEMBERSHIP_REPORT_ADDRESS);
373 clib_memcpy (&ip4->dst_address, &group->addr.ip4,
374 sizeof (ip4_address_t));
376 ip4->dst_address.as_u32 =
377 clib_host_to_net_u32 (IGMP_GENERAL_QUERY_ADDRESS);
380 b->current_data += ip4_header_bytes (ip4);
381 b->current_length += ip4_header_bytes (ip4);
383 config->next_create_msg (b, config, group);
384 ip4->length = clib_host_to_net_u16 (b->current_length);
386 ip4->checksum = ip4_header_checksum (ip4);
390 /** \brief igmp send message
391 @param vm - vlib main
392 @param node - vlib runtime node
393 @param im - igmp main
394 @param config - igmp configuration
395 @param group - igmp mebership group
396 @param is_report - 0 == qeury, else report
398 Send an igmp message. Get free vlib buffer fill it with igmp packet and transmit.
401 igmp_send_msg (vlib_main_t * vm, vlib_node_runtime_t * node,
402 igmp_main_t * im, igmp_config_t * config, igmp_group_t * group,
406 u32 next_index = ip4_rewrite_node.index;
409 vlib_buffer_alloc (vm, &bi, 1);
411 vlib_buffer_t *b = vlib_get_buffer (vm, bi);
412 vlib_buffer_free_list_t *fl = vlib_buffer_get_free_list (vm,
413 VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
414 vlib_buffer_init_for_free_list (b, fl);
417 b->current_length = 0;
419 igmp_create_ip4 (b, config, group, is_report);
423 b->total_length_not_including_first_buffer = 0;
424 b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
425 vnet_buffer (b)->sw_if_index[VLIB_RX] = (u32) ~ 0;
426 vnet_buffer (b)->ip.adj_index[VLIB_TX] = config->adj_index;
428 b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
431 vlib_frame_t *f = vlib_get_frame_to_node (vm, next_index);
432 to_next = vlib_frame_vector_args (f);
437 vlib_buffer_t *c = vlib_buffer_copy (vm, b);
439 to_next[0] = vlib_get_buffer_index (vm, c);
441 vlib_put_frame_to_node (vm, next_index, f);
445 igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im,
446 igmp_timer_t * timer)
448 igmp_config_t *config;
449 /* TODO: group-specific query: pass group key in timer */
450 igmp_group_t *group = NULL;
452 u32 sw_if_index = timer->sw_if_index;
454 pool_put (im->timers, timer);
456 config = igmp_config_lookup (im, sw_if_index);
460 /* TODO: implement IGMPv2 */
461 config->next_create_msg = igmp_create_query_v3;
462 igmp_send_msg (vm, rt, im, config, group, /* is_report */ 0);
464 /* in case of group query we don't want to set up another qery timer */
465 if (PREDICT_TRUE (!group))
466 igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER, sw_if_index,
471 igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
472 igmp_main_t * im, igmp_timer_t * timer)
474 igmp_config_t *config;
475 /* TODO: group-specific query: pass group key in timer */
476 igmp_group_t *group = NULL;
478 u32 sw_if_index = timer->sw_if_index;
480 pool_put (im->timers, timer);
482 config = igmp_config_lookup (im, sw_if_index);
486 /* if group != NULL this is a group-specific qeury timer */
487 if (PREDICT_FALSE (group != NULL))
489 if ((group->flags & IGMP_GROUP_FLAG_QUERY_RESP_RECVED) == 0)
491 igmp_clear_group (config, group);
495 /* if report not received in max resp time clear igmp on interface */
496 if ((config->flags & IGMP_CONFIG_FLAG_QUERY_RESP_RECVED) == 0)
498 igmp_clear_config (config);
503 igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt,
504 igmp_main_t * im, igmp_timer_t * timer)
506 igmp_config_t *config;
508 u32 sw_if_index = timer->sw_if_index;
510 pool_put (im->timers, timer);
512 config = igmp_config_lookup (im, sw_if_index);
516 if (config->flags & IGMP_CONFIG_FLAG_CAN_SEND_REPORT)
518 /* TODO: implement IGMPv2 and IGMPv1 */
519 config->next_create_msg = igmp_create_report_v3;
520 /* pass NULL as group to send all groups at once */
521 igmp_send_msg (vm, rt, im, config, NULL, /* is_report */ 1);
522 /* WIP: unset flag after all reports sent */
523 config->flags &= ~IGMP_CONFIG_FLAG_CAN_SEND_REPORT;
528 igmp_send_state_changed (vlib_main_t * vm, vlib_node_runtime_t * rt,
529 igmp_main_t * im, igmp_timer_t * timer)
531 igmp_config_t *config;
536 u32 sw_if_index = timer->sw_if_index;
537 IGMP_DBG ("sw_if_index %d", sw_if_index);
539 ASSERT (timer->data);
540 clib_memcpy (&gkey, timer->data, sizeof (igmp_key_t));
542 pool_put (im->timers, timer);
544 config = igmp_config_lookup (im, sw_if_index);
548 group = igmp_group_lookup (config, &gkey);
552 config->next_create_msg = igmp_create_report_v3;
553 igmp_send_msg (vm, rt, im, config, group, /* is_report */ 1);
555 IGMP_DBG ("group_type %u", group->type);
557 if (group->type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include)
560 igmp_group_t *new_group;
563 clib_memcpy (&new_gkey.data, &group->addr, sizeof (ip46_address_t));
564 new_gkey.group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
566 new_group = igmp_group_lookup (config, &new_gkey);
569 IGMP_DBG ("creating new group...");
570 pool_get (config->groups, new_group);
571 /* get valid pointer to old group */
572 group = igmp_group_lookup (config, &gkey);
574 memset (new_group, 0, sizeof (igmp_group_t));
576 clib_memcpy (&new_group->addr, &group->addr,
577 sizeof (ip46_address_t));
578 new_group->n_srcs = 0;
579 new_group->type = new_gkey.group_type;
581 new_group->key = clib_mem_alloc (sizeof (igmp_key_t));
582 clib_memcpy (new_group->key, &new_gkey, sizeof (igmp_key_t));
583 new_group->igmp_src_by_key =
584 hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
585 hash_set_mem (config->igmp_group_by_key, new_group->key,
586 new_group - config->groups);
589 /* loop through old group sources */
590 pool_foreach (src, group->srcs, (
592 /* add sources to new group */
593 new_src = igmp_src_lookup (new_group, src->key);
596 pool_get (new_group->srcs, new_src);
597 memset (new_src, 0, sizeof (igmp_src_t));
598 new_group->n_srcs += 1;
599 new_src->key = clib_mem_alloc (sizeof (igmp_key_t));
600 clib_memcpy (new_src->key, src->key, sizeof (igmp_key_t));
601 clib_memcpy (&new_src->addr, &src->addr,
602 sizeof (ip46_address_t));
604 hash_set_mem (new_group->igmp_src_by_key, new_src->key,
605 new_src - new_group->srcs);
612 IGMP_DBG ("remove group");
613 igmp_clear_group (config, group);
614 if (pool_elts (config->groups) == 0)
616 hash_unset (im->igmp_config_by_sw_if_index, config->sw_if_index);
617 pool_put (im->configs, config);
622 igmp_src_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
623 igmp_main_t * im, igmp_timer_t * timer)
625 igmp_config_t *config;
629 ASSERT (timer->data);
631 igmp_key_t *gkey = (igmp_key_t *) & ((igmp_key_t *) timer->data)[0];
632 igmp_key_t *skey = (igmp_key_t *) & ((igmp_key_t *) timer->data)[1];
634 config = igmp_config_lookup (im, timer->sw_if_index);
637 group = igmp_group_lookup (config, gkey);
640 src = igmp_src_lookup (group, skey);
643 /* check if this timer is valid */
644 if (timer->exp_time != src->exp_time)
646 timer->exp_time = src->exp_time;
647 igmp_sort_timers (im->timers);
651 ip46_address_t saddr;
652 ip46_address_t gaddr;
653 clib_memcpy (&saddr, skey->data, sizeof (ip46_address_t));
654 clib_memcpy (&gaddr, gkey->data, sizeof (ip46_address_t));
656 /* source timer expired, remove src */
657 igmp_listen (vm, 0, timer->sw_if_index, saddr, gaddr, 0);
659 clib_mem_free (timer->data);
660 pool_put (im->timers, timer);
663 /** \brief igmp timer process
664 @param vm - vlib main
665 @param rt - vlib runtime node
666 @param f - vlib frame
671 igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
674 igmp_main_t *im = &igmp_main;
675 uword *event_data = 0, event_type;
677 igmp_timer_t *timer = NULL;
680 /* suspend util timer expires */
682 vlib_process_wait_for_event_or_clock (vm,
683 timer->exp_time - time_start);
685 vlib_process_wait_for_event (vm);
686 time_start = vlib_time_now (vm);
687 event_type = vlib_process_get_events (vm, &event_data);
688 vec_reset_length (event_data);
689 if (event_type == IGMP_PROCESS_EVENT_UPDATE_TIMER)
691 IGMP_DBG ("time: %f", vlib_time_now (vm));
693 if (NULL != timer && timer->func != NULL)
694 timer->func (vm, rt, im, timer);
696 timer = igmp_get_next_timer (im);
702 VLIB_REGISTER_NODE (igmp_timer_process_node) =
704 .function = igmp_timer_process,
705 .type = VLIB_NODE_TYPE_PROCESS,
706 .name = "igmp-timer-process",
707 .n_next_nodes = IGMP_N_NEXT,
709 [IGMP_NEXT_IP4_REWRITE_MCAST_NODE] = "ip4-rewrite-mcast",
710 [IGMP_NEXT_IP6_REWRITE_MCAST_NODE] = "ip6-rewrite-mcast",
716 igmp_listen (vlib_main_t * vm, u8 enable, u32 sw_if_index,
717 ip46_address_t saddr, ip46_address_t gaddr, u8 flags)
719 igmp_main_t *im = &igmp_main;
720 igmp_config_t *config;
726 igmp_membership_group_v3_type_t group_type =
727 (flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) ?
728 IGMP_MEMBERSHIP_GROUP_change_to_filter_include :
729 IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
732 /* set the lookup keys */
734 gkey.group_type = group_type;
735 clib_memcpy (&skey.data, &saddr, sizeof (ip46_address_t));
736 clib_memcpy (&gkey.data, &gaddr, sizeof (ip46_address_t));
740 /* find configuration, if it dosn't exist, create new */
741 config = igmp_config_lookup (im, sw_if_index);
744 pool_get (im->configs, config);
745 memset (config, 0, sizeof (igmp_config_t));
746 config->sw_if_index = sw_if_index;
747 config->igmp_group_by_key =
748 hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
749 /* use IGMPv3 by default */
750 config->igmp_ver = IGMP_V3;
751 config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
752 config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED | flags;
754 if ((flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) == 0)
756 /* create qery timer */
757 igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER,
758 sw_if_index, igmp_send_query);
761 adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
762 config->sw_if_index);
763 hash_set (im->igmp_config_by_sw_if_index,
764 config->sw_if_index, config - im->configs);
766 else if ((config->flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED & flags)
772 /* find igmp group, if it dosn't exist, create new */
773 group = igmp_group_lookup (config, &gkey);
776 pool_get (config->groups, group);
777 memset (group, 0, sizeof (igmp_group_t));
778 group->key = clib_mem_alloc (sizeof (igmp_key_t));
779 clib_memcpy (group->key, &gkey, sizeof (igmp_key_t));
780 clib_memcpy (&group->addr, &gaddr, sizeof (ip46_address_t));
781 group->igmp_src_by_key =
782 hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
784 group->type = gkey.group_type;
785 if (flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED)
787 /* create state-changed report timer with zero timeout */
788 igmp_create_group_timer (0, sw_if_index, group->key,
789 igmp_send_state_changed);
792 hash_set_mem (config->igmp_group_by_key, group->key,
793 group - config->groups);
795 /* find source, if it dosn't exist, create new */
796 src = igmp_src_lookup (group, &skey);
799 pool_get (group->srcs, src);
800 memset (src, 0, sizeof (igmp_src_t));
802 src->key = clib_mem_alloc (sizeof (igmp_key_t));
803 clib_memcpy (src->key, &skey, sizeof (igmp_key_t));
804 clib_memcpy (&src->addr, &saddr, sizeof (ip46_address_t));
805 if ((flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) == 0)
807 /* arm source timer (after expiration remove (S,G)) */
808 igmp_event (im, config, group, src);
809 src->exp_time = vlib_time_now (vm) + IGMP_SRC_TIMER;
810 igmp_create_src_timer (src->exp_time, config->sw_if_index,
811 group->key, src->key, igmp_src_exp);
814 hash_set_mem (group->igmp_src_by_key, src->key, src - group->srcs);
824 config = igmp_config_lookup (im, sw_if_index);
827 gkey.group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
828 group = igmp_group_lookup (config, &gkey);
831 src = igmp_src_lookup (group, &skey);
834 /* add source to block_all_sources group */
836 igmp_group_t *new_group;
838 clib_memcpy (&new_gkey, &gkey, sizeof (igmp_key_t));
839 new_gkey.group_type =
840 IGMP_MEMBERSHIP_GROUP_block_old_sources;
841 new_group = igmp_group_lookup (config, &new_gkey);
844 pool_get (config->groups, new_group);
846 group = igmp_group_lookup (config, &gkey);
848 memset (new_group, 0, sizeof (igmp_group_t));
849 new_group->key = clib_mem_alloc (sizeof (igmp_key_t));
850 clib_memcpy (new_group->key, &new_gkey,
851 sizeof (igmp_key_t));
852 clib_memcpy (&new_group->addr, &group->addr,
853 sizeof (ip46_address_t));
854 new_group->igmp_src_by_key =
855 hash_create_mem (0, sizeof (igmp_key_t),
857 new_group->n_srcs = 0;
858 new_group->type = new_gkey.group_type;
859 hash_set_mem (config->igmp_group_by_key, new_group->key,
860 new_group - config->groups);
863 new_src = igmp_src_lookup (new_group, &skey);
866 pool_get (new_group->srcs, new_src);
867 memset (new_src, 0, sizeof (igmp_src_t));
868 new_group->n_srcs += 1;
869 new_src->key = clib_mem_alloc (sizeof (igmp_key_t));
870 clib_memcpy (new_src->key, src->key,
871 sizeof (igmp_key_t));
872 clib_memcpy (&new_src->addr, &src->addr,
873 sizeof (ip46_address_t));
874 hash_set_mem (new_group->igmp_src_by_key, new_src->key,
875 new_src - new_group->srcs);
878 /* notify all registered api clients */
879 if ((flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED) == 0)
880 igmp_event (im, config, new_group, new_src);
882 igmp_create_group_timer (0, sw_if_index, new_group->key,
883 igmp_send_state_changed);
884 /* remove source form mode_is_filter_include group */
885 hash_unset_mem (group->igmp_src_by_key, src->key);
886 clib_mem_free (src->key);
887 pool_put (group->srcs, src);
889 if (group->n_srcs <= 0)
890 igmp_clear_group (config, group);
891 if (pool_elts (config->groups) <= 0)
892 igmp_clear_config (config);
917 /** \brief igmp hardware interface link up down
918 @param vnm - vnet main
919 @param hw_if_index - interface hw_if_index
920 @param flags - hw interface flags
922 If an interface goes down, remove its (S,G)s.
924 static clib_error_t *
925 igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
927 igmp_main_t *im = &igmp_main;
928 igmp_config_t *config;
929 clib_error_t *error = NULL;
930 /* remove igmp from a down interface to prevent crashes... */
932 igmp_config_lookup (im,
933 vnet_get_hw_interface (vnm,
934 hw_if_index)->sw_if_index);
937 if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
938 igmp_clear_config (config);
943 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down);
945 /** \brief igmp initialization
946 @param vm - vlib main
948 initialize igmp plugin. Initialize igmp_main, set mfib to allow igmp traffic.
950 static clib_error_t *
951 igmp_init (vlib_main_t * vm)
954 igmp_main_t *im = &igmp_main;
956 if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
958 im->igmp_config_by_sw_if_index = hash_create (0, sizeof (u32));
959 im->igmp_api_client_by_client_index = hash_create (0, sizeof (u32));
960 ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
961 igmp_type_info_t *ti;
962 igmp_report_type_info_t *rti;
963 #define igmp_type(n,s) \
965 vec_add2 (im->type_infos, ti, 1); \
967 ti->name = (u8 *) #s; \
969 #define igmp_report_type(n,s) \
971 vec_add2 (im->report_type_infos, rti, 1); \
973 rti->name = (u8 *) #s; \
977 #undef igmp_report_type
978 for (i = 0; i < vec_len (im->type_infos); i++)
980 ti = im->type_infos + i;
981 hash_set (im->type_info_by_type, ti->type, i);
984 for (i = 0; i < vec_len (im->report_type_infos); i++)
986 rti = im->report_type_infos + i;
987 hash_set (im->report_type_info_by_report_type, rti->type, i);
990 /* General Query address */
991 ip46_address_t addr0 = {
995 addr0.ip4.as_u32 = clib_host_to_net_u32 (IGMP_GENERAL_QUERY_ADDRESS);
998 ip46_address_t addr1 = {
1002 addr1.ip4.as_u32 = clib_host_to_net_u32 (IGMP_MEMBERSHIP_REPORT_ADDRESS);
1004 fib_route_path_t path = {
1005 .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
1006 .frp_addr = zero_addr,
1007 .frp_sw_if_index = 0xffffffff,
1010 .frp_flags = FIB_ROUTE_PATH_LOCAL,
1013 const mfib_prefix_t mpfx0 = {
1014 .fp_proto = FIB_PROTOCOL_IP4,
1016 .fp_grp_addr = addr0,
1019 const mfib_prefix_t mpfx1 = {
1020 .fp_proto = FIB_PROTOCOL_IP4,
1022 .fp_grp_addr = addr1,
1025 /* configure MFIB to accept IGMPv3 general query
1026 * and reports from all interfaces
1028 mfib_table_entry_path_update (0, &mpfx0,
1029 MFIB_SOURCE_DEFAULT_ROUTE, &path,
1030 MFIB_ITF_FLAG_FORWARD);
1031 mfib_table_entry_path_update (0, &mpfx1,
1032 MFIB_SOURCE_DEFAULT_ROUTE, &path,
1033 MFIB_ITF_FLAG_FORWARD);
1034 mfib_table_entry_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE,
1035 0, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
1036 mfib_table_entry_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE,
1037 0, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
1041 VLIB_INIT_FUNCTION (igmp_init);
1043 VLIB_PLUGIN_REGISTER () = {
1044 .version = VPP_BUILD_VER,
1045 .description = "IGMP messaging",
1050 * fd.io coding-style-patch-verification: ON
1053 * eval: (c-set-style "gnu")