Line data Source code
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 4 : format_igmp_input_trace (u8 * s, va_list * va)
70 : {
71 4 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
72 4 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
73 4 : igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
74 :
75 4 : s = format (s, "sw_if_index %u next-index %u",
76 : t->sw_if_index, t->next_index);
77 4 : s = format (s, "\n%U", format_igmp_header, t->packet_data,
78 : sizeof (t->packet_data));
79 4 : return s;
80 : }
81 :
82 : static u8 *
83 2 : format_igmp_parse_report_trace (u8 * s, va_list * va)
84 : {
85 2 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
86 2 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
87 2 : igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
88 :
89 2 : s = format (s, "sw_if_index %u next-index %u",
90 : t->sw_if_index, t->next_index);
91 2 : s = format (s, "\n%U", format_igmp_report_v3, t->packet_data,
92 : sizeof (t->packet_data));
93 2 : return s;
94 : }
95 :
96 : static u8 *
97 2 : format_igmp_parse_query_trace (u8 * s, va_list * va)
98 : {
99 2 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
100 2 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
101 2 : igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
102 :
103 2 : s = format (s, "sw_if_index %u next-input %u len %u",
104 : t->sw_if_index, t->next_index, t->len);
105 2 : s = format (s, "\n%U", format_igmp_query_v3, t->packet_data,
106 : sizeof (t->packet_data));
107 2 : s = format (s, "\n%U", format_hex_bytes,
108 2 : t->packet_data, sizeof (t->packet_data));
109 2 : return s;
110 : }
111 :
112 : static uword
113 26 : 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 26 : error = IGMP_ERROR_NONE;
122 26 : error_node = node;
123 :
124 26 : from = vlib_frame_vector_args (frame);
125 26 : n_left_from = frame->n_vectors;
126 26 : next_index = node->cached_next_index;
127 :
128 52 : while (n_left_from > 0)
129 : {
130 : u32 n_left_to_next;
131 :
132 26 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
133 :
134 54 : 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 28 : next = IGMP_INPUT_NEXT_DROP;
144 28 : bi = from[0];
145 28 : to_next[0] = bi;
146 28 : from++;
147 28 : to_next++;
148 28 : n_left_from--;
149 28 : n_left_to_next--;
150 :
151 28 : b = vlib_get_buffer (vm, bi);
152 28 : ip = vlib_buffer_get_current (b);
153 :
154 28 : if (ip->protocol != IP_PROTOCOL_IGMP)
155 : {
156 0 : error = IGMP_ERROR_INVALID_PROTOCOL;
157 0 : next = IGMP_INPUT_NEXT_DROP;
158 0 : goto next_buffer;
159 : }
160 :
161 28 : vlib_buffer_advance (b, ip4_header_bytes (ip));
162 :
163 28 : igmp = vlib_buffer_get_current (b);
164 :
165 28 : checksum = igmp->checksum;
166 28 : igmp->checksum = 0;
167 28 : sum = ip_incremental_checksum (0, igmp,
168 56 : clib_net_to_host_u16 (ip->length) -
169 28 : ip4_header_bytes (ip));
170 28 : igmp->checksum = checksum;
171 28 : csum = ~ip_csum_fold (sum);
172 28 : if (checksum != csum)
173 : {
174 0 : error = IGMP_ERROR_BAD_CHECKSUM;
175 0 : next = IGMP_INPUT_NEXT_DROP;
176 0 : goto next_buffer;
177 : }
178 28 : if (!igmp_config_lookup (vnet_buffer (b)->sw_if_index[VLIB_RX]))
179 : {
180 1 : error = IGMP_ERROR_NOT_ENABLED;
181 1 : next = IGMP_INPUT_NEXT_DROP;
182 1 : goto next_buffer;
183 : }
184 :
185 : /* TODO: IGMPv2 and IGMPv1 */
186 27 : switch (igmp->type)
187 : {
188 14 : case IGMP_TYPE_membership_query:
189 14 : next = IGMP_INPUT_NEXT_PARSE_QUERY;
190 14 : break;
191 13 : case IGMP_TYPE_membership_report_v3:
192 13 : next = IGMP_INPUT_NEXT_PARSE_REPORT;
193 13 : break;
194 0 : default:
195 0 : error = IGMP_ERROR_UNKNOWN_TYPE;
196 0 : next = IGMP_INPUT_NEXT_DROP;
197 0 : break;
198 : }
199 28 : next_buffer:
200 28 : b->error = error_node->errors[error];
201 :
202 28 : if (node->flags & VLIB_NODE_FLAG_TRACE)
203 : {
204 : igmp_input_trace_t *tr;
205 28 : tr = vlib_add_trace (vm, node, b, sizeof (*tr));
206 28 : tr->next_index = next;
207 28 : tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
208 28 : tr->len = vlib_buffer_length_in_chain (vm, b);
209 28 : clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
210 : sizeof (tr->packet_data));
211 : }
212 :
213 28 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
214 : n_left_to_next, bi, next);
215 : }
216 26 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
217 : }
218 :
219 26 : return frame->n_vectors;
220 : }
221 :
222 : /* *INDENT-OFF* */
223 128492 : 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 12 : 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 12 : from = vlib_frame_vector_args (frame);
252 12 : n_left_from = frame->n_vectors;
253 12 : next_index = node->cached_next_index;
254 :
255 24 : while (n_left_from > 0)
256 : {
257 : u32 n_left_to_next;
258 :
259 12 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
260 :
261 26 : 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 14 : next = IGMP_PARSE_QUERY_NEXT_DROP;
269 14 : bi = from[0];
270 14 : to_next[0] = bi;
271 14 : from++;
272 14 : to_next++;
273 14 : n_left_from--;
274 14 : n_left_to_next--;
275 :
276 14 : b = vlib_get_buffer (vm, bi);
277 14 : igmp = vlib_buffer_get_current (b);
278 14 : ASSERT (igmp->header.type == IGMP_TYPE_membership_query);
279 14 : len = igmp_membership_query_v3_length (igmp);
280 :
281 14 : if (node->flags & VLIB_NODE_FLAG_TRACE)
282 : {
283 : igmp_input_trace_t *tr;
284 14 : tr = vlib_add_trace (vm, node, b, sizeof (*tr));
285 14 : tr->next_index = next;
286 14 : tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
287 14 : tr->len = len;
288 14 : 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 14 : 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 13 : vlib_buffer_advance (b, -sizeof (u32));
304 13 : args = vlib_buffer_get_current (b);
305 13 : args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
306 :
307 13 : 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 1 : b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
317 : }
318 :
319 14 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
320 : n_left_to_next, bi, next);
321 : }
322 12 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
323 : }
324 :
325 12 : return frame->n_vectors;
326 : }
327 :
328 : /* *INDENT-OFF* */
329 128492 : 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 13 : 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 13 : vlib_node_get_runtime (vm, igmp_input_node.index);
356 : u8 error;
357 :
358 13 : from = vlib_frame_vector_args (frame);
359 13 : n_left_from = frame->n_vectors;
360 13 : next_index = node->cached_next_index;
361 :
362 26 : while (n_left_from > 0)
363 : {
364 : u32 n_left_to_next;
365 :
366 13 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
367 :
368 26 : 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 13 : next = IGMP_PARSE_REPORT_NEXT_DROP;
376 :
377 13 : bi = from[0];
378 13 : to_next[0] = bi;
379 13 : from++;
380 13 : to_next++;
381 13 : n_left_from--;
382 13 : n_left_to_next--;
383 :
384 13 : b = vlib_get_buffer (vm, bi);
385 :
386 13 : error = IGMP_ERROR_NONE;
387 13 : b->error = error_node->errors[error];
388 13 : igmp = vlib_buffer_get_current (b);
389 13 : len = igmp_membership_report_v3_length (igmp);
390 :
391 13 : ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
392 :
393 13 : if (node->flags & VLIB_NODE_FLAG_TRACE)
394 : {
395 : igmp_input_trace_t *tr;
396 13 : tr = vlib_add_trace (vm, node, b, sizeof (*tr));
397 13 : tr->next_index = next;
398 13 : tr->len = len;
399 13 : tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
400 13 : 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 13 : 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 13 : vlib_buffer_advance (b, -sizeof (u32));
415 13 : args = vlib_buffer_get_current (b);
416 13 : args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
417 :
418 13 : 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 0 : b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
428 : }
429 :
430 13 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
431 : n_left_to_next, bi, next);
432 : }
433 13 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
434 : }
435 :
436 13 : return frame->n_vectors;
437 : }
438 :
439 : /* *INDENT-OFF* */
440 128492 : 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 575 : igmp_input_init (vlib_main_t * vm)
461 : {
462 575 : ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
463 :
464 575 : IGMP_DBG ("input-initialized");
465 :
466 575 : return (0);
467 : }
468 :
469 : /* *INDENT-OFF* */
470 1727 : 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 : */
|