vxlan:use bihash_16_8 for ipv4 lookup
[vpp.git] / src / plugins / igmp / 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 <vlibmemory/api.h>
20 #include <vnet/plugin/plugin.h>
21 #include <vpp/app/version.h>
22 #include <vnet/ip/ip.h>
23 #include <vlib/unix/unix.h>
24 #include <vnet/adj/adj_mcast.h>
25
26 #include <igmp/igmp.h>
27 #include <igmp/igmp_pkt.h>
28 #include <igmp/igmp_query.h>
29 #include <igmp/igmp_report.h>
30 #include <igmp/igmp_error.h>
31
32 #include <limits.h>
33
34 typedef enum
35 {
36   IGMP_INPUT_NEXT_DROP,
37   IGMP_INPUT_NEXT_PARSE_QUERY,
38   IGMP_INPUT_NEXT_PARSE_REPORT,
39   IGMP_INPUT_N_NEXT,
40 } igmp_input_next_t;
41
42 typedef enum
43 {
44   IGMP_PARSE_QUERY_NEXT_DROP,
45   IGMP_PARSE_QUERY_N_NEXT,
46 } igmp_parse_query_next_t;
47
48 typedef enum
49 {
50   IGMP_PARSE_REPORT_NEXT_DROP,
51   IGMP_PARSE_REPORT_N_NEXT,
52 } igmp_parse_report_next_t;
53
54 char *igmp_error_strings[] = {
55 #define _(sym,string) string,
56   foreach_igmp_error
57 #undef _
58 };
59
60 typedef struct
61 {
62   u32 next_index;
63   u32 sw_if_index;
64
65   u8 packet_data[64];
66 } igmp_input_trace_t;
67
68 static u8 *
69 format_igmp_input_trace (u8 * s, va_list * va)
70 {
71   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
72   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
73   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
74
75   s = format (s, "sw_if_index %u next-index %u",
76               t->sw_if_index, t->next_index);
77   s = format (s, "\n%U", format_igmp_header, t->packet_data,
78               sizeof (t->packet_data));
79   return s;
80 }
81
82 static u8 *
83 format_igmp_parse_report_trace (u8 * s, va_list * va)
84 {
85   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
86   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
87   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
88
89   s = format (s, "sw_if_index %u next-index %u",
90               t->sw_if_index, t->next_index);
91   s = format (s, "\n%U", format_igmp_report_v3, t->packet_data,
92               sizeof (t->packet_data));
93   return s;
94 }
95
96 static u8 *
97 format_igmp_parse_query_trace (u8 * s, va_list * va)
98 {
99   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
100   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
101   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
102
103   s = format (s, "sw_if_index %u next-input %u",
104               t->sw_if_index, t->next_index);
105   s = format (s, "\n%U", format_igmp_query_v3, t->packet_data,
106               sizeof (t->packet_data));
107   return s;
108 }
109
110 static uword
111 igmp_input (vlib_main_t * vm, vlib_node_runtime_t * node,
112             vlib_frame_t * frame)
113 {
114   igmp_parse_query_next_t next_index;
115   u32 n_left_from, *from, *to_next;
116   vlib_node_runtime_t *error_node;
117   u8 error;
118
119   error = IGMP_ERROR_NONE;
120   error_node = node;
121
122   from = vlib_frame_vector_args (frame);
123   n_left_from = frame->n_vectors;
124   next_index = node->cached_next_index;
125
126   while (n_left_from > 0)
127     {
128       u32 n_left_to_next;
129
130       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
131
132       while (n_left_from > 0 && n_left_to_next > 0)
133         {
134           igmp_header_t *igmp;
135           u16 checksum, csum;
136           vlib_buffer_t *b;
137           ip4_header_t *ip;
138           ip_csum_t sum;
139           u32 bi, next;
140
141           next = IGMP_INPUT_NEXT_DROP;
142           bi = from[0];
143           to_next[0] = bi;
144           from++;
145           to_next++;
146           n_left_from--;
147           n_left_to_next--;
148
149           b = vlib_get_buffer (vm, bi);
150           ip = vlib_buffer_get_current (b);
151
152           if (ip->protocol != IP_PROTOCOL_IGMP)
153             {
154               error = IGMP_ERROR_INVALID_PROTOCOL;
155               next = IGMP_INPUT_NEXT_DROP;
156               goto next_buffer;
157             }
158
159           vlib_buffer_advance (b, ip4_header_bytes (ip));
160
161           igmp = vlib_buffer_get_current (b);
162
163           checksum = igmp->checksum;
164           igmp->checksum = 0;
165           sum = ip_incremental_checksum (0, igmp,
166                                          clib_net_to_host_u16 (ip->length) -
167                                          ip4_header_bytes (ip));
168           igmp->checksum = checksum;
169           csum = ~ip_csum_fold (sum);
170           if (checksum != csum)
171             {
172               error = IGMP_ERROR_BAD_CHECKSUM;
173               next = IGMP_INPUT_NEXT_DROP;
174               goto next_buffer;
175             }
176           if (!igmp_config_lookup (vnet_buffer (b)->sw_if_index[VLIB_RX]))
177             {
178               error = IGMP_ERROR_NOT_ENABLED;
179               next = IGMP_INPUT_NEXT_DROP;
180               goto next_buffer;
181             }
182
183           /* TODO: IGMPv2 and IGMPv1 */
184           switch (igmp->type)
185             {
186             case IGMP_TYPE_membership_query:
187               next = IGMP_INPUT_NEXT_PARSE_QUERY;
188               break;
189             case IGMP_TYPE_membership_report_v3:
190               next = IGMP_INPUT_NEXT_PARSE_REPORT;
191               break;
192             default:
193               error = IGMP_ERROR_UNKNOWN_TYPE;
194               next = IGMP_INPUT_NEXT_DROP;
195               break;
196             }
197         next_buffer:
198           b->error = error_node->errors[error];
199
200           if (node->flags & VLIB_NODE_FLAG_TRACE)
201             {
202               igmp_input_trace_t *tr;
203               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
204               tr->next_index = next;
205               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
206               clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
207                            sizeof (tr->packet_data));
208             }
209
210           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
211                                            n_left_to_next, bi, next);
212         }
213       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
214     }
215
216   return frame->n_vectors;
217 }
218
219 /* *INDENT-OFF* */
220 VLIB_REGISTER_NODE (igmp_input_node) =
221 {
222   .function = igmp_input,
223   .name = "igmp-input",
224   .vector_size = sizeof (u32),
225
226   .format_buffer = format_igmp_header,
227   .format_trace = format_igmp_input_trace,
228
229   .n_errors = IGMP_N_ERROR,
230   .error_strings = igmp_error_strings,
231
232   .n_next_nodes = IGMP_INPUT_N_NEXT,
233   .next_nodes = {
234       [IGMP_INPUT_NEXT_DROP] = "error-drop",
235       [IGMP_INPUT_NEXT_PARSE_QUERY] = "igmp-parse-query",
236       [IGMP_INPUT_NEXT_PARSE_REPORT] = "igmp-parse-report",
237   }
238 };
239 /* *INDENT-ON* */
240
241 static uword
242 igmp_parse_query (vlib_main_t * vm, vlib_node_runtime_t * node,
243                   vlib_frame_t * frame)
244 {
245   u32 n_left_from, *from, *to_next;
246   igmp_parse_query_next_t next_index;
247
248   from = vlib_frame_vector_args (frame);
249   n_left_from = frame->n_vectors;
250   next_index = node->cached_next_index;
251
252   while (n_left_from > 0)
253     {
254       u32 n_left_to_next;
255
256       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
257
258       while (n_left_from > 0 && n_left_to_next > 0)
259         {
260           igmp_membership_query_v3_t *igmp;
261           igmp_query_args_t *args;
262           vlib_buffer_t *b;
263           u32 bi, next;
264
265           next = IGMP_PARSE_QUERY_NEXT_DROP;
266           bi = from[0];
267           to_next[0] = bi;
268           from++;
269           to_next++;
270           n_left_from--;
271           n_left_to_next--;
272
273           b = vlib_get_buffer (vm, bi);
274           igmp = vlib_buffer_get_current (b);
275           ASSERT (igmp->header.type == IGMP_TYPE_membership_query);
276
277           if (node->flags & VLIB_NODE_FLAG_TRACE)
278             {
279               igmp_input_trace_t *tr;
280               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
281               tr->next_index = next;
282               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
283               clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
284                            sizeof (tr->packet_data));
285             }
286
287           /*
288            * copy the contents of the query, and the interface, over
289            * to the main thread for processing
290            */
291           vlib_buffer_advance (b, -sizeof (u32));
292           args = vlib_buffer_get_current (b);
293           args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
294
295           vl_api_rpc_call_main_thread (igmp_handle_query,
296                                        (u8 *) args,
297                                        sizeof (*args) +
298                                        igmp_membership_query_v3_length
299                                        (igmp));
300
301           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
302                                            n_left_to_next, bi, next);
303         }
304       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
305     }
306
307   return frame->n_vectors;
308 }
309
310 /* *INDENT-OFF* */
311 VLIB_REGISTER_NODE (igmp_parse_query_node) =
312 {
313   .function = igmp_parse_query,
314   .name = "igmp-parse-query",
315   .vector_size = sizeof (u32),
316
317   .format_buffer = format_igmp_query_v3,
318   .format_trace = format_igmp_parse_query_trace,
319
320   .n_errors = IGMP_N_ERROR,
321   .error_strings = igmp_error_strings,
322
323   .n_next_nodes = IGMP_PARSE_QUERY_N_NEXT,
324   .next_nodes = {
325     [IGMP_PARSE_QUERY_NEXT_DROP] = "error-drop",
326   }
327 };
328 /* *INDENT-ON* */
329
330 static uword
331 igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
332                    vlib_frame_t * frame)
333 {
334   u32 n_left_from, *from, *to_next;
335   igmp_input_next_t next_index;
336   vlib_node_runtime_t *error_node =
337     vlib_node_get_runtime (vm, igmp_input_node.index);
338   u8 error;
339
340   from = vlib_frame_vector_args (frame);
341   n_left_from = frame->n_vectors;
342   next_index = node->cached_next_index;
343
344   while (n_left_from > 0)
345     {
346       u32 n_left_to_next;
347
348       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
349
350       while (n_left_from > 0 && n_left_to_next > 0)
351         {
352           igmp_membership_report_v3_t *igmp;
353           igmp_report_args_t *args;
354           u32 bi, next;
355           vlib_buffer_t *b;
356
357           next = IGMP_PARSE_REPORT_NEXT_DROP;
358
359           bi = from[0];
360           to_next[0] = bi;
361           from++;
362           to_next++;
363           n_left_from--;
364           n_left_to_next--;
365
366           b = vlib_get_buffer (vm, bi);
367
368           error = IGMP_ERROR_NONE;
369           b->error = error_node->errors[error];
370           igmp = vlib_buffer_get_current (b);
371
372           ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
373
374           if (node->flags & VLIB_NODE_FLAG_TRACE)
375             {
376               igmp_input_trace_t *tr;
377               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
378               tr->next_index = next;
379               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
380               clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
381                            sizeof (tr->packet_data));
382             }
383
384           /*
385            * copy the contents of the query, and the interface, over
386            * to the main thread for processing
387            */
388           vlib_buffer_advance (b, -sizeof (u32));
389           args = vlib_buffer_get_current (b);
390           args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
391
392           vl_api_rpc_call_main_thread (igmp_handle_report,
393                                        (u8 *) args,
394                                        sizeof (*args) +
395                                        igmp_membership_report_v3_length
396                                        (igmp));
397
398           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
399                                            n_left_to_next, bi, next);
400         }
401       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
402     }
403
404   return frame->n_vectors;
405 }
406
407 /* *INDENT-OFF* */
408 VLIB_REGISTER_NODE (igmp_parse_report_node) =
409 {
410   .function = igmp_parse_report,
411   .name = "igmp-parse-report",
412   .vector_size = sizeof (u32),
413
414   .format_buffer = format_igmp_report_v3,
415   .format_trace = format_igmp_parse_report_trace,
416
417   .n_errors = IGMP_N_ERROR,
418   .error_strings = igmp_error_strings,
419
420   .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
421   .next_nodes = {
422     [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
423   }
424 };
425 /* *INDENT-ON* */
426
427 static clib_error_t *
428 igmp_input_init (vlib_main_t * vm)
429 {
430   clib_error_t *error;
431
432   if ((error = vlib_call_init_function (vm, igmp_init)))
433     return error;
434
435   ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
436
437   IGMP_DBG ("input-initialized");
438
439   return (error);
440 }
441
442 VLIB_INIT_FUNCTION (igmp_input_init);
443
444 /*
445  * fd.io coding-style-patch-verification: ON
446  *
447  * Local Variables:
448  * eval: (c-set-style "gnu")
449  * End:
450  */