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_mem (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,
405 u32 thread_index = vlib_get_thread_index ();
407 u32 next_index = IGMP_NEXT_IP4_REWRITE_MCAST_NODE;
409 u32 n_free_bufs = vec_len (im->buffers[thread_index]);
410 if (PREDICT_FALSE (n_free_bufs < 1))
412 vec_validate (im->buffers[thread_index], 1 + n_free_bufs - 1);
414 vlib_buffer_alloc (vm, &im->buffers[thread_index][n_free_bufs], 1);
415 _vec_len (im->buffers[thread_index]) = n_free_bufs;
419 u32 next0 = next_index;
420 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
422 if (n_left_to_next > 0)
424 vlib_buffer_t *b = 0;
429 u32 last_buf = vec_len (im->buffers[thread_index]) - 1;
430 bi = im->buffers[thread_index][last_buf];
431 b = vlib_get_buffer (vm, bi);
432 _vec_len (im->buffers[thread_index]) = last_buf;
434 if (PREDICT_FALSE (n_free_bufs == 0))
436 n_free_bufs += vlib_buffer_alloc (vm,
437 &im->buffers[thread_index]
439 _vec_len (im->buffers[thread_index]) = n_free_bufs;
443 b->current_length = 0;
445 igmp_create_ip4 (b, config, group, is_report);
449 b->total_length_not_including_first_buffer = 0;
450 b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
451 vnet_buffer (b)->sw_if_index[VLIB_RX] = (u32) ~ 0;
452 vnet_buffer (b)->ip.adj_index[VLIB_TX] = config->adj_index;
453 b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
460 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
461 n_left_to_next, bi, next0);
463 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
467 igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im,
468 igmp_timer_t * timer)
470 igmp_config_t *config;
471 /* TODO: group-specific query: pass group key in timer */
472 igmp_group_t *group = NULL;
474 u32 sw_if_index = timer->sw_if_index;
476 pool_put (im->timers, timer);
478 config = igmp_config_lookup (im, sw_if_index);
482 /* TODO: implement IGMPv2 */
483 config->next_create_msg = igmp_create_query_v3;
484 igmp_send_msg (vm, rt, im, config, group, /* is_report */ 0);
486 /* in case of group query we don't want to set up another qery timer */
487 if (PREDICT_TRUE (!group))
488 igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER, sw_if_index,
493 igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
494 igmp_main_t * im, igmp_timer_t * timer)
496 igmp_config_t *config;
497 /* TODO: group-specific query: pass group key in timer */
498 igmp_group_t *group = NULL;
500 u32 sw_if_index = timer->sw_if_index;
502 pool_put (im->timers, timer);
504 config = igmp_config_lookup (im, sw_if_index);
508 /* if group != NULL this is a group-specific qeury timer */
509 if (PREDICT_FALSE (group != NULL))
511 if ((group->flags & IGMP_GROUP_FLAG_QUERY_RESP_RECVED) == 0)
513 igmp_clear_group (config, group);
517 /* if report not received in max resp time clear igmp on interface */
518 if ((config->flags & IGMP_CONFIG_FLAG_QUERY_RESP_RECVED) == 0)
520 igmp_clear_config (config);
525 igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt,
526 igmp_main_t * im, igmp_timer_t * timer)
528 igmp_config_t *config;
530 u32 sw_if_index = timer->sw_if_index;
532 pool_put (im->timers, timer);
534 config = igmp_config_lookup (im, sw_if_index);
538 if (config->flags & IGMP_CONFIG_FLAG_CAN_SEND_REPORT)
540 /* TODO: implement IGMPv2 and IGMPv1 */
541 config->next_create_msg = igmp_create_report_v3;
542 /* pass NULL as group to send all groups at once */
543 igmp_send_msg (vm, rt, im, config, NULL, /* is_report */ 1);
544 /* WIP: unset flag after all reports sent */
545 config->flags &= ~IGMP_CONFIG_FLAG_CAN_SEND_REPORT;
550 igmp_send_state_changed (vlib_main_t * vm, vlib_node_runtime_t * rt,
551 igmp_main_t * im, igmp_timer_t * timer)
553 igmp_config_t *config;
558 u32 sw_if_index = timer->sw_if_index;
559 IGMP_DBG ("sw_if_index %d", sw_if_index);
561 ASSERT (timer->data);
562 clib_memcpy (&gkey, timer->data, sizeof (igmp_key_t));
564 pool_put (im->timers, timer);
566 config = igmp_config_lookup (im, sw_if_index);
570 group = igmp_group_lookup (config, &gkey);
574 config->next_create_msg = igmp_create_report_v3;
575 igmp_send_msg (vm, rt, im, config, group, /* is_report */ 1);
577 IGMP_DBG ("group_type %u", group->type);
579 if (group->type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include)
582 igmp_group_t *new_group;
585 clib_memcpy (&new_gkey.data, &group->addr, sizeof (ip46_address_t));
586 new_gkey.group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
588 new_group = igmp_group_lookup (config, &new_gkey);
591 IGMP_DBG ("creating new group...");
592 pool_get (config->groups, new_group);
593 /* get valid pointer to old group */
594 group = igmp_group_lookup (config, &gkey);
596 memset (new_group, 0, sizeof (igmp_group_t));
598 clib_memcpy (&new_group->addr, &group->addr,
599 sizeof (ip46_address_t));
600 new_group->n_srcs = 0;
601 new_group->type = new_gkey.group_type;
603 new_group->key = clib_mem_alloc (sizeof (igmp_key_t));
604 clib_memcpy (new_group->key, &new_gkey, sizeof (igmp_key_t));
605 new_group->igmp_src_by_key =
606 hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
607 hash_set_mem (config->igmp_group_by_key, new_group->key,
608 new_group - config->groups);
611 /* loop through old group sources */
612 pool_foreach (src, group->srcs, (
614 /* add sources to new group */
615 new_src = igmp_src_lookup (new_group, src->key);
618 pool_get (new_group->srcs, new_src);
619 memset (new_src, 0, sizeof (igmp_src_t));
620 new_group->n_srcs += 1;
621 new_src->key = clib_mem_alloc (sizeof (igmp_key_t));
622 clib_memcpy (new_src->key, src->key, sizeof (igmp_key_t));
623 clib_memcpy (&new_src->addr, &src->addr,
624 sizeof (ip46_address_t));
626 hash_set_mem (new_group->igmp_src_by_key, new_src->key,
627 new_src - new_group->srcs);
634 IGMP_DBG ("remove group");
635 igmp_clear_group (config, group);
636 if (pool_elts (config->groups) == 0)
638 hash_unset_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index);
639 pool_put (im->configs, config);
644 igmp_src_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
645 igmp_main_t * im, igmp_timer_t * timer)
647 igmp_config_t *config;
651 ASSERT (timer->data);
653 igmp_key_t *gkey = (igmp_key_t *) & ((igmp_key_t *) timer->data)[0];
654 igmp_key_t *skey = (igmp_key_t *) & ((igmp_key_t *) timer->data)[1];
656 config = igmp_config_lookup (im, timer->sw_if_index);
659 group = igmp_group_lookup (config, gkey);
662 src = igmp_src_lookup (group, skey);
665 /* check if this timer is valid */
666 if (timer->exp_time != src->exp_time)
668 timer->exp_time = src->exp_time;
669 igmp_sort_timers (im->timers);
673 ip46_address_t saddr;
674 ip46_address_t gaddr;
675 clib_memcpy (&saddr, skey->data, sizeof (ip46_address_t));
676 clib_memcpy (&gaddr, gkey->data, sizeof (ip46_address_t));
678 /* source timer expired, remove src */
679 igmp_listen (vm, 0, timer->sw_if_index, saddr, gaddr, 0);
681 clib_mem_free (timer->data);
682 pool_put (im->timers, timer);
685 /** \brief igmp timer process
686 @param vm - vlib main
687 @param rt - vlib runtime node
688 @param f - vlib frame
693 igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
696 igmp_main_t *im = &igmp_main;
697 uword *event_data = 0, event_type;
699 igmp_timer_t *timer = NULL;
702 /* suspend util timer expires */
704 vlib_process_wait_for_event_or_clock (vm,
705 timer->exp_time - time_start);
707 vlib_process_wait_for_event (vm);
708 time_start = vlib_time_now (vm);
709 event_type = vlib_process_get_events (vm, &event_data);
710 vec_reset_length (event_data);
711 if (event_type == IGMP_PROCESS_EVENT_UPDATE_TIMER)
713 IGMP_DBG ("time: %f", vlib_time_now (vm));
715 if (NULL != timer && timer->func != NULL)
716 timer->func (vm, rt, im, timer);
718 timer = igmp_get_next_timer (im);
724 VLIB_REGISTER_NODE (igmp_timer_process_node) =
726 .function = igmp_timer_process,
727 .type = VLIB_NODE_TYPE_PROCESS,
728 .name = "igmp-timer-process",
729 .n_next_nodes = IGMP_N_NEXT,
731 [IGMP_NEXT_IP4_REWRITE_MCAST_NODE] = "ip4-rewrite-mcast",
732 [IGMP_NEXT_IP6_REWRITE_MCAST_NODE] = "ip6-rewrite-mcast",
738 igmp_listen (vlib_main_t * vm, u8 enable, u32 sw_if_index,
739 ip46_address_t saddr, ip46_address_t gaddr,
740 u8 cli_api_configured)
742 igmp_main_t *im = &igmp_main;
743 igmp_config_t *config;
749 igmp_membership_group_v3_type_t group_type =
750 (cli_api_configured) ?
751 IGMP_MEMBERSHIP_GROUP_change_to_filter_include :
752 IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
755 /* set the lookup keys */
757 gkey.group_type = group_type;
758 clib_memcpy (&skey.data, &saddr, sizeof (ip46_address_t));
759 clib_memcpy (&gkey.data, &gaddr, sizeof (ip46_address_t));
763 /* find configuration, if it dosn't exist, create new */
764 config = igmp_config_lookup (im, sw_if_index);
767 pool_get (im->configs, config);
768 memset (config, 0, sizeof (igmp_config_t));
769 config->sw_if_index = sw_if_index;
770 config->igmp_group_by_key =
771 hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
772 config->cli_api_configured = cli_api_configured;
773 /* use IGMPv3 by default */
774 config->igmp_ver = IGMP_V3;
775 config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
776 config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
778 if (!cli_api_configured)
780 /* create qery timer */
781 igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER,
782 sw_if_index, igmp_send_query);
785 adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
786 config->sw_if_index);
787 hash_set_mem (im->igmp_config_by_sw_if_index,
788 &config->sw_if_index, config - im->configs);
790 else if (config->cli_api_configured != cli_api_configured)
795 /* find igmp group, if it dosn't exist, create new */
796 group = igmp_group_lookup (config, &gkey);
799 pool_get (config->groups, group);
800 memset (group, 0, sizeof (igmp_group_t));
801 group->key = clib_mem_alloc (sizeof (igmp_key_t));
802 clib_memcpy (group->key, &gkey, sizeof (igmp_key_t));
803 clib_memcpy (&group->addr, &gaddr, sizeof (ip46_address_t));
804 group->igmp_src_by_key =
805 hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
807 group->type = gkey.group_type;
808 if (cli_api_configured)
810 /* create state-changed report timer with zero timeout */
811 igmp_create_group_timer (0, sw_if_index, group->key,
812 igmp_send_state_changed);
815 hash_set_mem (config->igmp_group_by_key, group->key,
816 group - config->groups);
818 /* find source, if it dosn't exist, create new */
819 src = igmp_src_lookup (group, &skey);
822 pool_get (group->srcs, src);
823 memset (src, 0, sizeof (igmp_src_t));
825 src->key = clib_mem_alloc (sizeof (igmp_key_t));
826 clib_memcpy (src->key, &skey, sizeof (igmp_key_t));
827 clib_memcpy (&src->addr, &saddr, sizeof (ip46_address_t));
828 if (!cli_api_configured)
830 /* arm source timer (after expiration remove (S,G)) */
831 igmp_event (im, config, group, src);
832 src->exp_time = vlib_time_now (vm) + IGMP_SRC_TIMER;
833 igmp_create_src_timer (src->exp_time, config->sw_if_index,
834 group->key, src->key, igmp_src_exp);
837 hash_set_mem (group->igmp_src_by_key, src->key, src - group->srcs);
847 config = igmp_config_lookup (im, sw_if_index);
850 gkey.group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
851 group = igmp_group_lookup (config, &gkey);
854 src = igmp_src_lookup (group, &skey);
857 /* add source to block_all_sources group */
859 igmp_group_t *new_group;
861 clib_memcpy (&new_gkey, &gkey, sizeof (igmp_key_t));
862 new_gkey.group_type =
863 IGMP_MEMBERSHIP_GROUP_block_old_sources;
864 new_group = igmp_group_lookup (config, &new_gkey);
867 pool_get (config->groups, new_group);
869 group = igmp_group_lookup (config, &gkey);
871 memset (new_group, 0, sizeof (igmp_group_t));
872 new_group->key = clib_mem_alloc (sizeof (igmp_key_t));
873 clib_memcpy (new_group->key, &new_gkey,
874 sizeof (igmp_key_t));
875 clib_memcpy (&new_group->addr, &group->addr,
876 sizeof (ip46_address_t));
877 new_group->igmp_src_by_key =
878 hash_create_mem (0, sizeof (igmp_key_t),
880 new_group->n_srcs = 0;
881 new_group->type = new_gkey.group_type;
882 hash_set_mem (config->igmp_group_by_key, new_group->key,
883 new_group - config->groups);
886 new_src = igmp_src_lookup (new_group, &skey);
889 pool_get (new_group->srcs, new_src);
890 memset (new_src, 0, sizeof (igmp_src_t));
891 new_group->n_srcs += 1;
892 new_src->key = clib_mem_alloc (sizeof (igmp_key_t));
893 clib_memcpy (new_src->key, src->key,
894 sizeof (igmp_key_t));
895 clib_memcpy (&new_src->addr, &src->addr,
896 sizeof (ip46_address_t));
897 hash_set_mem (new_group->igmp_src_by_key, new_src->key,
898 new_src - new_group->srcs);
901 /* notify all registered api clients */
902 if (!cli_api_configured)
903 igmp_event (im, config, new_group, new_src);
905 igmp_create_group_timer (0, sw_if_index, new_group->key,
906 igmp_send_state_changed);
907 /* remove source form mode_is_filter_include group */
908 hash_unset_mem (group->igmp_src_by_key, src->key);
909 clib_mem_free (src->key);
910 pool_put (group->srcs, src);
912 if (group->n_srcs <= 0)
913 igmp_clear_group (config, group);
914 if (pool_elts (config->groups) <= 0)
915 igmp_clear_config (config);
940 /** \brief igmp hardware interface link up down
941 @param vnm - vnet main
942 @param hw_if_index - interface hw_if_index
943 @param flags - hw interface flags
945 If an interface goes down, remove its (S,G)s.
947 static clib_error_t *
948 igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
950 igmp_main_t *im = &igmp_main;
951 igmp_config_t *config;
952 clib_error_t *error = NULL;
953 /* remove igmp from a down interface to prevent crashes... */
955 igmp_config_lookup (im,
956 vnet_get_hw_interface (vnm,
957 hw_if_index)->sw_if_index);
960 if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
961 igmp_clear_config (config);
966 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down);
968 /** \brief igmp initialization
969 @param vm - vlib main
971 initialize igmp plugin. Initialize igmp_main, set mfib to allow igmp traffic.
973 static clib_error_t *
974 igmp_init (vlib_main_t * vm)
977 igmp_main_t *im = &igmp_main;
978 vlib_thread_main_t *tm = vlib_get_thread_main ();
980 if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
982 im->igmp_config_by_sw_if_index =
983 hash_create_mem (0, sizeof (u32), sizeof (uword));
984 im->igmp_api_client_by_client_index =
985 hash_create_mem (0, sizeof (u32), sizeof (uword));
986 vec_validate_aligned (im->buffers, tm->n_vlib_mains - 1,
987 CLIB_CACHE_LINE_BYTES);
988 ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
989 igmp_type_info_t *ti;
990 igmp_report_type_info_t *rti;
991 #define igmp_type(n,s) \
993 vec_add2 (im->type_infos, ti, 1); \
995 ti->name = (u8 *) #s; \
997 #define igmp_report_type(n,s) \
999 vec_add2 (im->report_type_infos, rti, 1); \
1001 rti->name = (u8 *) #s; \
1005 #undef igmp_report_type
1006 for (i = 0; i < vec_len (im->type_infos); i++)
1008 ti = im->type_infos + i;
1009 hash_set (im->type_info_by_type, ti->type, i);
1012 for (i = 0; i < vec_len (im->report_type_infos); i++)
1014 rti = im->report_type_infos + i;
1015 hash_set (im->report_type_info_by_report_type, rti->type, i);
1018 /* General Query address */
1019 ip46_address_t addr0 = {
1023 addr0.ip4.as_u32 = clib_host_to_net_u32 (IGMP_GENERAL_QUERY_ADDRESS);
1025 /* Report address */
1026 ip46_address_t addr1 = {
1030 addr1.ip4.as_u32 = clib_host_to_net_u32 (IGMP_MEMBERSHIP_REPORT_ADDRESS);
1032 fib_route_path_t path = {
1033 .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
1034 .frp_addr = zero_addr,
1035 .frp_sw_if_index = 0xffffffff,
1038 .frp_flags = FIB_ROUTE_PATH_LOCAL,
1041 const mfib_prefix_t mpfx0 = {
1042 .fp_proto = FIB_PROTOCOL_IP4,
1044 .fp_grp_addr = addr0,
1047 const mfib_prefix_t mpfx1 = {
1048 .fp_proto = FIB_PROTOCOL_IP4,
1050 .fp_grp_addr = addr1,
1053 /* configure MFIB to accept IGMPv3 general query
1054 * and reports from all interfaces
1056 mfib_table_entry_path_update (0, &mpfx0,
1057 MFIB_SOURCE_DEFAULT_ROUTE, &path,
1058 MFIB_ITF_FLAG_FORWARD);
1059 mfib_table_entry_path_update (0, &mpfx1,
1060 MFIB_SOURCE_DEFAULT_ROUTE, &path,
1061 MFIB_ITF_FLAG_FORWARD);
1062 mfib_table_entry_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE,
1063 0, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
1064 mfib_table_entry_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE,
1065 0, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
1069 VLIB_INIT_FUNCTION (igmp_init);
1071 VLIB_PLUGIN_REGISTER () = {
1072 .version = VPP_BUILD_VER,
1073 .description = "IGMP messaging",
1078 * fd.io coding-style-patch-verification: ON
1081 * eval: (c-set-style "gnu")