igmp: fix debug macro
[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   igmp_timer_t *timer = NULL;
553
554   while (1)
555     {
556       if (NULL != timer)
557         vlib_process_wait_for_event_or_clock (vm,
558                                               timer->exp_time - time_start);
559       else
560         vlib_process_wait_for_event (vm);
561
562       time_start = vlib_time_now (vm);
563
564       event_type = vlib_process_get_events (vm, &event_data);
565       vec_reset_length (event_data);
566
567       if (event_type == IGMP_PROCESS_EVENT_UPDATE_TIMER)
568         goto next_timer;
569
570       IGMP_DBG ("time: %f", vlib_time_now (vm));
571
572       /* timer expired */
573       if (NULL != timer)
574         timer->func (vm, rt, im, timer);
575
576     next_timer:
577       timer = igmp_get_next_timer (im);
578     }
579   return 0;
580 }
581
582 /* *INDENT-OFF* */
583 VLIB_REGISTER_NODE (igmp_timer_process_node) =
584 {
585   .function = igmp_timer_process,
586   .type = VLIB_NODE_TYPE_PROCESS,
587   .name = "igmp-timer-process",
588   .n_next_nodes = IGMP_N_NEXT,
589   .next_nodes =  {
590     [IGMP_NEXT_IP4_REWRITE_MCAST_NODE] = "ip4-rewrite-mcast",
591     [IGMP_NEXT_IP6_REWRITE_MCAST_NODE] = "ip6-rewrite-mcast",
592   }
593 };
594 /* *INDENT-ON* */
595
596 int
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)
600 {
601   igmp_main_t *im = &igmp_main;
602   igmp_config_t *config;
603   igmp_sg_t *sg;
604   igmp_sg_key_t key;
605   int rv = 0;
606
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));
610
611   if (enable)
612     {
613       config = igmp_config_lookup (im, sw_if_index);
614       if (!config)
615         {
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)
627             {
628               igmp_create_int_timer (vlib_time_now (vm) + IGMP_QUERY_TIMER,
629                                      sw_if_index, igmp_send_query);
630             }
631           config->adj_index =
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);
636         }
637       else if (config->cli_api_configured != cli_api_configured)
638         {
639           rv = -2;
640           goto error;
641         }
642       sg = igmp_sg_lookup (config, &key);
643       if (!sg)
644         {
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)
653             {
654               /* create state-changed report timer with zero timeout */
655               igmp_create_int_timer (0, sw_if_index, igmp_send_state_changed);
656             }
657           else
658             {
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);
665             }
666           hash_set_mem (config->igmp_sg_by_key, sg->key, sg - config->sg);
667         }
668       else
669         {
670           rv = -1;
671           goto error;
672         }
673
674       im->next_index.config_index = config - im->configs;
675       im->next_index.sg_index = sg - config->sg;
676     }
677   else
678     {
679       config = igmp_config_lookup (im, sw_if_index);
680       if (config)
681         {
682           sg = igmp_sg_lookup (config, &key);
683           if (sg)
684             {
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);
691               else
692                 igmp_create_int_timer (0, sw_if_index,
693                                        igmp_send_state_changed);
694             }
695           else
696             {
697               rv = -1;
698               goto error;
699             }
700         }
701       else
702         {
703           rv = -1;
704           goto error;
705         }
706     }
707
708 error:
709   return rv;
710 }
711
712 static clib_error_t *
713 igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
714 {
715   igmp_main_t *im = &igmp_main;
716   igmp_config_t *config;
717   clib_error_t *error = NULL;
718
719   /* remove igmp from a down interface to prevent crashes... */
720   config =
721     igmp_config_lookup (im,
722                         vnet_get_hw_interface (vnm,
723                                                hw_if_index)->sw_if_index);
724   if (config)
725     {
726       if ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
727         igmp_clear_config (config);
728     }
729   return error;
730 }
731
732 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down);
733
734 static clib_error_t *
735 igmp_init (vlib_main_t * vm)
736 {
737   clib_error_t *error;
738   igmp_main_t *im = &igmp_main;
739   vlib_thread_main_t *tm = vlib_get_thread_main ();
740   int i;
741
742   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
743     return error;
744
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));
749
750   vec_validate_aligned (im->buffers, tm->n_vlib_mains - 1,
751                         CLIB_CACHE_LINE_BYTES);
752
753   ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
754
755   igmp_type_info_t *ti;
756   igmp_report_type_info_t *rti;
757 #define igmp_type(n,s)                          \
758 do {                                            \
759   vec_add2 (im->type_infos, ti, 1);             \
760   ti->type = n;                                 \
761   ti->name = (u8 *) #s;                         \
762 } while (0);
763
764 #define igmp_report_type(n,s)                           \
765 do {                                            \
766   vec_add2 (im->report_type_infos, rti, 1);             \
767   rti->type = n;                                        \
768   rti->name = (u8 *) #s;                                \
769 } while (0);
770
771 #include "igmp.def"
772
773 #undef igmp_type
774 #undef igmp_report_type
775
776   for (i = 0; i < vec_len (im->type_infos); i++)
777     {
778       ti = im->type_infos + i;
779       hash_set (im->type_info_by_type, ti->type, i);
780     }
781
782   for (i = 0; i < vec_len (im->report_type_infos); i++)
783     {
784       rti = im->report_type_infos + i;
785       hash_set (im->report_type_info_by_report_type, rti->type, i);
786     }
787
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;
794   /* Report address */
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;
800
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,
805     .frp_fib_index = 0,
806     .frp_weight = 0,
807     .frp_flags = FIB_ROUTE_PATH_LOCAL,
808   };
809   const mfib_prefix_t mpfx0 = {
810     .fp_proto = FIB_PROTOCOL_IP4,
811     .fp_len = 32,
812     .fp_grp_addr = addr0,
813   };
814   const mfib_prefix_t mpfx1 = {
815     .fp_proto = FIB_PROTOCOL_IP4,
816     .fp_len = 32,
817     .fp_grp_addr = addr1,
818   };
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);
824
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);
829
830   return (error);
831 }
832
833 VLIB_INIT_FUNCTION (igmp_init);
834
835 /* *INDENT-OFF* */
836 VLIB_PLUGIN_REGISTER () = {
837     .version = VPP_BUILD_VER,
838     .description = "IGMP messaging",
839 };
840 /* *INDENT-ON* */
841
842 /*
843  * fd.io coding-style-patch-verification: ON
844  *
845  * Local Variables:
846  * eval: (c-set-style "gnu")
847  * End:
848  */