FD.io VPP  v20.09-64-g4f7b92f0a
Vector Packet Processing
igmp_input.c
Go to the documentation of this file.
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 {
41 
42 typedef enum
43 {
47 
48 typedef enum
49 {
53 
54 char *igmp_error_strings[] = {
55 #define _(sym,string) string,
57 #undef _
58 };
59 
60 typedef struct
61 {
65  u8 packet_data[64];
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 *
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
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 
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  }
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:
190  break;
191  case IGMP_TYPE_membership_report_v3:
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);
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* */
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
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  {
264  igmp_query_args_t *args;
265  u32 bi, next, len;
266  vlib_buffer_t *b;
267 
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;
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 
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* */
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
351 {
352  u32 n_left_from, *from, *to_next;
353  igmp_input_next_t next_index;
354  vlib_node_runtime_t *error_node =
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  {
371  igmp_report_args_t *args;
372  u32 bi, next, len;
373  vlib_buffer_t *b;
374 
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);
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];
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 
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* */
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 *
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* */
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  */
vlib_node_registration_t igmp_parse_report_node
(constructor) VLIB_REGISTER_NODE (igmp_parse_report_node)
Definition: igmp_input.c:440
igmp_parse_report_next_t
Definition: igmp_input.c:48
static uword igmp_input(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: igmp_input.c:113
A copy of the query message sent from the worker to the main thread.
Definition: igmp_query.h:23
#define CLIB_UNUSED(x)
Definition: clib.h:87
void igmp_handle_report(const igmp_report_args_t *args)
Definition: igmp_report.c:216
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
igmp_input_next_t
Definition: igmp_input.c:34
char * igmp_error_strings[]
Definition: igmp_input.c:54
static uword igmp_parse_report(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: igmp_input.c:349
static u32 igmp_membership_query_v3_length(const igmp_membership_query_v3_t *q)
Definition: igmp_packet.h:119
uword ip_csum_t
Definition: ip_packet.h:244
vlib_main_t * vm
Definition: in2out_ed.c:1582
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
u8 * format_igmp_report_v3(u8 *s, va_list *args)
Definition: igmp_format.c:100
void ip4_register_protocol(u32 protocol, u32 node_index)
Definition: ip4_forward.c:1933
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:469
static uword vlib_buffer_length_in_chain(vlib_main_t *vm, vlib_buffer_t *b)
Get length in bytes of the buffer chain.
Definition: buffer_funcs.h:402
unsigned char u8
Definition: types.h:56
#define IGMP_DBG(...)
Definition: igmp.h:38
static u8 * format_igmp_input_trace(u8 *s, va_list *va)
Definition: igmp_input.c:69
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:173
vlib_node_registration_t igmp_input_node
(constructor) VLIB_REGISTER_NODE (igmp_input_node)
Definition: igmp_input.c:223
u8 * format_hex_bytes(u8 *s, va_list *va)
Definition: std-formats.c:84
void vl_api_rpc_call_main_thread(void *fp, u8 *data, u32 data_length)
Definition: vlib_api.c:619
unsigned int u32
Definition: types.h:88
static u8 * format_igmp_parse_query_trace(u8 *s, va_list *va)
Definition: igmp_input.c:97
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:136
igmp_config_t * igmp_config_lookup(u32 sw_if_index)
igmp config lookup
Definition: igmp_config.c:45
unsigned short u16
Definition: types.h:57
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:229
u8 * format_igmp_header(u8 *s, va_list *args)
Definition: igmp_format.c:80
igmp_type_t type
Definition: igmp_packet.h:70
#define vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0)
Finish enqueueing one buffer forward in the graph.
Definition: buffer_node.h:224
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Get pointer to next frame vector data by (vlib_node_runtime_t, next_index).
Definition: node_funcs.h:391
A copy of the report message sent from the worker to the main thread.
Definition: igmp_report.h:23
u8 len
Definition: ip_types.api:92
static clib_error_t * igmp_input_init(vlib_main_t *vm)
Definition: igmp_input.c:460
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
u16 n_vectors
Definition: node.h:396
vlib_node_registration_t igmp_parse_query_node
(constructor) VLIB_REGISTER_NODE (igmp_parse_query_node)
Definition: igmp_input.c:329
#define foreach_igmp_error
Definition: igmp_error.h:21
static vlib_node_runtime_t * vlib_node_get_runtime(vlib_main_t *vm, u32 node_index)
Get node runtime by node index.
Definition: node_funcs.h:115
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Release pointer to next frame vector data.
Definition: main.c:483
vlib_main_t vlib_node_runtime_t * node
Definition: in2out_ed.c:1582
u16 cached_next_index
Next frame index that vector arguments were last enqueued to last time this node ran.
Definition: node.h:510
#define ASSERT(truth)
igmp_parse_query_next_t
Definition: igmp_input.c:42
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:248
void igmp_handle_query(const igmp_query_args_t *args)
Called from the main thread on reception of a Query message.
Definition: igmp_query.c:174
vl_api_address_t ip
Definition: l2.api:501
vlib_main_t vlib_node_runtime_t vlib_frame_t * frame
Definition: in2out_ed.c:1583
u8 * format_igmp_query_v3(u8 *s, va_list *args)
Definition: igmp_format.c:146
VLIB buffer representation.
Definition: buffer.h:102
u64 uword
Definition: types.h:112
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:297
static uword igmp_parse_query(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: igmp_input.c:245
static u8 * format_igmp_parse_report_trace(u8 *s, va_list *va)
Definition: igmp_input.c:83
#define vnet_buffer(b)
Definition: buffer.h:417
static u32 igmp_membership_report_v3_length(const igmp_membership_report_v3_t *r)
Definition: igmp_packet.h:193
u16 flags
Copy of main node flags.
Definition: node.h:500
void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace.c:577
static int ip4_header_bytes(const ip4_header_t *i)
Definition: ip4_packet.h:190
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:301
static ip_csum_t ip_incremental_checksum(ip_csum_t sum, void *_data, uword n_bytes)
Definition: ip_packet.h:318
#define VLIB_INITS(...)
Definition: init.h:357
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:85
static u16 ip_csum_fold(ip_csum_t c)
Definition: ip_packet.h:300
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:33
Definition: defs.h:46