FD.io VPP  v18.10-34-gcce845e
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 {
64 
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",
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
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 
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  }
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:
188  break;
189  case IGMP_TYPE_membership_report_v3:
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];
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* */
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
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  {
261  igmp_query_args_t *args;
262  u32 bi, next, len;
263  vlib_buffer_t *b;
264 
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];
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 
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* */
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
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 =
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  {
362  igmp_report_args_t *args;
363  u32 bi, next, len;
364  vlib_buffer_t *b;
365 
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);
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];
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 
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* */
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 *
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 
461 
462 /*
463  * fd.io coding-style-patch-verification: ON
464  *
465  * Local Variables:
466  * eval: (c-set-style "gnu")
467  * End:
468  */
vlib_node_registration_t igmp_parse_report_node
(constructor) VLIB_REGISTER_NODE (igmp_parse_report_node)
Definition: igmp_input.c:426
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:111
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:81
void igmp_handle_report(const igmp_report_args_t *args)
Definition: igmp_report.c:162
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:340
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:181
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
u8 * format_igmp_report_v3(u8 *s, va_list *args)
Definition: igmp_format.c:100
static clib_error_t * igmp_init(vlib_main_t *vm)
igmp initialization
Definition: igmp.c:467
void ip4_register_protocol(u32 protocol, u32 node_index)
Definition: ip4_forward.c:1684
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:472
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:263
unsigned char u8
Definition: types.h:56
#define IGMP_DBG(...)
Definition: igmp.h:37
static u8 * format_igmp_input_trace(u8 *s, va_list *va)
Definition: igmp_input.c:69
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:163
u32 sw_if_index
Definition: vxlan_gbp.api:39
vlib_node_registration_t igmp_input_node
(constructor) VLIB_REGISTER_NODE (igmp_input_node)
Definition: igmp_input.c:220
void vl_api_rpc_call_main_thread(void *fp, u8 *data, u32 data_length)
Definition: vlib_api.c:638
unsigned int u32
Definition: types.h:88
#define vlib_call_init_function(vm, x)
Definition: init.h:260
static u8 * format_igmp_parse_query_trace(u8 *s, va_list *va)
Definition: igmp_input.c:97
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:205
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:218
#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:364
A copy of the report message sent from the worker to the main thread.
Definition: igmp_report.h:23
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:138
static clib_error_t * igmp_input_init(vlib_main_t *vm)
Definition: igmp_input.c:446
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:155
u16 n_vectors
Definition: node.h:401
vlib_node_registration_t igmp_parse_query_node
(constructor) VLIB_REGISTER_NODE (igmp_parse_query_node)
Definition: igmp_input.c:320
vlib_main_t * vm
Definition: buffer.c:294
#define foreach_igmp_error
Definition: igmp_error.h:21
static ip_csum_t ip_incremental_checksum(ip_csum_t sum, void *_data, uword n_bytes)
Definition: ip_packet.h:254
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:89
#define clib_memcpy(a, b, c)
Definition: string.h:75
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:455
u16 cached_next_index
Next frame index that vector arguments were last enqueued to last time this node ran.
Definition: node.h:513
#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:218
static void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:57
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
u8 * format_igmp_query_v3(u8 *s, va_list *args)
Definition: igmp_format.c:146
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:267
static uword igmp_parse_query(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: igmp_input.c:242
static u8 * format_igmp_parse_report_trace(u8 *s, va_list *va)
Definition: igmp_input.c:83
#define vnet_buffer(b)
Definition: buffer.h:344
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:507
static int ip4_header_bytes(const ip4_header_t *i)
Definition: ip4_packet.h:234
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:310
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:58
static u16 ip_csum_fold(ip_csum_t c)
Definition: ip_packet.h:237
Definition: defs.h:46