IGMP: validate the packets length in the DP
[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           u32 bi, next, len;
263           vlib_buffer_t *b;
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           len = igmp_membership_query_v3_length (igmp);
287
288           /*
289            * validate that the length on the packet on the wire
290            * corresponds to the length on the calculated v3 query
291            */
292           if (vlib_buffer_length_in_chain (vm, b) == len)
293             {
294               /*
295                * copy the contents of the query, and the interface, over
296                * to the main thread for processing
297                */
298               vlib_buffer_advance (b, -sizeof (u32));
299               args = vlib_buffer_get_current (b);
300               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
301
302               vl_api_rpc_call_main_thread (igmp_handle_query,
303                                            (u8 *) args, sizeof (*args) + len);
304             }
305           /*
306            * else a packet that is reporting more or less sources
307            * than it really has, bin it
308            */
309
310           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
311                                            n_left_to_next, bi, next);
312         }
313       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
314     }
315
316   return frame->n_vectors;
317 }
318
319 /* *INDENT-OFF* */
320 VLIB_REGISTER_NODE (igmp_parse_query_node) =
321 {
322   .function = igmp_parse_query,
323   .name = "igmp-parse-query",
324   .vector_size = sizeof (u32),
325
326   .format_buffer = format_igmp_query_v3,
327   .format_trace = format_igmp_parse_query_trace,
328
329   .n_errors = IGMP_N_ERROR,
330   .error_strings = igmp_error_strings,
331
332   .n_next_nodes = IGMP_PARSE_QUERY_N_NEXT,
333   .next_nodes = {
334     [IGMP_PARSE_QUERY_NEXT_DROP] = "error-drop",
335   }
336 };
337 /* *INDENT-ON* */
338
339 static uword
340 igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
341                    vlib_frame_t * frame)
342 {
343   u32 n_left_from, *from, *to_next;
344   igmp_input_next_t next_index;
345   vlib_node_runtime_t *error_node =
346     vlib_node_get_runtime (vm, igmp_input_node.index);
347   u8 error;
348
349   from = vlib_frame_vector_args (frame);
350   n_left_from = frame->n_vectors;
351   next_index = node->cached_next_index;
352
353   while (n_left_from > 0)
354     {
355       u32 n_left_to_next;
356
357       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
358
359       while (n_left_from > 0 && n_left_to_next > 0)
360         {
361           igmp_membership_report_v3_t *igmp;
362           igmp_report_args_t *args;
363           u32 bi, next, len;
364           vlib_buffer_t *b;
365
366           next = IGMP_PARSE_REPORT_NEXT_DROP;
367
368           bi = from[0];
369           to_next[0] = bi;
370           from++;
371           to_next++;
372           n_left_from--;
373           n_left_to_next--;
374
375           b = vlib_get_buffer (vm, bi);
376
377           error = IGMP_ERROR_NONE;
378           b->error = error_node->errors[error];
379           igmp = vlib_buffer_get_current (b);
380           len = igmp_membership_report_v3_length (igmp);
381
382           ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
383
384           if (node->flags & VLIB_NODE_FLAG_TRACE)
385             {
386               igmp_input_trace_t *tr;
387               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
388               tr->next_index = next;
389               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
390               clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
391                            sizeof (tr->packet_data));
392             }
393
394           /*
395            * validate that the length on the packet on the wire
396            * corresponds to the length on the calculated v3 query
397            */
398           if (vlib_buffer_length_in_chain (vm, b) == len)
399             {
400               /*
401                * copy the contents of the query, and the interface, over
402                * to the main thread for processing
403                */
404               vlib_buffer_advance (b, -sizeof (u32));
405               args = vlib_buffer_get_current (b);
406               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
407
408               vl_api_rpc_call_main_thread (igmp_handle_report,
409                                            (u8 *) args, sizeof (*args) + len);
410             }
411           /*
412            * else
413            *   this is a packet with more groups/sources than the
414            *   header reports. bin it
415            */
416           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
417                                            n_left_to_next, bi, next);
418         }
419       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
420     }
421
422   return frame->n_vectors;
423 }
424
425 /* *INDENT-OFF* */
426 VLIB_REGISTER_NODE (igmp_parse_report_node) =
427 {
428   .function = igmp_parse_report,
429   .name = "igmp-parse-report",
430   .vector_size = sizeof (u32),
431
432   .format_buffer = format_igmp_report_v3,
433   .format_trace = format_igmp_parse_report_trace,
434
435   .n_errors = IGMP_N_ERROR,
436   .error_strings = igmp_error_strings,
437
438   .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
439   .next_nodes = {
440     [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
441   }
442 };
443 /* *INDENT-ON* */
444
445 static clib_error_t *
446 igmp_input_init (vlib_main_t * vm)
447 {
448   clib_error_t *error;
449
450   if ((error = vlib_call_init_function (vm, igmp_init)))
451     return error;
452
453   ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
454
455   IGMP_DBG ("input-initialized");
456
457   return (error);
458 }
459
460 VLIB_INIT_FUNCTION (igmp_input_init);
461
462 /*
463  * fd.io coding-style-patch-verification: ON
464  *
465  * Local Variables:
466  * eval: (c-set-style "gnu")
467  * End:
468  */