vxlan:use bihash_16_8 for ipv4 lookup
[vpp.git] / src / plugins / igmp / input.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 <vlib/unix/unix.h>
23 #include <vnet/adj/adj_mcast.h>
24
25 #include <igmp/igmp.h>
26 #include <igmp/error.h>
27
28 #include <limits.h>
29
30 /* TODO: mld...
31 typedef enum
32 {
33   MLD_INPUT_NEXT_DROP,
34   ...
35 } mld_input_next_t;
36 */
37
38 typedef enum
39 {
40   IGMP_INPUT_NEXT_DROP,
41   IGMP_INPUT_NEXT_PARSE_QUERY,
42   IGMP_INPUT_NEXT_PARSE_REPORT,
43   IGMP_INPUT_N_NEXT,
44 } igmp_input_next_t;
45
46 typedef enum
47 {
48   IGMP_PARSE_QUERY_NEXT_DROP,
49   IGMP_PARSE_QUERY_N_NEXT,
50 } igmp_parse_query_next_t;
51
52 typedef enum
53 {
54   IGMP_PARSE_REPORT_NEXT_DROP,
55   IGMP_PARSE_REPORT_N_NEXT,
56 } igmp_parse_report_next_t;
57
58 char *igmp_error_strings[] = {
59 #define _(sym,string) string,
60   foreach_igmp_error
61 #undef _
62 };
63
64 typedef struct
65 {
66   u32 next_index;
67   u32 sw_if_index;
68
69   u8 packet_data[64];
70 } igmp_input_trace_t;
71
72 static u8 *
73 format_igmp_input_trace (u8 * s, va_list * va)
74 {
75   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
76   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
77   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
78
79   s =
80     format (s, "sw_if_index %u next-index %u", t->sw_if_index, t->next_index);
81   s =
82     format (s, "\n%U", format_igmp_header, t->packet_data,
83             sizeof (t->packet_data));
84   return s;
85 }
86
87 static u8 *
88 format_igmp_parse_report_trace (u8 * s, va_list * va)
89 {
90   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
91   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
92   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
93
94   s =
95     format (s, "sw_if_index %u next-index %u", t->sw_if_index, t->next_index);
96   s =
97     format (s, "\n%U", format_igmp_report_v3, t->packet_data,
98             sizeof (t->packet_data));
99   return s;
100 }
101
102 static u8 *
103 format_igmp_parse_query_trace (u8 * s, va_list * va)
104 {
105   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
106   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
107   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
108
109   s =
110     format (s, "sw_if_index %u next-input %u", t->sw_if_index, t->next_index);
111   s =
112     format (s, "\n%U", format_igmp_query_v3, t->packet_data,
113             sizeof (t->packet_data));
114   return s;
115 }
116
117 uword
118 igmp_input (vlib_main_t * vm, vlib_node_runtime_t * node,
119             vlib_frame_t * frame)
120 {
121   IGMP_DBG ("IGMP_INPUT");
122   u32 n_left_from, *from, *to_next;
123   igmp_parse_query_next_t next_index;
124   vlib_node_runtime_t *error_node =
125     vlib_node_get_runtime (vm, igmp_input_node.index);
126   u8 error;
127   ip_csum_t sum;
128   u16 csum;
129
130   error = IGMP_ERROR_NONE;
131
132   from = vlib_frame_vector_args (frame);
133   n_left_from = frame->n_vectors;
134   next_index = node->cached_next_index;
135
136   while (n_left_from > 0)
137     {
138       u32 n_left_to_next;
139
140       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
141
142       while (n_left_from > 0 && n_left_to_next > 0)
143         {
144           vlib_buffer_t *b;
145           ip4_header_t *ip;
146           u32 bi, next;
147           next = IGMP_INPUT_NEXT_DROP;
148
149           bi = from[0];
150           to_next[0] = bi;
151           from++;
152           to_next++;
153           n_left_from--;
154           n_left_to_next--;
155
156           b = vlib_get_buffer (vm, bi);
157           ip = vlib_buffer_get_current (b);
158
159           if (ip->protocol != 2)
160             {
161               error = IGMP_ERROR_INVALID_PROTOCOL;
162               next = IGMP_INPUT_NEXT_DROP;
163               goto next_buffer;
164             }
165
166           vlib_buffer_advance (b, ip4_header_bytes (ip));
167
168           igmp_header_t *igmp = vlib_buffer_get_current (b);
169
170           u16 checksum = igmp->checksum;
171           igmp->checksum = 0;
172           sum = ip_incremental_checksum (0, igmp,
173                                          clib_net_to_host_u16 (ip->length) -
174                                          ip4_header_bytes (ip));
175           igmp->checksum = checksum;
176           csum = ~ip_csum_fold (sum);
177           if (checksum != csum)
178             {
179               error = IGMP_ERROR_BAD_CHECKSUM;
180               next = IGMP_INPUT_NEXT_DROP;
181               goto next_buffer;
182             }
183
184           /* TODO: IGMPv2 and IGMPv1 */
185           switch (igmp->type)
186             {
187             case IGMP_TYPE_membership_query:
188               next = IGMP_INPUT_NEXT_PARSE_QUERY;
189               break;
190             case IGMP_TYPE_membership_report_v3:
191               next = IGMP_INPUT_NEXT_PARSE_REPORT;
192               break;
193             default:
194               error = IGMP_ERROR_UNKNOWN_TYPE;
195               next = IGMP_INPUT_NEXT_DROP;
196               break;
197             }
198         next_buffer:
199           b->error = error_node->errors[error];
200
201           if (node->flags & VLIB_NODE_FLAG_TRACE)
202             {
203               igmp_input_trace_t *tr;
204               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
205               tr->next_index = next;
206               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
207               clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
208                            sizeof (tr->packet_data));
209             }
210
211           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
212                                            n_left_to_next, bi, next);
213         }
214       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
215     }
216
217   return frame->n_vectors;
218 }
219
220 /* *INDENT-OFF* */
221 VLIB_REGISTER_NODE (igmp_input_node) =
222 {
223   .function = igmp_input,
224   .name = "igmp-input",
225   .vector_size = sizeof (u32),
226
227   .format_buffer = format_igmp_header,
228   .format_trace = format_igmp_input_trace,
229
230   .n_errors = IGMP_N_ERROR,
231   .error_strings = igmp_error_strings,
232
233   .n_next_nodes = IGMP_INPUT_N_NEXT,
234   .next_nodes = {
235       [IGMP_INPUT_NEXT_DROP] = "error-drop",
236       [IGMP_INPUT_NEXT_PARSE_QUERY] = "igmp-parse-query",
237       [IGMP_INPUT_NEXT_PARSE_REPORT] = "igmp-parse-report",
238   }
239 };
240 /* *INDENT-ON* */
241
242 uword
243 igmp_parse_query (vlib_main_t * vm, vlib_node_runtime_t * node,
244                   vlib_frame_t * frame)
245 {
246   IGMP_DBG ("IGMP_PARSE_QUERY");
247
248   u32 n_left_from, *from, *to_next;
249   igmp_parse_query_next_t next_index;
250   igmp_main_t *im = &igmp_main;
251   igmp_config_t *config;
252
253   from = vlib_frame_vector_args (frame);
254   n_left_from = frame->n_vectors;
255   next_index = node->cached_next_index;
256
257   while (n_left_from > 0)
258     {
259       u32 n_left_to_next;
260
261       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
262
263       while (n_left_from > 0 && n_left_to_next > 0)
264         {
265           vlib_buffer_t *b;
266           u32 sw_if_index, bi, next;
267           next = IGMP_PARSE_QUERY_NEXT_DROP;
268
269           bi = from[0];
270           to_next[0] = bi;
271           from++;
272           to_next++;
273           n_left_from--;
274           n_left_to_next--;
275
276           b = vlib_get_buffer (vm, bi);
277           sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
278
279           igmp_membership_query_v3_t *igmp = vlib_buffer_get_current (b);
280           ASSERT (igmp->header.type == IGMP_TYPE_membership_query);
281
282           /* if group address is zero, this is a general query */
283           if (igmp->dst.as_u32 == 0)
284             {
285               config = igmp_config_lookup (im, sw_if_index);
286               if (!config)
287                 {
288                   IGMP_DBG ("No config on interface %u", sw_if_index);
289                 }
290               else
291                 {
292                   /* WIP
293                    *
294                    * TODO: divide to multipe reports in random time range [now, max resp time]
295                    */
296                   u32 seed = vlib_time_now (vm);
297                   f64 next_resp_time = random_f64 (&seed) *
298                     (f64) (igmp->header.code / 10) + vlib_time_now (vm);
299                   config->flags |= IGMP_CONFIG_FLAG_CAN_SEND_REPORT;
300                   igmp_create_int_timer (next_resp_time, sw_if_index,
301                                          igmp_send_report);
302                   vlib_process_signal_event (vm,
303                                              igmp_timer_process_node.index,
304                                              IGMP_PROCESS_EVENT_UPDATE_TIMER,
305                                              0);
306                 }
307             }
308
309           if (node->flags & VLIB_NODE_FLAG_TRACE)
310             {
311               igmp_input_trace_t *tr;
312               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
313               tr->next_index = next;
314               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
315               clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
316                            sizeof (tr->packet_data));
317             }
318           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
319                                            n_left_to_next, bi, next);
320         }
321       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
322     }
323
324   return frame->n_vectors;
325 }
326
327 /* *INDENT-OFF* */
328 VLIB_REGISTER_NODE (igmp_parse_query_node) =
329 {
330   .function = igmp_parse_query,
331   .name = "igmp-parse-query",
332   .vector_size = sizeof (u32),
333
334   .format_buffer = format_igmp_query_v3,
335   .format_trace = format_igmp_parse_query_trace,
336
337   .n_errors = IGMP_N_ERROR,
338   .error_strings = igmp_error_strings,
339
340   .n_next_nodes = IGMP_PARSE_QUERY_N_NEXT,
341   .next_nodes = {
342     [IGMP_PARSE_QUERY_NEXT_DROP] = "error-drop",
343   }
344 };
345 /* *INDENT-ON* */
346
347 uword
348 igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
349                    vlib_frame_t * frame)
350 {
351   IGMP_DBG ("IGMP_PARSE_REPORT");
352
353   igmp_main_t *im = &igmp_main;
354   u32 n_left_from, *from, *to_next;
355   igmp_input_next_t next_index;
356   igmp_config_t *config;
357   igmp_group_t *group;
358   igmp_src_t *src;
359   igmp_membership_group_v3_t *igmp_group;
360   ip4_address_t *src_addr;
361   igmp_key_t gkey;
362   igmp_key_t skey;
363   memset (&gkey, 0, sizeof (igmp_key_t));
364   memset (&skey, 0, sizeof (igmp_key_t));
365   ip46_address_t saddr;
366   memset (&saddr, 0, sizeof (ip46_address_t));
367   ip46_address_t gaddr;
368   memset (&gaddr, 0, sizeof (ip46_address_t));
369   u32 len;
370   vlib_node_runtime_t *error_node =
371     vlib_node_get_runtime (vm, igmp_input_node.index);
372   u8 error;
373
374   from = vlib_frame_vector_args (frame);
375   n_left_from = frame->n_vectors;
376   next_index = node->cached_next_index;
377
378   while (n_left_from > 0)
379     {
380       u32 n_left_to_next;
381
382       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
383
384       while (n_left_from > 0 && n_left_to_next > 0)
385         {
386           vlib_buffer_t *b;
387           u32 sw_if_index, bi, next;
388           next = IGMP_PARSE_REPORT_NEXT_DROP;
389
390           bi = from[0];
391           to_next[0] = bi;
392           from++;
393           to_next++;
394           n_left_from--;
395           n_left_to_next--;
396
397           b = vlib_get_buffer (vm, bi);
398
399           error = IGMP_ERROR_NONE;
400           b->error = error_node->errors[error];
401
402           sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
403
404           igmp_membership_report_v3_t *igmp = vlib_buffer_get_current (b);
405           ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
406           len = sizeof (igmp_membership_report_v3_t);
407
408           /* if interface (S,G)s were configured by CLI/API goto next frame */
409           config = igmp_config_lookup (im, sw_if_index);
410           if (config)
411             {
412               config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
413               if (config->flags & IGMP_CONFIG_FLAG_CLI_API_CONFIGURED)
414                 {
415                   IGMP_DBG ("Interface %u has (S,G)s configured by CLI/API",
416                             sw_if_index);
417                   error = IGMP_ERROR_CLI_API_CONFIG;
418                   b->error = error_node->errors[error];
419                   goto next_frame;
420                 }
421             }
422           IGMP_DBG ("interface %u", sw_if_index);
423           int i, j = 0;
424           for (i = 0; i < clib_net_to_host_u16 (igmp->n_groups); i++)
425             {
426               igmp_group = group_ptr (igmp, len);
427               src_addr = igmp_group->src_addresses;
428               if (igmp_group->type ==
429                   IGMP_MEMBERSHIP_GROUP_mode_is_filter_include)
430                 {
431                   ip46_address_set_ip4 ((ip46_address_t *) & gkey.data,
432                                         &igmp_group->dst_address);
433
434                   gkey.group_type =
435                     IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
436
437                   group = igmp_group_lookup (config, &gkey);
438                   if (group)
439                     {
440                       for (j = 0;
441                            j <
442                            clib_net_to_host_u16 (igmp_group->n_src_addresses);
443                            j++)
444                         {
445                           /* update (S,G) expiration timer */
446                           ip46_address_set_ip4 ((ip46_address_t *) &
447                                                 skey.data, src_addr);
448                           src = igmp_src_lookup (group, &skey);
449                           if (src)
450                             src->exp_time =
451                               vlib_time_now (vm) + IGMP_SRC_TIMER;
452                           src_addr++;
453                         }
454                     }
455                   else
456                     {
457                       j = clib_net_to_host_u16 (igmp_group->n_src_addresses);
458                     }
459                 }
460               else if (igmp_group->type ==
461                        IGMP_MEMBERSHIP_GROUP_mode_is_filter_exclude)
462                 {
463                   for (j = 0;
464                        j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
465                        j++)
466                     {
467                       /* nothing for now... */
468                       src_addr++;
469                     }
470                 }
471               else if (igmp_group->type ==
472                        IGMP_MEMBERSHIP_GROUP_change_to_filter_include)
473                 {
474                   for (j = 0;
475                        j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
476                        j++)
477                     {
478                       /* add new (S,G) to interface */
479                       saddr.ip4 = *src_addr;
480                       gaddr.ip4 = igmp_group->dst_address;
481                       igmp_listen (vm, 1, sw_if_index, saddr, gaddr, 0);
482                       src_addr++;
483                     }
484                 }
485               else if (igmp_group->type ==
486                        IGMP_MEMBERSHIP_GROUP_change_to_filter_exclude)
487                 {
488                   for (j = 0;
489                        j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
490                        j++)
491                     {
492                       /* remove (S,G) from interface */
493                       saddr.ip4 = *src_addr;
494                       gaddr.ip4 = igmp_group->dst_address;
495                       igmp_listen (vm, 0, sw_if_index, saddr, gaddr, 0);
496                       src_addr++;
497                     }
498                 }
499               else if (igmp_group->type ==
500                        IGMP_MEMBERSHIP_GROUP_allow_new_sources)
501                 {
502                   for (j = 0;
503                        j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
504                        j++)
505                     {
506                       /* nothing for now... */
507                       src_addr++;
508                     }
509                 }
510               else if (igmp_group->type ==
511                        IGMP_MEMBERSHIP_GROUP_block_old_sources)
512                 {
513                   for (j = 0;
514                        j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
515                        j++)
516                     {
517                       /* remove (S,G) from interface */
518                       saddr.ip4 = *src_addr;
519                       gaddr.ip4 = igmp_group->dst_address;
520                       igmp_listen (vm, 0, sw_if_index, saddr, gaddr, 0);
521                       src_addr++;
522                     }
523                 }
524               /*
525                * Unrecognized Record Type values MUST be silently ignored.
526                */
527
528               /* move ptr to next Group Record */
529               len +=
530                 sizeof (igmp_membership_group_v3_t) +
531                 (sizeof (ip4_address_t) * j);
532             }
533         next_frame:
534           if (node->flags & VLIB_NODE_FLAG_TRACE)
535             {
536               igmp_input_trace_t *tr;
537               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
538               tr->next_index = next;
539               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
540               clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
541                            sizeof (tr->packet_data));
542             }
543           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
544                                            n_left_to_next, bi, next);
545         }
546       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
547     }
548
549   return frame->n_vectors;
550 }
551
552 /* *INDENT-OFF* */
553 VLIB_REGISTER_NODE (igmp_parse_report_node) =
554 {
555   .function = igmp_parse_report,
556   .name = "igmp-parse-report",
557   .vector_size = sizeof (u32),
558
559   .format_buffer = format_igmp_report_v3,
560   .format_trace = format_igmp_parse_report_trace,
561
562   .n_errors = IGMP_N_ERROR,
563   .error_strings = igmp_error_strings,
564
565   .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
566   .next_nodes = {
567     [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
568   }
569 };
570 /* *INDENT-ON* */
571
572 /*
573  * fd.io coding-style-patch-verification: ON
574  *
575  * Local Variables:
576  * eval: (c-set-style "gnu")
577  * End:
578  */