nat: use correct data types for memory sizes
[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   u32 len;
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 len %u",
104               t->sw_if_index, t->next_index, t->len);
105   s = format (s, "\n%U", format_igmp_query_v3, t->packet_data,
106               sizeof (t->packet_data));
107   s = format (s, "\n%U", format_hex_bytes,
108               t->packet_data, sizeof (t->packet_data));
109   return s;
110 }
111
112 static uword
113 igmp_input (vlib_main_t * vm, vlib_node_runtime_t * node,
114             vlib_frame_t * frame)
115 {
116   igmp_parse_query_next_t next_index;
117   u32 n_left_from, *from, *to_next;
118   vlib_node_runtime_t *error_node;
119   u8 error;
120
121   error = IGMP_ERROR_NONE;
122   error_node = node;
123
124   from = vlib_frame_vector_args (frame);
125   n_left_from = frame->n_vectors;
126   next_index = node->cached_next_index;
127
128   while (n_left_from > 0)
129     {
130       u32 n_left_to_next;
131
132       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
133
134       while (n_left_from > 0 && n_left_to_next > 0)
135         {
136           igmp_header_t *igmp;
137           u16 checksum, csum;
138           vlib_buffer_t *b;
139           ip4_header_t *ip;
140           ip_csum_t sum;
141           u32 bi, next;
142
143           next = IGMP_INPUT_NEXT_DROP;
144           bi = from[0];
145           to_next[0] = bi;
146           from++;
147           to_next++;
148           n_left_from--;
149           n_left_to_next--;
150
151           b = vlib_get_buffer (vm, bi);
152           ip = vlib_buffer_get_current (b);
153
154           if (ip->protocol != IP_PROTOCOL_IGMP)
155             {
156               error = IGMP_ERROR_INVALID_PROTOCOL;
157               next = IGMP_INPUT_NEXT_DROP;
158               goto next_buffer;
159             }
160
161           vlib_buffer_advance (b, ip4_header_bytes (ip));
162
163           igmp = vlib_buffer_get_current (b);
164
165           checksum = igmp->checksum;
166           igmp->checksum = 0;
167           sum = ip_incremental_checksum (0, igmp,
168                                          clib_net_to_host_u16 (ip->length) -
169                                          ip4_header_bytes (ip));
170           igmp->checksum = checksum;
171           csum = ~ip_csum_fold (sum);
172           if (checksum != csum)
173             {
174               error = IGMP_ERROR_BAD_CHECKSUM;
175               next = IGMP_INPUT_NEXT_DROP;
176               goto next_buffer;
177             }
178           if (!igmp_config_lookup (vnet_buffer (b)->sw_if_index[VLIB_RX]))
179             {
180               error = IGMP_ERROR_NOT_ENABLED;
181               next = IGMP_INPUT_NEXT_DROP;
182               goto next_buffer;
183             }
184
185           /* TODO: IGMPv2 and IGMPv1 */
186           switch (igmp->type)
187             {
188             case IGMP_TYPE_membership_query:
189               next = IGMP_INPUT_NEXT_PARSE_QUERY;
190               break;
191             case IGMP_TYPE_membership_report_v3:
192               next = IGMP_INPUT_NEXT_PARSE_REPORT;
193               break;
194             default:
195               error = IGMP_ERROR_UNKNOWN_TYPE;
196               next = IGMP_INPUT_NEXT_DROP;
197               break;
198             }
199         next_buffer:
200           b->error = error_node->errors[error];
201
202           if (node->flags & VLIB_NODE_FLAG_TRACE)
203             {
204               igmp_input_trace_t *tr;
205               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
206               tr->next_index = next;
207               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
208               tr->len = vlib_buffer_length_in_chain (vm, b);
209               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
210                                 sizeof (tr->packet_data));
211             }
212
213           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
214                                            n_left_to_next, bi, next);
215         }
216       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
217     }
218
219   return frame->n_vectors;
220 }
221
222 /* *INDENT-OFF* */
223 VLIB_REGISTER_NODE (igmp_input_node) =
224 {
225   .function = igmp_input,
226   .name = "igmp-input",
227   .vector_size = sizeof (u32),
228
229   .format_buffer = format_igmp_header,
230   .format_trace = format_igmp_input_trace,
231
232   .n_errors = IGMP_N_ERROR,
233   .error_strings = igmp_error_strings,
234
235   .n_next_nodes = IGMP_INPUT_N_NEXT,
236   .next_nodes = {
237       [IGMP_INPUT_NEXT_DROP] = "error-drop",
238       [IGMP_INPUT_NEXT_PARSE_QUERY] = "igmp-parse-query",
239       [IGMP_INPUT_NEXT_PARSE_REPORT] = "igmp-parse-report",
240   }
241 };
242 /* *INDENT-ON* */
243
244 static uword
245 igmp_parse_query (vlib_main_t * vm, vlib_node_runtime_t * node,
246                   vlib_frame_t * frame)
247 {
248   u32 n_left_from, *from, *to_next;
249   igmp_parse_query_next_t next_index;
250
251   from = vlib_frame_vector_args (frame);
252   n_left_from = frame->n_vectors;
253   next_index = node->cached_next_index;
254
255   while (n_left_from > 0)
256     {
257       u32 n_left_to_next;
258
259       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
260
261       while (n_left_from > 0 && n_left_to_next > 0)
262         {
263           igmp_membership_query_v3_t *igmp;
264           igmp_query_args_t *args;
265           u32 bi, next, len;
266           vlib_buffer_t *b;
267
268           next = IGMP_PARSE_QUERY_NEXT_DROP;
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           igmp = vlib_buffer_get_current (b);
278           ASSERT (igmp->header.type == IGMP_TYPE_membership_query);
279           len = igmp_membership_query_v3_length (igmp);
280
281           if (node->flags & VLIB_NODE_FLAG_TRACE)
282             {
283               igmp_input_trace_t *tr;
284               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
285               tr->next_index = next;
286               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
287               tr->len = len;
288               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
289                                 sizeof (tr->packet_data));
290             }
291
292           /*
293            * validate that the length on the packet on the wire  corresponds
294            * to at least the length of the calculated v3 query.
295            * If there's extra, then it will be ignored.
296            */
297           if (vlib_buffer_length_in_chain (vm, b) >= len)
298             {
299               /*
300                * copy the contents of the query, and the interface, over
301                * to the main thread for processing
302                */
303               vlib_buffer_advance (b, -sizeof (u32));
304               args = vlib_buffer_get_current (b);
305               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
306
307               vl_api_rpc_call_main_thread (igmp_handle_query,
308                                            (u8 *) args, sizeof (*args) + len);
309             }
310           else
311             {
312               /*
313                * else a packet that is reporting more sources than it really
314                * has; bin it
315                */
316               b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
317             }
318
319           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
320                                            n_left_to_next, bi, next);
321         }
322       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
323     }
324
325   return frame->n_vectors;
326 }
327
328 /* *INDENT-OFF* */
329 VLIB_REGISTER_NODE (igmp_parse_query_node) =
330 {
331   .function = igmp_parse_query,
332   .name = "igmp-parse-query",
333   .vector_size = sizeof (u32),
334
335   .format_buffer = format_igmp_query_v3,
336   .format_trace = format_igmp_parse_query_trace,
337
338   .n_errors = IGMP_N_ERROR,
339   .error_strings = igmp_error_strings,
340
341   .n_next_nodes = IGMP_PARSE_QUERY_N_NEXT,
342   .next_nodes = {
343     [IGMP_PARSE_QUERY_NEXT_DROP] = "error-drop",
344   }
345 };
346 /* *INDENT-ON* */
347
348 static uword
349 igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
350                    vlib_frame_t * frame)
351 {
352   u32 n_left_from, *from, *to_next;
353   igmp_input_next_t next_index;
354   vlib_node_runtime_t *error_node =
355     vlib_node_get_runtime (vm, igmp_input_node.index);
356   u8 error;
357
358   from = vlib_frame_vector_args (frame);
359   n_left_from = frame->n_vectors;
360   next_index = node->cached_next_index;
361
362   while (n_left_from > 0)
363     {
364       u32 n_left_to_next;
365
366       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
367
368       while (n_left_from > 0 && n_left_to_next > 0)
369         {
370           igmp_membership_report_v3_t *igmp;
371           igmp_report_args_t *args;
372           u32 bi, next, len;
373           vlib_buffer_t *b;
374
375           next = IGMP_PARSE_REPORT_NEXT_DROP;
376
377           bi = from[0];
378           to_next[0] = bi;
379           from++;
380           to_next++;
381           n_left_from--;
382           n_left_to_next--;
383
384           b = vlib_get_buffer (vm, bi);
385
386           error = IGMP_ERROR_NONE;
387           b->error = error_node->errors[error];
388           igmp = vlib_buffer_get_current (b);
389           len = igmp_membership_report_v3_length (igmp);
390
391           ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
392
393           if (node->flags & VLIB_NODE_FLAG_TRACE)
394             {
395               igmp_input_trace_t *tr;
396               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
397               tr->next_index = next;
398               tr->len = len;
399               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
400               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
401                                 sizeof (tr->packet_data));
402             }
403
404           /*
405            * validate that the length on the packet on the wire
406            * corresponds to the length on the calculated v3 query
407            */
408           if (vlib_buffer_length_in_chain (vm, b) >= len)
409             {
410               /*
411                * copy the contents of the query, and the interface, over
412                * to the main thread for processing
413                */
414               vlib_buffer_advance (b, -sizeof (u32));
415               args = vlib_buffer_get_current (b);
416               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
417
418               vl_api_rpc_call_main_thread (igmp_handle_report,
419                                            (u8 *) args, sizeof (*args) + len);
420             }
421           else
422             {
423               /*
424                * this is a packet with more groups/sources than the
425                * header reports. bin it
426                */
427               b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
428             }
429
430           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
431                                            n_left_to_next, bi, next);
432         }
433       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
434     }
435
436   return frame->n_vectors;
437 }
438
439 /* *INDENT-OFF* */
440 VLIB_REGISTER_NODE (igmp_parse_report_node) =
441 {
442   .function = igmp_parse_report,
443   .name = "igmp-parse-report",
444   .vector_size = sizeof (u32),
445
446   .format_buffer = format_igmp_report_v3,
447   .format_trace = format_igmp_parse_report_trace,
448
449   .n_errors = IGMP_N_ERROR,
450   .error_strings = igmp_error_strings,
451
452   .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
453   .next_nodes = {
454     [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
455   }
456 };
457 /* *INDENT-ON* */
458
459 static clib_error_t *
460 igmp_input_init (vlib_main_t * vm)
461 {
462   ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
463
464   IGMP_DBG ("input-initialized");
465
466   return (0);
467 }
468
469 /* *INDENT-OFF* */
470 VLIB_INIT_FUNCTION (igmp_input_init) =
471 {
472   .runs_after = VLIB_INITS("igmp_init"),
473 };
474 /* *INDENT-ON* */
475
476 /*
477  * fd.io coding-style-patch-verification: ON
478  *
479  * Local Variables:
480  * eval: (c-set-style "gnu")
481  * End:
482  */