FD.io VPP  v19.04.2-12-g66b1689
Vector Packet Processing
ip6_hop_by_hop.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vlib/vlib.h>
16 #include <vnet/vnet.h>
17 #include <vnet/pg/pg.h>
18 #include <vppinfra/error.h>
19 
20 #include <vnet/ip/ip.h>
21 
22 #include <vppinfra/hash.h>
23 #include <vppinfra/error.h>
24 #include <vppinfra/elog.h>
25 
26 #include <vnet/ip/ip6_hop_by_hop.h>
27 #include <vnet/fib/ip6_fib.h>
29 
30 /**
31  * @file
32  * @brief In-band OAM (iOAM).
33  *
34  * In-band OAM (iOAM) is an implementation study to record operational
35  * information in the packet while the packet traverses a path between
36  * two points in the network.
37  *
38  * VPP can function as in-band OAM encapsulating, transit and
39  * decapsulating node. In this version of VPP in-band OAM data is
40  * transported as options in an IPv6 hop-by-hop extension header. Hence
41  * in-band OAM can be enabled for IPv6 traffic.
42  */
43 
44 #ifndef CLIB_MARCH_VARIANT
46 #endif /* CLIB_MARCH_VARIANT */
47 
48 #define foreach_ip6_hbyh_ioam_input_next \
49  _(IP6_REWRITE, "ip6-rewrite") \
50  _(IP6_LOOKUP, "ip6-lookup") \
51  _(DROP, "ip6-drop")
52 
53 typedef enum
54 {
55 #define _(s,n) IP6_HBYH_IOAM_INPUT_NEXT_##s,
57 #undef _
60 
61 #ifndef CLIB_MARCH_VARIANT
62 static uword
63 unformat_opaque_ioam (unformat_input_t * input, va_list * args)
64 {
65  u64 *opaquep = va_arg (*args, u64 *);
66  u8 *flow_name = NULL;
67  uword ret = 0;
68 
69  if (unformat (input, "ioam-encap %s", &flow_name))
70  {
71  *opaquep = ioam_flow_add (1, flow_name);
72  ret = 1;
73  }
74  else if (unformat (input, "ioam-decap %s", &flow_name))
75  {
76  *opaquep = ioam_flow_add (0, flow_name);
77  ret = 1;
78  }
79 
80  vec_free (flow_name);
81  return ret;
82 }
83 
84 u8 *
86 {
89  u32 index;
90 
91  index = IOAM_MASK_DECAP_BIT (flow_ctx);
92 
93  if (pool_is_free_index (hm->flows, index))
94  return NULL;
95 
96  flow = pool_elt_at_index (hm->flows, index);
97  return (flow->flow_name);
98 }
99 
100 /* The main h-b-h tracer will be invoked, no need to do much here */
101 int
103  u8 size,
104  int rewrite_options (u8 * rewrite_string,
105  u8 * rewrite_size))
106 {
108 
109  ASSERT ((u32) option < ARRAY_LEN (hm->add_options));
110 
111  /* Already registered */
112  if (hm->add_options[option])
113  return (-1);
114 
115  hm->add_options[option] = rewrite_options;
116  hm->options_size[option] = size;
117 
118  return (0);
119 }
120 
121 int
123 {
125 
126  ASSERT ((u32) option < ARRAY_LEN (hm->add_options));
127 
128  /* Not registered */
129  if (!hm->add_options[option])
130  return (-1);
131 
132  hm->add_options[option] = NULL;
133  hm->options_size[option] = 0;
134  return (0);
135 }
136 
137 /* Config handler registration */
138 int
140  int config_handler (void *data, u8 disable))
141 {
143 
144  ASSERT ((u32) option < ARRAY_LEN (hm->config_handler));
145 
146  /* Already registered */
147  if (hm->config_handler[option])
148  return (VNET_API_ERROR_INVALID_REGISTRATION);
149 
150  hm->config_handler[option] = config_handler;
151 
152  return (0);
153 }
154 
155 int
157 {
159 
160  ASSERT ((u32) option < ARRAY_LEN (hm->config_handler));
161 
162  /* Not registered */
163  if (!hm->config_handler[option])
164  return (VNET_API_ERROR_INVALID_REGISTRATION);
165 
166  hm->config_handler[option] = NULL;
167  return (0);
168 }
169 
170 /* Flow handler registration */
171 int
173  u32 ioam_flow_handler (u32 flow_ctx, u8 add))
174 {
176 
177  ASSERT ((u32) option < ARRAY_LEN (hm->flow_handler));
178 
179  /* Already registered */
180  if (hm->flow_handler[option])
181  return (VNET_API_ERROR_INVALID_REGISTRATION);
182 
183  hm->flow_handler[option] = ioam_flow_handler;
184 
185  return (0);
186 }
187 
188 int
190 {
192 
193  ASSERT ((u32) option < ARRAY_LEN (hm->flow_handler));
194 
195  /* Not registered */
196  if (!hm->flow_handler[option])
197  return (VNET_API_ERROR_INVALID_REGISTRATION);
198 
199  hm->flow_handler[option] = NULL;
200  return (0);
201 }
202 #endif /* CLIB_MARCH_VARIANT */
203 
204 typedef struct
205 {
208 
209 /* packet trace format function */
210 static u8 *
211 format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
212 {
213  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
214  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
215  ip6_add_hop_by_hop_trace_t *t = va_arg (*args,
217 
218  s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d", t->next_index);
219  return s;
220 }
221 
223 
224 #define foreach_ip6_add_hop_by_hop_error \
225 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
226 
227 typedef enum
228 {
229 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
231 #undef _
234 
235 static char *ip6_add_hop_by_hop_error_strings[] = {
236 #define _(sym,string) string,
238 #undef _
239 };
240 
241 VLIB_NODE_FN (ip6_add_hop_by_hop_node) (vlib_main_t * vm,
242  vlib_node_runtime_t * node,
243  vlib_frame_t * frame)
244 {
246  u32 n_left_from, *from, *to_next;
247  ip_lookup_next_t next_index;
248  u32 processed = 0;
249  u8 *rewrite = hm->rewrite;
250  u32 rewrite_length = vec_len (rewrite);
251 
252  from = vlib_frame_vector_args (frame);
253  n_left_from = frame->n_vectors;
254  next_index = node->cached_next_index;
255 
256  while (n_left_from > 0)
257  {
258  u32 n_left_to_next;
259 
260  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
261  while (n_left_from >= 4 && n_left_to_next >= 2)
262  {
263  u32 bi0, bi1;
264  vlib_buffer_t *b0, *b1;
265  u32 next0, next1;
266  ip6_header_t *ip0, *ip1;
267  ip6_hop_by_hop_header_t *hbh0, *hbh1;
268  u64 *copy_src0, *copy_dst0, *copy_src1, *copy_dst1;
269  u16 new_l0, new_l1;
270 
271  /* Prefetch next iteration. */
272  {
273  vlib_buffer_t *p2, *p3;
274 
275  p2 = vlib_get_buffer (vm, from[2]);
276  p3 = vlib_get_buffer (vm, from[3]);
277 
278  vlib_prefetch_buffer_header (p2, LOAD);
279  vlib_prefetch_buffer_header (p3, LOAD);
280 
281  CLIB_PREFETCH (p2->data - rewrite_length,
282  2 * CLIB_CACHE_LINE_BYTES, STORE);
283  CLIB_PREFETCH (p3->data - rewrite_length,
284  2 * CLIB_CACHE_LINE_BYTES, STORE);
285  }
286 
287  /* speculatively enqueue b0 and b1 to the current next frame */
288  to_next[0] = bi0 = from[0];
289  to_next[1] = bi1 = from[1];
290  from += 2;
291  to_next += 2;
292  n_left_from -= 2;
293  n_left_to_next -= 2;
294 
295  b0 = vlib_get_buffer (vm, bi0);
296  b1 = vlib_get_buffer (vm, bi1);
297 
298  /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
299  ip0 = vlib_buffer_get_current (b0);
300  ip1 = vlib_buffer_get_current (b1);
301 
302  /* Copy the ip header left by the required amount */
303  copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
304  copy_dst1 = (u64 *) (((u8 *) ip1) - rewrite_length);
305  copy_src0 = (u64 *) ip0;
306  copy_src1 = (u64 *) ip1;
307 
308  copy_dst0[0] = copy_src0[0];
309  copy_dst0[1] = copy_src0[1];
310  copy_dst0[2] = copy_src0[2];
311  copy_dst0[3] = copy_src0[3];
312  copy_dst0[4] = copy_src0[4];
313 
314  copy_dst1[0] = copy_src1[0];
315  copy_dst1[1] = copy_src1[1];
316  copy_dst1[2] = copy_src1[2];
317  copy_dst1[3] = copy_src1[3];
318  copy_dst1[4] = copy_src1[4];
319 
320  vlib_buffer_advance (b0, -(word) rewrite_length);
321  vlib_buffer_advance (b1, -(word) rewrite_length);
322  ip0 = vlib_buffer_get_current (b0);
323  ip1 = vlib_buffer_get_current (b1);
324 
325  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
326  hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
327  /* $$$ tune, rewrite_length is a multiple of 8 */
328  clib_memcpy_fast (hbh0, rewrite, rewrite_length);
329  clib_memcpy_fast (hbh1, rewrite, rewrite_length);
330  /* Patch the protocol chain, insert the h-b-h (type 0) header */
331  hbh0->protocol = ip0->protocol;
332  hbh1->protocol = ip1->protocol;
333  ip0->protocol = 0;
334  ip1->protocol = 0;
335  new_l0 =
336  clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
337  new_l1 =
338  clib_net_to_host_u16 (ip1->payload_length) + rewrite_length;
339  ip0->payload_length = clib_host_to_net_u16 (new_l0);
340  ip1->payload_length = clib_host_to_net_u16 (new_l1);
341 
342  /* Populate the (first) h-b-h list elt */
343  next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
344  next1 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
345 
346 
347  /* $$$$$ End of processing 2 x packets $$$$$ */
348 
349  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
350  {
351  if (b0->flags & VLIB_BUFFER_IS_TRACED)
352  {
354  vlib_add_trace (vm, node, b0, sizeof (*t));
355  t->next_index = next0;
356  }
357  if (b1->flags & VLIB_BUFFER_IS_TRACED)
358  {
360  vlib_add_trace (vm, node, b1, sizeof (*t));
361  t->next_index = next1;
362  }
363  }
364  processed += 2;
365  /* verify speculative enqueues, maybe switch current next frame */
366  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
367  to_next, n_left_to_next,
368  bi0, bi1, next0, next1);
369  }
370  while (n_left_from > 0 && n_left_to_next > 0)
371  {
372  u32 bi0;
373  vlib_buffer_t *b0;
374  u32 next0;
375  ip6_header_t *ip0;
377  u64 *copy_src0, *copy_dst0;
378  u16 new_l0;
379 
380  /* speculatively enqueue b0 to the current next frame */
381  bi0 = from[0];
382  to_next[0] = bi0;
383  from += 1;
384  to_next += 1;
385  n_left_from -= 1;
386  n_left_to_next -= 1;
387 
388  b0 = vlib_get_buffer (vm, bi0);
389 
390  ip0 = vlib_buffer_get_current (b0);
391 
392  /* Copy the ip header left by the required amount */
393  copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
394  copy_src0 = (u64 *) ip0;
395 
396  copy_dst0[0] = copy_src0[0];
397  copy_dst0[1] = copy_src0[1];
398  copy_dst0[2] = copy_src0[2];
399  copy_dst0[3] = copy_src0[3];
400  copy_dst0[4] = copy_src0[4];
401  vlib_buffer_advance (b0, -(word) rewrite_length);
402  ip0 = vlib_buffer_get_current (b0);
403 
404  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
405  /* $$$ tune, rewrite_length is a multiple of 8 */
406  clib_memcpy_fast (hbh0, rewrite, rewrite_length);
407  /* Patch the protocol chain, insert the h-b-h (type 0) header */
408  hbh0->protocol = ip0->protocol;
409  ip0->protocol = 0;
410  new_l0 =
411  clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
412  ip0->payload_length = clib_host_to_net_u16 (new_l0);
413 
414  /* Populate the (first) h-b-h list elt */
415  next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
416 
417  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
418  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
419  {
421  vlib_add_trace (vm, node, b0, sizeof (*t));
422  t->next_index = next0;
423  }
424 
425  processed++;
426 
427  /* verify speculative enqueue, maybe switch current next frame */
428  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
429  to_next, n_left_to_next,
430  bi0, next0);
431  }
432 
433  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
434  }
435 
436  vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
437  IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
438  return frame->n_vectors;
439 }
440 
441 /* *INDENT-OFF* */
442 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = /* *INDENT-OFF* */
443 {
444  .name =
445  "ip6-add-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
447  VLIB_NODE_TYPE_INTERNAL,.n_errors =
448  ARRAY_LEN (ip6_add_hop_by_hop_error_strings),.error_strings =
450  /* See ip/lookup.h */
451  .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,.next_nodes =
452  {
453 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
455 #undef _
456  }
457 ,};
458 /* *INDENT-ON* */
459 
460 /* *INDENT-ON* */
461 
462 /* The main h-b-h tracer was already invoked, no need to do much here */
463 typedef struct
464 {
467 
468 /* packet trace format function */
469 static u8 *
470 format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
471 {
472  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
473  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
475  va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
476 
477  s = format (s, "IP6_POP_HOP_BY_HOP: next index %d", t->next_index);
478  return s;
479 }
480 
481 #ifndef CLIB_MARCH_VARIANT
482 int
484  int options (vlib_buffer_t * b,
485  ip6_header_t * ip,
487 {
489 
490  ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
491 
492  /* Already registered */
493  if (hm->pop_options[option])
494  return (-1);
495 
496  hm->pop_options[option] = options;
497 
498  return (0);
499 }
500 
501 int
503 {
505 
506  ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
507 
508  /* Not registered */
509  if (!hm->pop_options[option])
510  return (-1);
511 
512  hm->pop_options[option] = NULL;
513  return (0);
514 }
515 #endif /* CLIB_MARCH_VARIANT */
516 
518 
519 #define foreach_ip6_pop_hop_by_hop_error \
520 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \
521 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
522 _(OPTION_FAILED, "ip6 pop hop-by-hop failed to process")
523 
524 typedef enum
525 {
526 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
528 #undef _
531 
532 static char *ip6_pop_hop_by_hop_error_strings[] = {
533 #define _(sym,string) string,
535 #undef _
536 };
537 
538 static inline void
540  ip6_header_t * ip0,
542  vlib_buffer_t * b)
543 {
545  ip6_hop_by_hop_option_t *opt0, *limit0;
546  u8 type0;
547 
548  if (!hbh0 || !ip0)
549  return;
550 
551  opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
552  limit0 = (ip6_hop_by_hop_option_t *)
553  ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
554 
555  /* Scan the set of h-b-h options, process ones that we understand */
556  while (opt0 < limit0)
557  {
558  type0 = opt0->type;
559  switch (type0)
560  {
561  case 0: /* Pad1 */
562  opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
563  continue;
564  case 1: /* PadN */
565  break;
566  default:
567  if (hm->pop_options[type0])
568  {
569  if ((*hm->pop_options[type0]) (b, ip0, opt0) < 0)
570  {
572  ip6_pop_hop_by_hop_node.index,
573  IP6_POP_HOP_BY_HOP_ERROR_OPTION_FAILED,
574  1);
575  }
576  }
577  }
578  opt0 =
579  (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
580  sizeof (ip6_hop_by_hop_option_t));
581  }
582 }
583 
584 VLIB_NODE_FN (ip6_pop_hop_by_hop_node) (vlib_main_t * vm,
585  vlib_node_runtime_t * node,
586  vlib_frame_t * frame)
587 {
588  u32 n_left_from, *from, *to_next;
589  ip_lookup_next_t next_index;
590  u32 processed = 0;
591  u32 no_header = 0;
592 
593  from = vlib_frame_vector_args (frame);
594  n_left_from = frame->n_vectors;
595  next_index = node->cached_next_index;
596 
597  while (n_left_from > 0)
598  {
599  u32 n_left_to_next;
600 
601  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
602 
603  while (n_left_from >= 4 && n_left_to_next >= 2)
604  {
605  u32 bi0, bi1;
606  vlib_buffer_t *b0, *b1;
607  u32 next0, next1;
608  u32 adj_index0, adj_index1;
609  ip6_header_t *ip0, *ip1;
610  ip_adjacency_t *adj0, *adj1;
611  ip6_hop_by_hop_header_t *hbh0, *hbh1;
612  u64 *copy_dst0, *copy_src0, *copy_dst1, *copy_src1;
613  u16 new_l0, new_l1;
614 
615  /* Prefetch next iteration. */
616  {
617  vlib_buffer_t *p2, *p3;
618 
619  p2 = vlib_get_buffer (vm, from[2]);
620  p3 = vlib_get_buffer (vm, from[3]);
621 
622  vlib_prefetch_buffer_header (p2, LOAD);
623  vlib_prefetch_buffer_header (p3, LOAD);
624 
627  }
628 
629  /* speculatively enqueue b0 and b1 to the current next frame */
630  to_next[0] = bi0 = from[0];
631  to_next[1] = bi1 = from[1];
632  from += 2;
633  to_next += 2;
634  n_left_from -= 2;
635  n_left_to_next -= 2;
636 
637  b0 = vlib_get_buffer (vm, bi0);
638  b1 = vlib_get_buffer (vm, bi1);
639 
640  /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
641  ip0 = vlib_buffer_get_current (b0);
642  ip1 = vlib_buffer_get_current (b1);
643  adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
644  adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
645  adj0 = adj_get (adj_index0);
646  adj1 = adj_get (adj_index1);
647 
648  next0 = adj0->lookup_next_index;
649  next1 = adj1->lookup_next_index;
650 
651  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
652  hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
653 
654  ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
655  ioam_pop_hop_by_hop_processing (vm, ip1, hbh1, b1);
656 
657  vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
658  vlib_buffer_advance (b1, (hbh1->length + 1) << 3);
659 
660  new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
661  ((hbh0->length + 1) << 3);
662  new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
663  ((hbh1->length + 1) << 3);
664 
665  ip0->payload_length = clib_host_to_net_u16 (new_l0);
666  ip1->payload_length = clib_host_to_net_u16 (new_l1);
667 
668  ip0->protocol = hbh0->protocol;
669  ip1->protocol = hbh1->protocol;
670 
671  copy_src0 = (u64 *) ip0;
672  copy_src1 = (u64 *) ip1;
673  copy_dst0 = copy_src0 + (hbh0->length + 1);
674  copy_dst0[4] = copy_src0[4];
675  copy_dst0[3] = copy_src0[3];
676  copy_dst0[2] = copy_src0[2];
677  copy_dst0[1] = copy_src0[1];
678  copy_dst0[0] = copy_src0[0];
679  copy_dst1 = copy_src1 + (hbh1->length + 1);
680  copy_dst1[4] = copy_src1[4];
681  copy_dst1[3] = copy_src1[3];
682  copy_dst1[2] = copy_src1[2];
683  copy_dst1[1] = copy_src1[1];
684  copy_dst1[0] = copy_src1[0];
685  processed += 2;
686  /* $$$$$ End of processing 2 x packets $$$$$ */
687 
688  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
689  {
690  if (b0->flags & VLIB_BUFFER_IS_TRACED)
691  {
693  vlib_add_trace (vm, node, b0, sizeof (*t));
694  t->next_index = next0;
695  }
696  if (b1->flags & VLIB_BUFFER_IS_TRACED)
697  {
699  vlib_add_trace (vm, node, b1, sizeof (*t));
700  t->next_index = next1;
701  }
702  }
703 
704  /* verify speculative enqueues, maybe switch current next frame */
705  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
706  to_next, n_left_to_next,
707  bi0, bi1, next0, next1);
708  }
709 
710  while (n_left_from > 0 && n_left_to_next > 0)
711  {
712  u32 bi0;
713  vlib_buffer_t *b0;
714  u32 next0;
715  u32 adj_index0;
716  ip6_header_t *ip0;
717  ip_adjacency_t *adj0;
719  u64 *copy_dst0, *copy_src0;
720  u16 new_l0;
721 
722  /* speculatively enqueue b0 to the current next frame */
723  bi0 = from[0];
724  to_next[0] = bi0;
725  from += 1;
726  to_next += 1;
727  n_left_from -= 1;
728  n_left_to_next -= 1;
729 
730  b0 = vlib_get_buffer (vm, bi0);
731 
732  ip0 = vlib_buffer_get_current (b0);
733  adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
734  adj0 = adj_get (adj_index0);
735 
736  /* Default use the next_index from the adjacency. */
737  next0 = adj0->lookup_next_index;
738 
739  /* Perfectly normal to end up here w/ out h-b-h header */
740  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
741 
742  /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
743  ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
744  /* Pop the trace data */
745  vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
746  new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
747  ((hbh0->length + 1) << 3);
748  ip0->payload_length = clib_host_to_net_u16 (new_l0);
749  ip0->protocol = hbh0->protocol;
750  copy_src0 = (u64 *) ip0;
751  copy_dst0 = copy_src0 + (hbh0->length + 1);
752  copy_dst0[4] = copy_src0[4];
753  copy_dst0[3] = copy_src0[3];
754  copy_dst0[2] = copy_src0[2];
755  copy_dst0[1] = copy_src0[1];
756  copy_dst0[0] = copy_src0[0];
757  processed++;
758 
759  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
760  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
761  {
763  vlib_add_trace (vm, node, b0, sizeof (*t));
764  t->next_index = next0;
765  }
766 
767  /* verify speculative enqueue, maybe switch current next frame */
768  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
769  to_next, n_left_to_next,
770  bi0, next0);
771  }
772 
773  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
774  }
775 
776  vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
777  IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
778  vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
779  IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
780  return frame->n_vectors;
781 }
782 
783 /* *INDENT-OFF* */
784 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) =
785 {
786  .name =
787  "ip6-pop-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
789  VLIB_NODE_TYPE_INTERNAL,.sibling_of = "ip6-lookup",.n_errors =
790  ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),.error_strings =
792  /* See ip/lookup.h */
793 .n_next_nodes = 0,};
794 
795 /* *INDENT-ON* */
796 
797 #ifndef CLIB_MARCH_VARIANT
798 static clib_error_t *
800 {
801  clib_error_t *error;
803 
804  if ((error = vlib_call_init_function (vm, ip_main_init)))
805  return (error);
806 
807  if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
808  return error;
809 
810  hm->vlib_main = vm;
811  hm->vnet_main = vnet_get_main ();
812  hm->unix_time_0 = (u32) time (0); /* Store starting time */
813  hm->vlib_time_0 = vlib_time_now (vm);
814  hm->ioam_flag = IOAM_HBYH_MOD;
815  clib_memset (hm->add_options, 0, sizeof (hm->add_options));
816  clib_memset (hm->pop_options, 0, sizeof (hm->pop_options));
817  clib_memset (hm->options_size, 0, sizeof (hm->options_size));
818 
820 
821  return (0);
822 }
823 
825 
826 int
827 ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option,
828  int has_pot_option, int has_seqno_option)
829 {
831  u8 *rewrite = NULL;
832  u32 size, rnd_size;
834  u8 *current;
835  u8 *trace_data_size = NULL;
836  u8 *pot_data_size = NULL;
837 
838  vec_free (*rwp);
839 
840  if (has_trace_option == 0 && has_pot_option == 0)
841  return -1;
842 
843  /* Work out how much space we need */
844  size = sizeof (ip6_hop_by_hop_header_t);
845 
846  //if (has_trace_option && hm->get_sizeof_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
847  if (has_trace_option
849  {
851  }
852  if (has_pot_option
854  {
856  }
857 
858  if (has_seqno_option)
859  {
861  }
862 
863  /* Round to a multiple of 8 octets */
864  rnd_size = (size + 7) & ~7;
865 
866  /* allocate it, zero-fill / pad by construction */
867  vec_validate (rewrite, rnd_size - 1);
868 
869  hbh = (ip6_hop_by_hop_header_t *) rewrite;
870  /* Length of header in 8 octet units, not incl first 8 octets */
871  hbh->length = (rnd_size >> 3) - 1;
872  current = (u8 *) (hbh + 1);
873 
874  if (has_trace_option
876  {
878  {
879  trace_data_size =
881  if (0 ==
883  trace_data_size))
884  current += *trace_data_size;
885  }
886  }
887  if (has_pot_option
889  {
890  pot_data_size =
892  if (0 ==
894  pot_data_size))
895  current += *pot_data_size;
896  }
897 
898  if (has_seqno_option &&
900  {
901  if (0 == hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] (current,
902  &
903  (hm->options_size
906  }
907 
908  *rwp = rewrite;
909  return 0;
910 }
911 
912 clib_error_t *
914 {
916 
917  vec_free (hm->rewrite);
918  hm->rewrite = 0;
919  hm->has_trace_option = 0;
920  hm->has_pot_option = 0;
921  hm->has_seqno_option = 0;
922  hm->has_analyse_option = 0;
925 
928 
930  {
932  &hm->has_analyse_option,
933  1);
934  }
935 
936  return 0;
937 }
938 
939 clib_error_t *
941  unformat_input_t * input,
942  vlib_cli_command_t * cmd)
943 {
944  return (clear_ioam_rewrite_fn ());
945 }
946 
947 /*?
948  * This command clears all the In-band OAM (iOAM) features enabled by
949  * the '<em>set ioam rewrite</em>' command. Use '<em>show ioam summary</em>' to
950  * verify the configured settings cleared.
951  *
952  * @cliexpar
953  * Example of how to clear iOAM features:
954  * @cliexcmd{clear ioam rewrite}
955 ?*/
956 /* *INDENT-OFF* */
957 VLIB_CLI_COMMAND (ip6_clear_ioam_rewrite_cmd, static) = {
958  .path = "clear ioam rewrite",
959  .short_help = "clear ioam rewrite",
960  .function = clear_ioam_rewrite_command_fn,
961 };
962 /* *INDENT-ON* */
963 
964 clib_error_t *
965 ip6_ioam_enable (int has_trace_option, int has_pot_option,
966  int has_seqno_option, int has_analyse_option)
967 {
968  int rv;
970  rv = ip6_ioam_set_rewrite (&hm->rewrite, has_trace_option,
971  has_pot_option, has_seqno_option);
972 
973  switch (rv)
974  {
975  case 0:
976  if (has_trace_option)
977  {
978  hm->has_trace_option = has_trace_option;
981  0);
982  }
983 
984  if (has_pot_option)
985  {
986  hm->has_pot_option = has_pot_option;
989  0);
990  }
991  hm->has_analyse_option = has_analyse_option;
992  if (has_seqno_option)
993  {
994  hm->has_seqno_option = has_seqno_option;
996  {
998  &has_analyse_option,
999  0);
1000  }
1001  }
1002  break;
1003 
1004  default:
1005  return clib_error_return_code (0, rv, 0,
1006  "ip6_ioam_set_rewrite returned %d", rv);
1007  }
1008 
1009  return 0;
1010 }
1011 
1012 
1013 static clib_error_t *
1015  unformat_input_t * input,
1016  vlib_cli_command_t * cmd)
1017 {
1018  int has_trace_option = 0;
1019  int has_pot_option = 0;
1020  int has_seqno_option = 0;
1021  int has_analyse_option = 0;
1022  clib_error_t *rv = 0;
1023 
1024  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1025  {
1026  if (unformat (input, "trace"))
1027  has_trace_option = 1;
1028  else if (unformat (input, "pot"))
1029  has_pot_option = 1;
1030  else if (unformat (input, "seqno"))
1031  has_seqno_option = 1;
1032  else if (unformat (input, "analyse"))
1033  has_analyse_option = 1;
1034  else
1035  break;
1036  }
1037 
1038 
1039  rv = ip6_ioam_enable (has_trace_option, has_pot_option,
1040  has_seqno_option, has_analyse_option);
1041 
1042  return rv;
1043 }
1044 
1045 /*?
1046  * This command is used to enable In-band OAM (iOAM) features on IPv6.
1047  * '<em>trace</em>' is used to enable iOAM trace feature. '<em>pot</em>' is used to
1048  * enable the Proof Of Transit feature. '<em>ppc</em>' is used to indicate the
1049  * Per Packet Counter feature for Edge to Edge processing. '<em>ppc</em>' is
1050  * used to indicate if this node is an '<em>encap</em>' node (iOAM edge node
1051  * where packet enters iOAM domain), a '<em>decap</em>' node (iOAM edge node
1052  * where packet leaves iOAM domain) or '<em>none</em>' (iOAM node where packet
1053  * is in-transit through the iOAM domain). '<em>ppc</em>' can only be set if
1054  * '<em>trace</em>' or '<em>pot</em>' is enabled.
1055  *
1056  * Use '<em>clear ioam rewrite</em>' to disable all features enabled by this
1057  * command. Use '<em>show ioam summary</em>' to verify the configured settings.
1058  *
1059  * @cliexpar
1060  * Example of how to enable trace and pot with ppc set to encap:
1061  * @cliexcmd{set ioam rewrite trace pot ppc encap}
1062 ?*/
1063 /* *INDENT-OFF* */
1064 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1065  .path = "set ioam rewrite",
1066  .short_help = "set ioam [trace] [pot] [seqno] [analyse]",
1067  .function = ip6_set_ioam_rewrite_command_fn,
1068 };
1069 /* *INDENT-ON* */
1070 
1071 static clib_error_t *
1073  unformat_input_t * input,
1074  vlib_cli_command_t * cmd)
1075 {
1077  u8 *s = 0;
1078 
1079 
1080  if (!is_zero_ip6_address (&hm->adj))
1081  {
1082  s = format (s, " REWRITE FLOW CONFIGS - \n");
1083  s = format (s, " Destination Address : %U\n",
1084  format_ip6_address, &hm->adj, sizeof (ip6_address_t));
1085  s =
1086  format (s, " Flow operation : %d (%s)\n",
1087  hm->ioam_flag,
1088  (hm->ioam_flag ==
1089  IOAM_HBYH_ADD) ? "Add" : ((hm->ioam_flag ==
1090  IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1091  }
1092  else
1093  {
1094  s = format (s, " REWRITE FLOW CONFIGS - Not configured\n");
1095  }
1096 
1097 
1098  s = format (s, " TRACE OPTION - %d (%s)\n",
1099  hm->has_trace_option,
1100  (hm->has_trace_option ? "Enabled" : "Disabled"));
1101  if (hm->has_trace_option)
1102  s =
1103  format (s,
1104  "Try 'show ioam trace and show ioam-trace profile' for more information\n");
1105 
1106 
1107  s = format (s, " POT OPTION - %d (%s)\n",
1108  hm->has_pot_option,
1109  (hm->has_pot_option ? "Enabled" : "Disabled"));
1110  if (hm->has_pot_option)
1111  s =
1112  format (s,
1113  "Try 'show ioam pot and show pot profile' for more information\n");
1114 
1115  s = format (s, " EDGE TO EDGE - SeqNo OPTION - %d (%s)\n",
1116  hm->has_seqno_option,
1117  hm->has_seqno_option ? "Enabled" : "Disabled");
1118  if (hm->has_seqno_option)
1119  s = format (s, "Try 'show ioam e2e' for more information\n");
1120 
1121  s = format (s, " iOAM Analyse OPTION - %d (%s)\n",
1122  hm->has_analyse_option,
1123  hm->has_analyse_option ? "Enabled" : "Disabled");
1124 
1125  vlib_cli_output (vm, "%v", s);
1126  vec_free (s);
1127  return 0;
1128 }
1129 
1130 /*?
1131  * This command displays the current configuration data for In-band
1132  * OAM (iOAM).
1133  *
1134  * @cliexpar
1135  * Example to show the iOAM configuration:
1136  * @cliexstart{show ioam summary}
1137  * REWRITE FLOW CONFIGS -
1138  * Destination Address : ff02::1
1139  * Flow operation : 2 (Pop)
1140  * TRACE OPTION - 1 (Enabled)
1141  * Try 'show ioam trace and show ioam-trace profile' for more information
1142  * POT OPTION - 1 (Enabled)
1143  * Try 'show ioam pot and show pot profile' for more information
1144  * EDGE TO EDGE - PPC OPTION - 1 (Encap)
1145  * @cliexend
1146 ?*/
1147 /* *INDENT-OFF* */
1148 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1149  .path = "show ioam summary",
1150  .short_help = "show ioam summary",
1151  .function = ip6_show_ioam_summary_cmd_fn,
1152 };
1153 /* *INDENT-ON* */
1154 
1155 void
1157 {
1159 
1160  hm->ioam_end_of_path_cb = cb;
1161 }
1162 
1163 #endif /* CLIB_MARCH_VARIANT */
1164 /*
1165  * fd.io coding-style-patch-verification: ON
1166  *
1167  * Local Variables:
1168  * eval: (c-set-style "gnu")
1169  * End:
1170  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
u32 flags
buffer flags: VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index, VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:124
ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main
#define CLIB_UNUSED(x)
Definition: clib.h:82
static void ioam_pop_hop_by_hop_processing(vlib_main_t *vm, ip6_header_t *ip0, ip6_hop_by_hop_header_t *hbh0, vlib_buffer_t *b)
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
static clib_error_t * ip6_show_ioam_summary_cmd_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
unsigned long u64
Definition: types.h:89
#define IOAM_HBYH_MOD
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
#define NULL
Definition: clib.h:58
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:255
IP unicast adjacency.
Definition: adj.h:221
static clib_error_t * ip6_set_ioam_rewrite_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
u8 data[0]
Packet data.
Definition: buffer.h:181
static u8 * format_ip6_add_hop_by_hop_trace(u8 *s, va_list *args)
clib_memset(h->entries, 0, sizeof(h->entries[0])*entries)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
u8 data[128]
Definition: ipsec.api:248
#define VLIB_NODE_FN(node)
Definition: node.h:201
unsigned char u8
Definition: types.h:56
ip_lookup_next_t
An adjacency is a representation of an attached L3 peer.
Definition: adj.h:50
int ip6_hbh_flow_handler_register(u8 option, u32 ioam_flow_handler(u32 flow_ctx, u8 add))
int ip6_hbh_flow_handler_unregister(u8 option)
int ip6_hbh_pop_unregister_option(u8 option)
int ip6_hbh_config_handler_unregister(u8 option)
static char * ip6_add_hop_by_hop_error_strings[]
static ip_adjacency_t * adj_get(adj_index_t adj_index)
Get a pointer to an adjacency object from its index.
Definition: adj.h:433
#define HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT
i64 word
Definition: types.h:111
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:163
#define HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE
int ip6_hbh_pop_register_option(u8 option, int options(vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt))
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
Definition: buffer.h:203
ip6_pop_hop_by_hop_error_t
vlib_node_registration_t ip6_pop_hop_by_hop_node
(constructor) VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node)
unsigned int u32
Definition: types.h:88
#define vlib_call_init_function(vm, x)
Definition: init.h:260
int ip6_ioam_set_rewrite(u8 **rwp, int has_trace_option, int has_pot_option, int has_seqno_option)
u8 options_size[MAX_IP6_HBH_OPTION]
static uword unformat_opaque_ioam(unformat_input_t *input, va_list *args)
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
uword size
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
#define foreach_ip6_pop_hop_by_hop_error
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:229
#define PREDICT_FALSE(x)
Definition: clib.h:111
void vnet_classify_register_unformat_opaque_index_fn(unformat_function_t *fn)
#define vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, n_left_to_next, bi0, bi1, next0, next1)
Finish enqueueing two buffers forward in the graph.
Definition: buffer_node.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:368
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1180
The fine-grained event logger allows lightweight, thread-safe event logging at minimum cost...
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
clib_error_t * ip_main_init(vlib_main_t *vm)
Definition: ip_init.c:45
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
u8 flow_name[64]
u32(* flow_handler[MAX_IP6_HBH_OPTION])(u32 flow_ctx, u8 add)
static u8 * format_ip6_pop_hop_by_hop_trace(u8 *s, va_list *args)
format_function_t format_ip6_address
Definition: format.h:93
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:80
vlib_main_t * vm
Definition: buffer.c:312
int ip6_hbh_config_handler_register(u8 option, int config_handler(void *data, u8 disable))
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
int ip6_hbh_add_unregister_option(u8 option)
static u32 ioam_flow_add(u8 encap, u8 *flow_name)
clib_error_t * ip6_ioam_enable(int has_trace_option, int has_pot_option, int has_seqno_option, int has_analyse_option)
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:283
#define ARRAY_LEN(x)
Definition: clib.h:62
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:465
void vnet_register_ioam_end_of_path_callback(void *cb)
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:155
static char * ip6_pop_hop_by_hop_error_strings[]
#define ASSERT(truth)
static u8 is_zero_ip6_address(ip6_address_t *a)
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:248
#define HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
int(* config_handler[MAX_IP6_HBH_OPTION])(void *data, u8 disable)
#define foreach_ip6_hbyh_ioam_input_next
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
struct _vlib_node_registration vlib_node_registration_t
u8 * get_flow_name_from_flow_ctx(u32 flow_ctx)
Definition: defs.h:47
u16 payload_length
Definition: ip6_packet.h:376
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
ip6_hbyh_ioam_input_next_t
static clib_error_t * ip6_hop_by_hop_ioam_init(vlib_main_t *vm)
ip_lookup_next_t lookup_next_index
Next hop after ip4-lookup.
Definition: adj.h:236
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:274
clib_error_t * clear_ioam_rewrite_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
#define vnet_buffer(b)
Definition: buffer.h:369
#define foreach_ip6_add_hop_by_hop_error
#define IOAM_MASK_DECAP_BIT(x)
#define clib_error_return_code(e, code, flags, args...)
Definition: error.h:93
clib_error_t * clear_ioam_rewrite_fn(void)
int ip6_hbh_add_register_option(u8 option, u8 size, int rewrite_options(u8 *rewrite_string, u8 *rewrite_size))
int(* pop_options[MAX_IP6_HBH_OPTION])(vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt)
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:301
static clib_error_t * ip6_lookup_init(vlib_main_t *vm)
Definition: ip6_forward.c:2692
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:59
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:762
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
icmpr_flow_t * flow
Definition: main.c:123
vlib_node_registration_t ip6_add_hop_by_hop_node
(constructor) VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node)
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
#define IOAM_HBYH_ADD
int(* add_options[MAX_IP6_HBH_OPTION])(u8 *rewrite_string, u8 *rewrite_size)
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170
ip6_add_hop_by_hop_error_t