vrrp: fix vrrp_garp_or_na_send()'s memory leak
[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 VLIB_REGISTER_NODE (igmp_input_node) =
223 {
224   .function = igmp_input,
225   .name = "igmp-input",
226   .vector_size = sizeof (u32),
227
228   .format_buffer = format_igmp_header,
229   .format_trace = format_igmp_input_trace,
230
231   .n_errors = IGMP_N_ERROR,
232   .error_strings = igmp_error_strings,
233
234   .n_next_nodes = IGMP_INPUT_N_NEXT,
235   .next_nodes = {
236       [IGMP_INPUT_NEXT_DROP] = "error-drop",
237       [IGMP_INPUT_NEXT_PARSE_QUERY] = "igmp-parse-query",
238       [IGMP_INPUT_NEXT_PARSE_REPORT] = "igmp-parse-report",
239   }
240 };
241
242 static uword
243 igmp_parse_query (vlib_main_t * vm, vlib_node_runtime_t * node,
244                   vlib_frame_t * frame)
245 {
246   u32 n_left_from, *from, *to_next;
247   igmp_parse_query_next_t next_index;
248
249   from = vlib_frame_vector_args (frame);
250   n_left_from = frame->n_vectors;
251   next_index = node->cached_next_index;
252
253   while (n_left_from > 0)
254     {
255       u32 n_left_to_next;
256
257       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
258
259       while (n_left_from > 0 && n_left_to_next > 0)
260         {
261           igmp_membership_query_v3_t *igmp;
262           igmp_query_args_t *args;
263           u32 bi, next, len;
264           vlib_buffer_t *b;
265
266           next = IGMP_PARSE_QUERY_NEXT_DROP;
267           bi = from[0];
268           to_next[0] = bi;
269           from++;
270           to_next++;
271           n_left_from--;
272           n_left_to_next--;
273
274           b = vlib_get_buffer (vm, bi);
275           igmp = vlib_buffer_get_current (b);
276           ASSERT (igmp->header.type == IGMP_TYPE_membership_query);
277           len = igmp_membership_query_v3_length (igmp);
278
279           if (node->flags & VLIB_NODE_FLAG_TRACE)
280             {
281               igmp_input_trace_t *tr;
282               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
283               tr->next_index = next;
284               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
285               tr->len = len;
286               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
287                                 sizeof (tr->packet_data));
288             }
289
290           /*
291            * validate that the length on the packet on the wire  corresponds
292            * to at least the length of the calculated v3 query.
293            * If there's extra, then it will be ignored.
294            */
295           if (vlib_buffer_length_in_chain (vm, b) >= len)
296             {
297               /*
298                * copy the contents of the query, and the interface, over
299                * to the main thread for processing
300                */
301               vlib_buffer_advance (b, -sizeof (u32));
302               args = vlib_buffer_get_current (b);
303               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
304
305               vl_api_rpc_call_main_thread (igmp_handle_query,
306                                            (u8 *) args, sizeof (*args) + len);
307             }
308           else
309             {
310               /*
311                * else a packet that is reporting more sources than it really
312                * has; bin it
313                */
314               b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
315             }
316
317           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
318                                            n_left_to_next, bi, next);
319         }
320       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
321     }
322
323   return frame->n_vectors;
324 }
325
326 VLIB_REGISTER_NODE (igmp_parse_query_node) =
327 {
328   .function = igmp_parse_query,
329   .name = "igmp-parse-query",
330   .vector_size = sizeof (u32),
331
332   .format_buffer = format_igmp_query_v3,
333   .format_trace = format_igmp_parse_query_trace,
334
335   .n_errors = IGMP_N_ERROR,
336   .error_strings = igmp_error_strings,
337
338   .n_next_nodes = IGMP_PARSE_QUERY_N_NEXT,
339   .next_nodes = {
340     [IGMP_PARSE_QUERY_NEXT_DROP] = "error-drop",
341   }
342 };
343
344 static uword
345 igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
346                    vlib_frame_t * frame)
347 {
348   u32 n_left_from, *from, *to_next;
349   igmp_input_next_t next_index;
350   vlib_node_runtime_t *error_node =
351     vlib_node_get_runtime (vm, igmp_input_node.index);
352   u8 error;
353
354   from = vlib_frame_vector_args (frame);
355   n_left_from = frame->n_vectors;
356   next_index = node->cached_next_index;
357
358   while (n_left_from > 0)
359     {
360       u32 n_left_to_next;
361
362       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
363
364       while (n_left_from > 0 && n_left_to_next > 0)
365         {
366           igmp_membership_report_v3_t *igmp;
367           igmp_report_args_t *args;
368           u32 bi, next, len;
369           vlib_buffer_t *b;
370
371           next = IGMP_PARSE_REPORT_NEXT_DROP;
372
373           bi = from[0];
374           to_next[0] = bi;
375           from++;
376           to_next++;
377           n_left_from--;
378           n_left_to_next--;
379
380           b = vlib_get_buffer (vm, bi);
381
382           error = IGMP_ERROR_NONE;
383           b->error = error_node->errors[error];
384           igmp = vlib_buffer_get_current (b);
385           len = igmp_membership_report_v3_length (igmp);
386
387           ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
388
389           if (node->flags & VLIB_NODE_FLAG_TRACE)
390             {
391               igmp_input_trace_t *tr;
392               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
393               tr->next_index = next;
394               tr->len = len;
395               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
396               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
397                                 sizeof (tr->packet_data));
398             }
399
400           /*
401            * validate that the length on the packet on the wire
402            * corresponds to the length on the calculated v3 query
403            */
404           if (vlib_buffer_length_in_chain (vm, b) >= len)
405             {
406               /*
407                * copy the contents of the query, and the interface, over
408                * to the main thread for processing
409                */
410               vlib_buffer_advance (b, -sizeof (u32));
411               args = vlib_buffer_get_current (b);
412               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
413
414               vl_api_rpc_call_main_thread (igmp_handle_report,
415                                            (u8 *) args, sizeof (*args) + len);
416             }
417           else
418             {
419               /*
420                * this is a packet with more groups/sources than the
421                * header reports. bin it
422                */
423               b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
424             }
425
426           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
427                                            n_left_to_next, bi, next);
428         }
429       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
430     }
431
432   return frame->n_vectors;
433 }
434
435 VLIB_REGISTER_NODE (igmp_parse_report_node) =
436 {
437   .function = igmp_parse_report,
438   .name = "igmp-parse-report",
439   .vector_size = sizeof (u32),
440
441   .format_buffer = format_igmp_report_v3,
442   .format_trace = format_igmp_parse_report_trace,
443
444   .n_errors = IGMP_N_ERROR,
445   .error_strings = igmp_error_strings,
446
447   .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
448   .next_nodes = {
449     [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
450   }
451 };
452
453 static clib_error_t *
454 igmp_input_init (vlib_main_t * vm)
455 {
456   ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
457
458   IGMP_DBG ("input-initialized");
459
460   return (0);
461 }
462
463 VLIB_INIT_FUNCTION (igmp_input_init) =
464 {
465   .runs_after = VLIB_INITS("igmp_init"),
466 };
467
468 /*
469  * fd.io coding-style-patch-verification: ON
470  *
471  * Local Variables:
472  * eval: (c-set-style "gnu")
473  * End:
474  */