a49790fe1195d657ca8783e9f01f52b6c1072ecb
[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
280           if (node->flags & VLIB_NODE_FLAG_TRACE)
281             {
282               igmp_input_trace_t *tr;
283               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
284               tr->next_index = next;
285               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
286               tr->len = vlib_buffer_length_in_chain (vm, b);
287               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
288                                 sizeof (tr->packet_data));
289             }
290           len = igmp_membership_query_v3_length (igmp);
291
292           /*
293            * validate that the length on the packet on the wire
294            * corresponds to the length on the calculated v3 query
295            */
296           if (vlib_buffer_length_in_chain (vm, b) == len)
297             {
298               /*
299                * copy the contents of the query, and the interface, over
300                * to the main thread for processing
301                */
302               vlib_buffer_advance (b, -sizeof (u32));
303               args = vlib_buffer_get_current (b);
304               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
305
306               vl_api_rpc_call_main_thread (igmp_handle_query,
307                                            (u8 *) args, sizeof (*args) + len);
308             }
309           else
310             {
311               /*
312                * else a packet that is reporting more or less sources
313                * than it really has, bin it
314                */
315               b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
316             }
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 static uword
348 igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
349                    vlib_frame_t * frame)
350 {
351   u32 n_left_from, *from, *to_next;
352   igmp_input_next_t next_index;
353   vlib_node_runtime_t *error_node =
354     vlib_node_get_runtime (vm, igmp_input_node.index);
355   u8 error;
356
357   from = vlib_frame_vector_args (frame);
358   n_left_from = frame->n_vectors;
359   next_index = node->cached_next_index;
360
361   while (n_left_from > 0)
362     {
363       u32 n_left_to_next;
364
365       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
366
367       while (n_left_from > 0 && n_left_to_next > 0)
368         {
369           igmp_membership_report_v3_t *igmp;
370           igmp_report_args_t *args;
371           u32 bi, next, len;
372           vlib_buffer_t *b;
373
374           next = IGMP_PARSE_REPORT_NEXT_DROP;
375
376           bi = from[0];
377           to_next[0] = bi;
378           from++;
379           to_next++;
380           n_left_from--;
381           n_left_to_next--;
382
383           b = vlib_get_buffer (vm, bi);
384
385           error = IGMP_ERROR_NONE;
386           b->error = error_node->errors[error];
387           igmp = vlib_buffer_get_current (b);
388           len = igmp_membership_report_v3_length (igmp);
389
390           ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
391
392           if (node->flags & VLIB_NODE_FLAG_TRACE)
393             {
394               igmp_input_trace_t *tr;
395               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
396               tr->next_index = next;
397               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
398               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
399                                 sizeof (tr->packet_data));
400             }
401
402           /*
403            * validate that the length on the packet on the wire
404            * corresponds to the length on the calculated v3 query
405            */
406           if (vlib_buffer_length_in_chain (vm, b) == len)
407             {
408               /*
409                * copy the contents of the query, and the interface, over
410                * to the main thread for processing
411                */
412               vlib_buffer_advance (b, -sizeof (u32));
413               args = vlib_buffer_get_current (b);
414               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
415
416               vl_api_rpc_call_main_thread (igmp_handle_report,
417                                            (u8 *) args, sizeof (*args) + len);
418             }
419           /*
420            * else
421            *   this is a packet with more groups/sources than the
422            *   header reports. bin it
423            */
424           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
425                                            n_left_to_next, bi, next);
426         }
427       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
428     }
429
430   return frame->n_vectors;
431 }
432
433 /* *INDENT-OFF* */
434 VLIB_REGISTER_NODE (igmp_parse_report_node) =
435 {
436   .function = igmp_parse_report,
437   .name = "igmp-parse-report",
438   .vector_size = sizeof (u32),
439
440   .format_buffer = format_igmp_report_v3,
441   .format_trace = format_igmp_parse_report_trace,
442
443   .n_errors = IGMP_N_ERROR,
444   .error_strings = igmp_error_strings,
445
446   .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
447   .next_nodes = {
448     [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
449   }
450 };
451 /* *INDENT-ON* */
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 /* *INDENT-OFF* */
464 VLIB_INIT_FUNCTION (igmp_input_init) =
465 {
466   .runs_after = VLIB_INITS("igmp_init"),
467 };
468 /* *INDENT-ON* */
469
470 /*
471  * fd.io coding-style-patch-verification: ON
472  *
473  * Local Variables:
474  * eval: (c-set-style "gnu")
475  * End:
476  */