IGMP plugin initialises the FIB/MFIB via ip4 module
[vpp.git] / src / plugins / igmp / igmp.c
1 /*
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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  *------------------------------------------------------------------
16  */
17
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>
28
29 #include <igmp/igmp.h>
30
31 #include <limits.h>
32 #include <float.h>
33
34 igmp_main_t igmp_main;
35
36 /* clear all (S,G)s on specified config and remove this config from pool */
37 void
38 igmp_clear_config (igmp_config_t * config)
39 {
40   igmp_main_t *im = &igmp_main;
41   igmp_sg_t *sg;
42
43   ASSERT (config);
44   /* *INDENT-OFF* */
45   pool_foreach (sg, config->sg, (
46     {
47       clib_mem_free (sg->key);
48     }));
49   /* *INDENT-ON* */
50   pool_free (config->sg);
51   hash_free (config->igmp_sg_by_key);
52
53   hash_unset_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index);
54   pool_put (im->configs, config);
55 }
56
57 /* sort igmp timers, so that the first to expire is at end */
58 int
59 igmp_timer_compare (const void *_a, const void *_b)
60 {
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);
65 }
66
67 void
68 igmp_sort_timers (igmp_timer_t * timers)
69 {
70   vlib_main_t *vm = vlib_get_main ();
71
72   qsort (timers, vec_len (timers), sizeof (igmp_timer_t), igmp_timer_compare);
73
74   vlib_process_signal_event (vm, igmp_timer_process_node.index,
75                              IGMP_PROCESS_EVENT_UPDATE_TIMER, 0);
76 }
77
78 /* create new per interface timer
79  *
80  * - delayed reports
81  * - query msg
82  * - query resp
83  */
84
85 void
86 igmp_create_int_timer (f64 time, u32 sw_if_index,
87                        igmp_timer_function_t * func)
88 {
89   igmp_main_t *im = &igmp_main;
90   igmp_timer_t *timer;
91
92   pool_get (im->timers, timer);
93   memset (timer, 0, sizeof (igmp_timer_t));
94   timer->func = func;
95   timer->exp_time = time;
96   timer->sw_if_index = sw_if_index;
97
98   igmp_sort_timers (im->timers);
99 }
100
101 void
102 igmp_create_sg_timer (f64 time, u32 sw_if_index, igmp_sg_key_t * key,
103                       igmp_timer_function_t * func)
104 {
105   igmp_main_t *im = &igmp_main;
106   igmp_timer_t *timer;
107
108   pool_get (im->timers, timer);
109   memset (timer, 0, sizeof (igmp_timer_t));
110   timer->func = func;
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));
116
117   igmp_sort_timers (im->timers);
118 }
119
120 /* get next timer to expire */
121 always_inline igmp_timer_t *
122 igmp_get_next_timer (igmp_main_t * im)
123 {
124   if (pool_elts (im->timers) > 0)
125     return vec_elt_at_index (im->timers, pool_elts (im->timers) - 1);
126   return NULL;
127 }
128
129 /*
130 static void
131 igmp_create_report_v2 (vlib_buffer_t * b, igmp_config_t * config)
132 {
133   ip_csum_t sum;
134   u16 csum;
135   igmp_main_t *im = &igmp_main;
136   igmp_sg_t *sg;
137
138   sg = vec_elt_at_index (config->sg, im->next_index.sg_index);
139
140   igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b));
141   memset (igmp, 0, sizeof (igmp_message_t));
142
143   clib_memcpy (&igmp->dst, &sg->gaddr.ip4, sizeof (ip4_address_t));
144   igmp->header.type =
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;
150
151   b->current_data += sizeof (igmp_message_t);
152   b->current_length += sizeof (igmp_message_t);
153 }
154 */
155
156 /* create IGMPv3 report with single (S,G)
157  * used to send state chenge reports
158  */
159 static void
160 igmp_create_report_v31 (vlib_buffer_t * b, igmp_config_t * config)
161 {
162   ip_csum_t sum;
163   u16 csum;
164   igmp_main_t *im = &igmp_main;
165   igmp_sg_t *sg;
166   u32 len = 0;
167
168   sg = vec_elt_at_index (config->sg, im->next_index.sg_index);
169
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));
174
175   igmp->header.type = IGMP_TYPE_membership_report_v3;
176   igmp->n_groups = clib_host_to_net_u16 (1);
177
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));
184
185   igmp->groups[0].n_src_addresses = clib_host_to_net_u16 (1);
186
187   len += sizeof (ip4_address_t);
188   clib_memcpy (&igmp->groups[0].src_addresses[0], &sg->saddr.ip4,
189                sizeof (ip4_address_t));
190
191   sum = ip_incremental_checksum (0, igmp, len);
192   csum = ~ip_csum_fold (sum);
193   igmp->header.checksum = csum;
194
195   b->current_data += len;
196   b->current_length += len;
197 }
198
199 u8
200 ip4_lookup (ip4_address_t * a, igmp_membership_report_v3_t * igmp, u16 n,
201             igmp_membership_group_v3_type_t type)
202 {
203   u16 i;
204   u8 rv = 0;
205   u32 l = sizeof (igmp_membership_report_v3_t);
206
207   for (i = 0; i < n; i++)
208     {
209       if ((!ip4_address_compare (a, &group_ptr (igmp, l)->dst_address)) &&
210           (type == group_ptr (igmp, l)->type))
211         {
212           rv = 1;
213           break;
214         }
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);
218     }
219
220   return rv;
221 }
222
223 /* create IGMPv3 report with all (S,G)s on config
224  * used to respond to general queries
225  */
226 static void
227 igmp_create_report_v32 (vlib_buffer_t * b, igmp_config_t * config)
228 {
229   ip_csum_t sum;
230   u16 csum;
231   igmp_sg_t *sg0, *sg1;
232   u32 len = 0;
233   u16 n_groups = 0, n_srcs = 0;
234   u32 grp_s = sizeof (igmp_membership_group_v3_t);
235
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));
240
241   igmp->header.type = IGMP_TYPE_membership_report_v3;
242
243 /* TODO: divide (S,G)s to multiple reports...
244  * - create report limited by <packet size|number of (S,G)s>?
245  * - save loop state
246  * - on next timer continue loop
247  * - case of new query -> reset loop
248  */
249   /* *INDENT-OFF* */
250   pool_foreach (sg0, config->sg, (
251     {
252       if (ip4_lookup (&sg0->gaddr.ip4, igmp, n_groups, sg0->group_type))
253         continue;
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;
257       len += grp_s;
258       n_srcs = 0;
259       pool_foreach (sg1, config->sg, (
260         {
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)))
263             {
264               clib_memcpy (group_ptr (igmp, len + sizeof (ip4_address_t) * n_srcs),
265                            &sg1->saddr.ip4, sizeof (ip4_address_t));
266               n_srcs++;
267             }
268         }));
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;
271       n_groups++;
272     }));
273   /* *INDENT-ON* */
274
275   igmp->n_groups = clib_host_to_net_u16 (n_groups);
276
277   sum = ip_incremental_checksum (0, igmp, len);
278   csum = ~ip_csum_fold (sum);
279   igmp->header.checksum = csum;
280
281   b->current_data += len;
282   b->current_length += len;
283 }
284
285 static void
286 igmp_create_general_query_v3 (vlib_buffer_t * b, igmp_config_t * config)
287 {
288   vlib_main_t *vm = vlib_get_main ();
289   ip_csum_t sum;
290   u16 csum;
291
292   igmp_message_t *igmp = (igmp_message_t *) (vlib_buffer_get_current (b));
293   memset (igmp, 0, sizeof (igmp_membership_query_v3_t));
294
295   igmp->header.type = IGMP_TYPE_membership_query;
296   igmp->header.code = 100;
297
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);
301
302   sum =
303     ip_incremental_checksum (0, igmp, sizeof (igmp_membership_query_v3_t));
304   csum = ~ip_csum_fold (sum);
305   igmp->header.checksum = csum;
306
307   b->current_data += sizeof (igmp_membership_query_v3_t);
308   b->current_length += sizeof (igmp_membership_query_v3_t);
309 }
310
311
312 static void
313 igmp_create_ip4 (vlib_buffer_t * b, igmp_config_t * config, u8 is_report)
314 {
315   ip_lookup_main_t *lm = &ip4_main.lookup_main;
316
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;
320   ip4->ttl = 1;
321   ip4->protocol = 2;
322   ip4->tos = 0xc0;
323
324   u32 if_add_index =
325     lm->if_address_pool_index_by_sw_if_index[config->sw_if_index];
326   if (PREDICT_TRUE (if_add_index != ~0))
327     {
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));
332     }
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;
337
338   b->current_data += ip4_header_bytes (ip4);
339   b->current_length += ip4_header_bytes (ip4);
340
341   config->next_create_msg (b, config);
342   ip4->length = clib_host_to_net_u16 (b->current_length);
343
344   ip4->checksum = ip4_header_checksum (ip4);
345 }
346
347 static void
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)
350 {
351   u32 thread_index = vlib_get_thread_index ();
352   u32 *to_next;
353   u32 next_index = IGMP_NEXT_IP4_REWRITE_MCAST_NODE;
354
355   u32 n_free_bufs = vec_len (im->buffers[thread_index]);
356   if (PREDICT_FALSE (n_free_bufs < 1))
357     {
358       vec_validate (im->buffers[thread_index], 1 + n_free_bufs - 1);
359       n_free_bufs +=
360         vlib_buffer_alloc (vm, &im->buffers[thread_index][n_free_bufs], 1);
361       _vec_len (im->buffers[thread_index]) = n_free_bufs;
362     }
363
364   u32 n_left_to_next;
365   u32 next0 = next_index;
366   vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
367
368   if (n_left_to_next > 0)
369     {
370       vlib_buffer_t *b = 0;
371       u32 bi = 0;
372
373       if (n_free_bufs)
374         {
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;
379           n_free_bufs--;
380           if (PREDICT_FALSE (n_free_bufs == 0))
381             {
382               n_free_bufs += vlib_buffer_alloc (vm,
383                                                 &im->buffers[thread_index]
384                                                 [n_free_bufs], 1);
385               _vec_len (im->buffers[thread_index]) = n_free_bufs;
386             }
387
388           b->current_data = 0;
389           b->current_length = 0;
390
391           igmp_create_ip4 (b, config, is_report);
392
393           b->current_data = 0;
394
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;
400         }
401
402       to_next[0] = bi;
403       to_next += 1;
404       n_left_to_next -= 1;
405
406       vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
407                                        n_left_to_next, bi, next0);
408     }
409   vlib_put_next_frame (vm, node, next_index, n_left_to_next);
410 }
411
412 void
413 igmp_send_query (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im,
414                  igmp_timer_t * timer)
415 {
416   igmp_config_t *config;
417
418   u32 sw_if_index = timer->sw_if_index;
419
420   pool_put (im->timers, timer);
421
422   config = igmp_config_lookup (im, sw_if_index);
423   if (!config)
424     return;
425
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);
429
430   igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER, sw_if_index,
431                          igmp_send_query);
432 }
433
434 void
435 igmp_query_resp_exp (vlib_main_t * vm, vlib_node_runtime_t * rt,
436                      igmp_main_t * im, igmp_timer_t * timer)
437 {
438   igmp_config_t *config;
439
440   u32 sw_if_index = timer->sw_if_index;
441
442   pool_put (im->timers, timer);
443
444   config = igmp_config_lookup (im, sw_if_index);
445   if (!config)
446     return;
447   /* if report not reveived in max resp time clear igmp on interface */
448   if ((config->flags & IGMP_CONFIG_FLAG_QUERY_RESP_RECVED) == 0)
449     {
450       igmp_clear_config (config);
451     }
452 }
453
454 void
455 igmp_send_report (vlib_main_t * vm, vlib_node_runtime_t * rt,
456                   igmp_main_t * im, igmp_timer_t * timer)
457 {
458   igmp_config_t *config;
459
460   u32 sw_if_index = timer->sw_if_index;
461
462   pool_put (im->timers, timer);
463
464   config = igmp_config_lookup (im, sw_if_index);
465   if (!config)
466     return;
467
468   if (config->flags & IGMP_CONFIG_FLAG_CAN_SEND_REPORT)
469     {
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;
475     }
476 }
477
478 void
479 igmp_send_state_changed (vlib_main_t * vm, vlib_node_runtime_t * rt,
480                          igmp_main_t * im, igmp_timer_t * timer)
481 {
482   igmp_config_t *config;
483   igmp_sg_t *sg;
484
485   pool_put (im->timers, timer);
486
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);
489
490   config->next_create_msg = igmp_create_report_v31;
491   igmp_send_msg (vm, rt, im, config, /* is_report */ 1);
492
493
494   if (sg->group_type == IGMP_MEMBERSHIP_GROUP_change_to_filter_include)
495     {
496       sg->group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
497     }
498   else if (sg->group_type == IGMP_MEMBERSHIP_GROUP_block_old_sources)
499     {
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)
505         {
506           hash_unset_mem (im->igmp_config_by_sw_if_index,
507                           &config->sw_if_index);
508           pool_put (im->configs, config);
509         }
510     }
511
512 }
513
514 void
515 igmp_sg_exp (vlib_main_t * vm, vlib_node_runtime_t * rt, igmp_main_t * im,
516              igmp_timer_t * timer)
517 {
518   igmp_config_t *config;
519   igmp_sg_t *sg;
520
521   igmp_sg_key_t *key = (igmp_sg_key_t *) timer->data;
522
523   config = igmp_config_lookup (im, timer->sw_if_index);
524   if (!config)
525     goto done;
526   sg = igmp_sg_lookup (config, key);
527   if (!sg)
528     goto done;
529
530   /* check if this timer is valid */
531   if (timer->exp_time != sg->exp_time)
532     {
533       timer->exp_time = sg->exp_time;
534       igmp_sort_timers (im->timers);
535       return;
536     }
537
538   /* source timer expired, remove (S,G) */
539   igmp_listen (vm, 0, timer->sw_if_index, key->saddr, key->gaddr, 0);
540
541 done:
542   pool_put (im->timers, timer);
543 }
544
545 static uword
546 igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
547                     vlib_frame_t * f)
548 {
549   igmp_main_t *im = &igmp_main;
550   uword *event_data = 0, event_type;
551   f64 time_start;
552   u8 enabled = 0;
553   igmp_timer_t *timer = NULL;
554
555   while (1)
556     {
557       if (enabled)
558         vlib_process_wait_for_event_or_clock (vm,
559                                               timer->exp_time - time_start);
560       else
561         vlib_process_wait_for_event (vm);
562
563       time_start = vlib_time_now (vm);
564
565       event_type = vlib_process_get_events (vm, &event_data);
566       vec_reset_length (event_data);
567
568       if (event_type == IGMP_PROCESS_EVENT_UPDATE_TIMER)
569         goto next_timer;
570
571       DBG ("time: %f", vlib_time_now (vm));
572
573       /* timer expired */
574       timer->func (vm, rt, im, timer);
575
576     next_timer:
577       timer = igmp_get_next_timer (im);
578       if (timer == NULL)
579         enabled = 0;
580       else
581         enabled = 1;
582     }
583   return 0;
584 }
585
586 /* *INDENT-OFF* */
587 VLIB_REGISTER_NODE (igmp_timer_process_node) =
588 {
589   .function = igmp_timer_process,
590   .type = VLIB_NODE_TYPE_PROCESS,
591   .name = "igmp-timer-process",
592   .n_next_nodes = IGMP_N_NEXT,
593   .next_nodes =  {
594     [IGMP_NEXT_IP4_REWRITE_MCAST_NODE] = "ip4-rewrite-mcast",
595     [IGMP_NEXT_IP6_REWRITE_MCAST_NODE] = "ip6-rewrite-mcast",
596   }
597 };
598 /* *INDENT-ON* */
599
600 int
601 igmp_listen (vlib_main_t * vm, u8 enable, u32 sw_if_index,
602              ip46_address_t saddr, ip46_address_t gaddr,
603              u8 cli_api_configured)
604 {
605   igmp_main_t *im = &igmp_main;
606   igmp_config_t *config;
607   igmp_sg_t *sg;
608   igmp_sg_key_t key;
609   int rv = 0;
610
611   /* set the lookup key */
612   clib_memcpy (&key.saddr, &saddr, sizeof (ip46_address_t));
613   clib_memcpy (&key.gaddr, &gaddr, sizeof (ip46_address_t));
614
615   if (enable)
616     {
617       config = igmp_config_lookup (im, sw_if_index);
618       if (!config)
619         {
620           pool_get (im->configs, config);
621           memset (config, 0, sizeof (igmp_config_t));
622           config->sw_if_index = sw_if_index;
623           config->igmp_sg_by_key =
624             hash_create_mem (0, sizeof (igmp_sg_key_t), sizeof (uword));
625           config->cli_api_configured = cli_api_configured;
626           /* use IGMPv3 by default */
627           config->igmp_ver = IGMP_V3;
628           config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
629           config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
630           if (!cli_api_configured)
631             {
632               igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER,
633                                      sw_if_index, igmp_send_query);
634             }
635           config->adj_index =
636             adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
637                                    config->sw_if_index);
638           hash_set_mem (im->igmp_config_by_sw_if_index, &config->sw_if_index,
639                         config - im->configs);
640         }
641       else if (config->cli_api_configured != cli_api_configured)
642         {
643           rv = -2;
644           goto error;
645         }
646       sg = igmp_sg_lookup (config, &key);
647       if (!sg)
648         {
649           pool_get (config->sg, sg);
650           memset (sg, 0, sizeof (igmp_sg_t));
651           sg->key = clib_mem_alloc (sizeof (igmp_sg_key_t));
652           clib_memcpy (sg->key, &key, sizeof (igmp_sg_key_t));
653           clib_memcpy (&sg->saddr, &saddr, sizeof (ip46_address_t));
654           clib_memcpy (&sg->gaddr, &gaddr, sizeof (ip46_address_t));
655           sg->group_type = IGMP_MEMBERSHIP_GROUP_change_to_filter_include;
656           if (cli_api_configured)
657             {
658               /* create state-changed report timer with zero timeout */
659               igmp_create_int_timer (0, sw_if_index, igmp_send_state_changed);
660             }
661           else
662             {
663               sg->group_type = IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
664               sg->exp_time = vlib_time_now (vm) + IGMP_SG_TIMER;
665               igmp_create_sg_timer (sg->exp_time, config->sw_if_index,
666                                     sg->key, igmp_sg_exp);
667               /* notify all registered api clients */
668               igmp_event (im, config, sg);
669             }
670           hash_set_mem (config->igmp_sg_by_key, sg->key, sg - config->sg);
671         }
672       else
673         {
674           rv = -1;
675           goto error;
676         }
677
678       im->next_index.config_index = config - im->configs;
679       im->next_index.sg_index = sg - config->sg;
680     }
681   else
682     {
683       config = igmp_config_lookup (im, sw_if_index);
684       if (config)
685         {
686           sg = igmp_sg_lookup (config, &key);
687           if (sg)
688             {
689               sg->group_type = IGMP_MEMBERSHIP_GROUP_block_old_sources;
690               im->next_index.config_index = config - im->configs;
691               im->next_index.sg_index = sg - config->sg;
692               /* notify all registered api clients */
693               if (!cli_api_configured)
694                 igmp_event (im, config, sg);
695               else
696                 igmp_create_int_timer (0, sw_if_index,
697                                        igmp_send_state_changed);
698             }
699           else
700             {
701               rv = -1;
702               goto error;
703             }
704         }
705       else
706         {
707           rv = -1;
708           goto error;
709         }
710     }
711
712 error:
713   return rv;
714 }
715
716 static clib_error_t *
717 igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
718 {
719   igmp_main_t *im = &igmp_main;
720   igmp_config_t *config;
721   clib_error_t *error = NULL;
722
723   /* remove igmp from a down interface to prevent crashes... */
724   config =
725     igmp_config_lookup (im,
726                         vnet_get_hw_interface (vnm,
727                                                hw_if_index)->sw_if_index);
728   if (config)
729     {
730       if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
731         igmp_clear_config (config);
732     }
733   return error;
734 }
735
736 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down);
737
738 static clib_error_t *
739 igmp_init (vlib_main_t * vm)
740 {
741   clib_error_t *error;
742   igmp_main_t *im = &igmp_main;
743   vlib_thread_main_t *tm = vlib_get_thread_main ();
744   int i;
745
746   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
747     return error;
748
749   im->igmp_config_by_sw_if_index =
750     hash_create_mem (0, sizeof (u32), sizeof (uword));
751   im->igmp_api_client_by_client_index =
752     hash_create_mem (0, sizeof (u32), sizeof (uword));
753
754   vec_validate_aligned (im->buffers, tm->n_vlib_mains - 1,
755                         CLIB_CACHE_LINE_BYTES);
756
757   ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
758
759   igmp_type_info_t *ti;
760   igmp_report_type_info_t *rti;
761 #define igmp_type(n,s)                          \
762 do {                                            \
763   vec_add2 (im->type_infos, ti, 1);             \
764   ti->type = n;                                 \
765   ti->name = (u8 *) #s;                         \
766 } while (0);
767
768 #define igmp_report_type(n,s)                           \
769 do {                                            \
770   vec_add2 (im->report_type_infos, rti, 1);             \
771   rti->type = n;                                        \
772   rti->name = (u8 *) #s;                                \
773 } while (0);
774
775 #include "igmp.def"
776
777 #undef igmp_type
778 #undef igmp_report_type
779
780   for (i = 0; i < vec_len (im->type_infos); i++)
781     {
782       ti = im->type_infos + i;
783       hash_set (im->type_info_by_type, ti->type, i);
784     }
785
786   for (i = 0; i < vec_len (im->report_type_infos); i++)
787     {
788       rti = im->report_type_infos + i;
789       hash_set (im->report_type_info_by_report_type, rti->type, i);
790     }
791
792   /* General Query address */
793   ip46_address_t addr0;
794   addr0.ip4.as_u8[0] = 224;
795   addr0.ip4.as_u8[1] = 0;
796   addr0.ip4.as_u8[2] = 0;
797   addr0.ip4.as_u8[3] = 1;
798   /* Report address */
799   ip46_address_t addr1;
800   addr1.ip4.as_u8[0] = 224;
801   addr1.ip4.as_u8[1] = 0;
802   addr1.ip4.as_u8[2] = 0;
803   addr1.ip4.as_u8[3] = 22;
804
805   fib_route_path_t path = {
806     .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
807     .frp_addr = zero_addr,
808     .frp_sw_if_index = 0xffffffff,
809     .frp_fib_index = 0,
810     .frp_weight = 0,
811     .frp_flags = FIB_ROUTE_PATH_LOCAL,
812   };
813   const mfib_prefix_t mpfx0 = {
814     .fp_proto = FIB_PROTOCOL_IP4,
815     .fp_len = 32,
816     .fp_grp_addr = addr0,
817   };
818   const mfib_prefix_t mpfx1 = {
819     .fp_proto = FIB_PROTOCOL_IP4,
820     .fp_len = 32,
821     .fp_grp_addr = addr1,
822   };
823   /* configure MFIB to accept IGMPv3 general query and reports from all interfaces */
824   mfib_table_entry_path_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE, &path,
825                                 MFIB_ITF_FLAG_FORWARD);
826   mfib_table_entry_path_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE, &path,
827                                 MFIB_ITF_FLAG_FORWARD);
828
829   mfib_table_entry_update (0, &mpfx0, MFIB_SOURCE_DEFAULT_ROUTE, 0,
830                            MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
831   mfib_table_entry_update (0, &mpfx1, MFIB_SOURCE_DEFAULT_ROUTE, 0,
832                            MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
833
834   return (error);
835 }
836
837 VLIB_INIT_FUNCTION (igmp_init);
838
839 /* *INDENT-OFF* */
840 VLIB_PLUGIN_REGISTER () = {
841     .version = VPP_BUILD_VER,
842     .description = "IGMP messaging",
843 };
844 /* *INDENT-ON* */
845
846 /*
847  * fd.io coding-style-patch-verification: ON
848  *
849  * Local Variables:
850  * eval: (c-set-style "gnu")
851  * End:
852  */