FD.io VPP  v17.01.1-3-gc6833f8
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 
45 
46 #define foreach_ip6_hbyh_ioam_input_next \
47  _(IP6_REWRITE, "ip6-rewrite") \
48  _(IP6_LOOKUP, "ip6-lookup") \
49  _(DROP, "error-drop")
50 
51 typedef enum
52 {
53 #define _(s,n) IP6_HBYH_IOAM_INPUT_NEXT_##s,
55 #undef _
58 
59 
60 u32
61 ioam_flow_add (u8 encap, u8 * flow_name)
62 {
64  flow_data_t *flow = 0;
65  u32 index = 0;
66  u8 i;
67 
68  pool_get (hm->flows, flow);
69  memset (flow, 0, sizeof (flow_data_t));
70 
71  index = flow - hm->flows;
72  strncpy ((char *) flow->flow_name, (char *) flow_name, 31);
73 
74  if (!encap)
75  IOAM_SET_DECAP (index);
76 
77  for (i = 0; i < 255; i++)
78  {
79  if (hm->flow_handler[i])
80  flow->ctx[i] = hm->flow_handler[i] (index, 1);
81  }
82  return (index);
83 }
84 
85 static uword
86 unformat_opaque_ioam (unformat_input_t * input, va_list * args)
87 {
88  u64 *opaquep = va_arg (*args, u64 *);
89  u8 *flow_name = NULL;
90  uword ret = 0;
91 
92  if (unformat (input, "ioam-encap %s", &flow_name))
93  {
94  *opaquep = ioam_flow_add (1, flow_name);
95  ret = 1;
96  }
97  else if (unformat (input, "ioam-decap %s", &flow_name))
98  {
99  *opaquep = ioam_flow_add (0, flow_name);
100  ret = 1;
101  }
102 
103  vec_free (flow_name);
104  return ret;
105 }
106 
107 u8 *
109 {
110  flow_data_t *flow = NULL;
112  u32 index;
113 
114  index = IOAM_MASK_DECAP_BIT (flow_ctx);
115 
116  if (pool_is_free_index (hm->flows, index))
117  return NULL;
118 
119  flow = pool_elt_at_index (hm->flows, index);
120  return (flow->flow_name);
121 }
122 
123 /* The main h-b-h tracer will be invoked, no need to do much here */
124 int
126  u8 size,
127  int rewrite_options (u8 * rewrite_string,
128  u8 * rewrite_size))
129 {
131 
132  ASSERT (option < ARRAY_LEN (hm->add_options));
133 
134  /* Already registered */
135  if (hm->add_options[option])
136  return (-1);
137 
138  hm->add_options[option] = rewrite_options;
139  hm->options_size[option] = size;
140 
141  return (0);
142 }
143 
144 int
146 {
148 
149  ASSERT (option < ARRAY_LEN (hm->add_options));
150 
151  /* Not registered */
152  if (!hm->add_options[option])
153  return (-1);
154 
155  hm->add_options[option] = NULL;
156  hm->options_size[option] = 0;
157  return (0);
158 }
159 
160 /* Config handler registration */
161 int
163  int config_handler (void *data, u8 disable))
164 {
166 
167  ASSERT (option < ARRAY_LEN (hm->config_handler));
168 
169  /* Already registered */
170  if (hm->config_handler[option])
171  return (VNET_API_ERROR_INVALID_REGISTRATION);
172 
173  hm->config_handler[option] = config_handler;
174 
175  return (0);
176 }
177 
178 int
180 {
182 
183  ASSERT (option < ARRAY_LEN (hm->config_handler));
184 
185  /* Not registered */
186  if (!hm->config_handler[option])
187  return (VNET_API_ERROR_INVALID_REGISTRATION);
188 
189  hm->config_handler[option] = NULL;
190  return (0);
191 }
192 
193 /* Flow handler registration */
194 int
196  u32 ioam_flow_handler (u32 flow_ctx, u8 add))
197 {
199 
200  ASSERT (option < ARRAY_LEN (hm->flow_handler));
201 
202  /* Already registered */
203  if (hm->flow_handler[option])
204  return (VNET_API_ERROR_INVALID_REGISTRATION);
205 
206  hm->flow_handler[option] = ioam_flow_handler;
207 
208  return (0);
209 }
210 
211 int
213 {
215 
216  ASSERT (option < ARRAY_LEN (hm->flow_handler));
217 
218  /* Not registered */
219  if (!hm->flow_handler[option])
220  return (VNET_API_ERROR_INVALID_REGISTRATION);
221 
222  hm->flow_handler[option] = NULL;
223  return (0);
224 }
225 
226 typedef struct
227 {
230 
231 /* packet trace format function */
232 static u8 *
233 format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
234 {
235  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
236  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
237  ip6_add_hop_by_hop_trace_t *t = va_arg (*args,
239 
240  s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d", t->next_index);
241  return s;
242 }
243 
245 
246 #define foreach_ip6_add_hop_by_hop_error \
247 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
248 
249 typedef enum
250 {
251 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
253 #undef _
256 
257 static char *ip6_add_hop_by_hop_error_strings[] = {
258 #define _(sym,string) string,
260 #undef _
261 };
262 
263 static uword
265  vlib_node_runtime_t * node, vlib_frame_t * frame)
266 {
268  u32 n_left_from, *from, *to_next;
269  ip_lookup_next_t next_index;
270  u32 processed = 0;
271  u8 *rewrite = hm->rewrite;
272  u32 rewrite_length = vec_len (rewrite);
273 
274  from = vlib_frame_vector_args (frame);
275  n_left_from = frame->n_vectors;
276  next_index = node->cached_next_index;
277 
278  while (n_left_from > 0)
279  {
280  u32 n_left_to_next;
281 
282  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
283  while (n_left_from >= 4 && n_left_to_next >= 2)
284  {
285  u32 bi0, bi1;
286  vlib_buffer_t *b0, *b1;
287  u32 next0, next1;
288  ip6_header_t *ip0, *ip1;
289  ip6_hop_by_hop_header_t *hbh0, *hbh1;
290  u64 *copy_src0, *copy_dst0, *copy_src1, *copy_dst1;
291  u16 new_l0, new_l1;
292 
293  /* Prefetch next iteration. */
294  {
295  vlib_buffer_t *p2, *p3;
296 
297  p2 = vlib_get_buffer (vm, from[2]);
298  p3 = vlib_get_buffer (vm, from[3]);
299 
300  vlib_prefetch_buffer_header (p2, LOAD);
301  vlib_prefetch_buffer_header (p3, LOAD);
302 
303  CLIB_PREFETCH (p2->data - rewrite_length,
304  2 * CLIB_CACHE_LINE_BYTES, STORE);
305  CLIB_PREFETCH (p3->data - rewrite_length,
306  2 * CLIB_CACHE_LINE_BYTES, STORE);
307  }
308 
309  /* speculatively enqueue b0 and b1 to the current next frame */
310  to_next[0] = bi0 = from[0];
311  to_next[1] = bi1 = from[1];
312  from += 2;
313  to_next += 2;
314  n_left_from -= 2;
315  n_left_to_next -= 2;
316 
317  b0 = vlib_get_buffer (vm, bi0);
318  b1 = vlib_get_buffer (vm, bi1);
319 
320  /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
321  ip0 = vlib_buffer_get_current (b0);
322  ip1 = vlib_buffer_get_current (b1);
323 
324  /* Copy the ip header left by the required amount */
325  copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
326  copy_dst1 = (u64 *) (((u8 *) ip1) - rewrite_length);
327  copy_src0 = (u64 *) ip0;
328  copy_src1 = (u64 *) ip1;
329 
330  copy_dst0[0] = copy_src0[0];
331  copy_dst0[1] = copy_src0[1];
332  copy_dst0[2] = copy_src0[2];
333  copy_dst0[3] = copy_src0[3];
334  copy_dst0[4] = copy_src0[4];
335 
336  copy_dst1[0] = copy_src1[0];
337  copy_dst1[1] = copy_src1[1];
338  copy_dst1[2] = copy_src1[2];
339  copy_dst1[3] = copy_src1[3];
340  copy_dst1[4] = copy_src1[4];
341 
342  vlib_buffer_advance (b0, -(word) rewrite_length);
343  vlib_buffer_advance (b1, -(word) rewrite_length);
344  ip0 = vlib_buffer_get_current (b0);
345  ip1 = vlib_buffer_get_current (b1);
346 
347  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
348  hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
349  /* $$$ tune, rewrite_length is a multiple of 8 */
350  clib_memcpy (hbh0, rewrite, rewrite_length);
351  clib_memcpy (hbh1, rewrite, rewrite_length);
352  /* Patch the protocol chain, insert the h-b-h (type 0) header */
353  hbh0->protocol = ip0->protocol;
354  hbh1->protocol = ip1->protocol;
355  ip0->protocol = 0;
356  ip1->protocol = 0;
357  new_l0 =
358  clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
359  new_l1 =
360  clib_net_to_host_u16 (ip1->payload_length) + rewrite_length;
361  ip0->payload_length = clib_host_to_net_u16 (new_l0);
362  ip1->payload_length = clib_host_to_net_u16 (new_l1);
363 
364  /* Populate the (first) h-b-h list elt */
365  next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
366  next1 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
367 
368 
369  /* $$$$$ End of processing 2 x packets $$$$$ */
370 
371  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
372  {
373  if (b0->flags & VLIB_BUFFER_IS_TRACED)
374  {
376  vlib_add_trace (vm, node, b0, sizeof (*t));
377  t->next_index = next0;
378  }
379  if (b1->flags & VLIB_BUFFER_IS_TRACED)
380  {
382  vlib_add_trace (vm, node, b1, sizeof (*t));
383  t->next_index = next1;
384  }
385  }
386  processed += 2;
387  /* verify speculative enqueues, maybe switch current next frame */
388  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
389  to_next, n_left_to_next,
390  bi0, bi1, next0, next1);
391  }
392  while (n_left_from > 0 && n_left_to_next > 0)
393  {
394  u32 bi0;
395  vlib_buffer_t *b0;
396  u32 next0;
397  ip6_header_t *ip0;
399  u64 *copy_src0, *copy_dst0;
400  u16 new_l0;
401 
402  /* speculatively enqueue b0 to the current next frame */
403  bi0 = from[0];
404  to_next[0] = bi0;
405  from += 1;
406  to_next += 1;
407  n_left_from -= 1;
408  n_left_to_next -= 1;
409 
410  b0 = vlib_get_buffer (vm, bi0);
411 
412  ip0 = vlib_buffer_get_current (b0);
413 
414  /* Copy the ip header left by the required amount */
415  copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
416  copy_src0 = (u64 *) ip0;
417 
418  copy_dst0[0] = copy_src0[0];
419  copy_dst0[1] = copy_src0[1];
420  copy_dst0[2] = copy_src0[2];
421  copy_dst0[3] = copy_src0[3];
422  copy_dst0[4] = copy_src0[4];
423  vlib_buffer_advance (b0, -(word) rewrite_length);
424  ip0 = vlib_buffer_get_current (b0);
425 
426  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
427  /* $$$ tune, rewrite_length is a multiple of 8 */
428  clib_memcpy (hbh0, rewrite, rewrite_length);
429  /* Patch the protocol chain, insert the h-b-h (type 0) header */
430  hbh0->protocol = ip0->protocol;
431  ip0->protocol = 0;
432  new_l0 =
433  clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
434  ip0->payload_length = clib_host_to_net_u16 (new_l0);
435 
436  /* Populate the (first) h-b-h list elt */
437  next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
438 
440  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
441  {
443  vlib_add_trace (vm, node, b0, sizeof (*t));
444  t->next_index = next0;
445  }
446 
447  processed++;
448 
449  /* verify speculative enqueue, maybe switch current next frame */
450  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
451  to_next, n_left_to_next,
452  bi0, next0);
453  }
454 
455  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
456  }
457 
458  vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
459  IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
460  return frame->n_vectors;
461 }
462 
463 /* *INDENT-OFF* */
464 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = /* *INDENT-OFF* */
465 {
466  .function = ip6_add_hop_by_hop_node_fn,.name =
467  "ip6-add-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
469  VLIB_NODE_TYPE_INTERNAL,.n_errors =
470  ARRAY_LEN (ip6_add_hop_by_hop_error_strings),.error_strings =
472  /* See ip/lookup.h */
473  .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,.next_nodes =
474  {
475 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
477 #undef _
478  }
479 ,};
480 /* *INDENT-ON* */
481 
482 /* *INDENT-ON* */
483 
484 VLIB_NODE_FUNCTION_MULTIARCH (ip6_add_hop_by_hop_node,
486 /* The main h-b-h tracer was already invoked, no need to do much here */
487 typedef struct
488 {
491 
492 /* packet trace format function */
493 static u8 *
494 format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
495 {
496  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
497  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
499  va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
500 
501  s = format (s, "IP6_POP_HOP_BY_HOP: next index %d", t->next_index);
502  return s;
503 }
504 
505 int
507  int options (vlib_buffer_t * b,
508  ip6_header_t * ip,
510 {
512 
513  ASSERT (option < ARRAY_LEN (hm->pop_options));
514 
515  /* Already registered */
516  if (hm->pop_options[option])
517  return (-1);
518 
519  hm->pop_options[option] = options;
520 
521  return (0);
522 }
523 
524 int
526 {
528 
529  ASSERT (option < ARRAY_LEN (hm->pop_options));
530 
531  /* Not registered */
532  if (!hm->pop_options[option])
533  return (-1);
534 
535  hm->pop_options[option] = NULL;
536  return (0);
537 }
538 
540 
541 #define foreach_ip6_pop_hop_by_hop_error \
542 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \
543 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
544 _(OPTION_FAILED, "ip6 pop hop-by-hop failed to process")
545 
546 typedef enum
547 {
548 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
550 #undef _
553 
554 static char *ip6_pop_hop_by_hop_error_strings[] = {
555 #define _(sym,string) string,
557 #undef _
558 };
559 
560 static inline void
562  ip6_header_t * ip0,
564  vlib_buffer_t * b)
565 {
567  ip6_hop_by_hop_option_t *opt0, *limit0;
568  u8 type0;
569 
570  if (!hbh0 || !ip0)
571  return;
572 
573  opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
574  limit0 = (ip6_hop_by_hop_option_t *)
575  ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
576 
577  /* Scan the set of h-b-h options, process ones that we understand */
578  while (opt0 < limit0)
579  {
580  type0 = opt0->type;
581  switch (type0)
582  {
583  case 0: /* Pad1 */
584  opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
585  continue;
586  case 1: /* PadN */
587  break;
588  default:
589  if (hm->pop_options[type0])
590  {
591  if ((*hm->pop_options[type0]) (b, ip0, opt0) < 0)
592  {
594  ip6_pop_hop_by_hop_node.index,
595  IP6_POP_HOP_BY_HOP_ERROR_OPTION_FAILED,
596  1);
597  }
598  }
599  }
600  opt0 =
601  (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
602  sizeof (ip6_hop_by_hop_option_t));
603  }
604 }
605 
606 static uword
608  vlib_node_runtime_t * node, vlib_frame_t * frame)
609 {
610  ip6_main_t *im = &ip6_main;
611  ip_lookup_main_t *lm = &im->lookup_main;
612  u32 n_left_from, *from, *to_next;
613  ip_lookup_next_t next_index;
614  u32 processed = 0;
615  u32 no_header = 0;
616 
617  from = vlib_frame_vector_args (frame);
618  n_left_from = frame->n_vectors;
619  next_index = node->cached_next_index;
620 
621  while (n_left_from > 0)
622  {
623  u32 n_left_to_next;
624 
625  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
626 
627  while (n_left_from >= 4 && n_left_to_next >= 2)
628  {
629  u32 bi0, bi1;
630  vlib_buffer_t *b0, *b1;
631  u32 next0, next1;
632  u32 adj_index0, adj_index1;
633  ip6_header_t *ip0, *ip1;
634  ip_adjacency_t *adj0, *adj1;
635  ip6_hop_by_hop_header_t *hbh0, *hbh1;
636  u64 *copy_dst0, *copy_src0, *copy_dst1, *copy_src1;
637  u16 new_l0, new_l1;
638 
639  /* Prefetch next iteration. */
640  {
641  vlib_buffer_t *p2, *p3;
642 
643  p2 = vlib_get_buffer (vm, from[2]);
644  p3 = vlib_get_buffer (vm, from[3]);
645 
646  vlib_prefetch_buffer_header (p2, LOAD);
647  vlib_prefetch_buffer_header (p3, LOAD);
648 
651  }
652 
653  /* speculatively enqueue b0 and b1 to the current next frame */
654  to_next[0] = bi0 = from[0];
655  to_next[1] = bi1 = from[1];
656  from += 2;
657  to_next += 2;
658  n_left_from -= 2;
659  n_left_to_next -= 2;
660 
661  b0 = vlib_get_buffer (vm, bi0);
662  b1 = vlib_get_buffer (vm, bi1);
663 
664  /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
665  ip0 = vlib_buffer_get_current (b0);
666  ip1 = vlib_buffer_get_current (b1);
667  adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
668  adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
669  adj0 = ip_get_adjacency (lm, adj_index0);
670  adj1 = ip_get_adjacency (lm, adj_index1);
671 
672  next0 = adj0->lookup_next_index;
673  next1 = adj1->lookup_next_index;
674 
675  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
676  hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
677 
678  ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
679  ioam_pop_hop_by_hop_processing (vm, ip1, hbh1, b1);
680 
681  vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
682  vlib_buffer_advance (b1, (hbh1->length + 1) << 3);
683 
684  new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
685  ((hbh0->length + 1) << 3);
686  new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
687  ((hbh1->length + 1) << 3);
688 
689  ip0->payload_length = clib_host_to_net_u16 (new_l0);
690  ip1->payload_length = clib_host_to_net_u16 (new_l1);
691 
692  ip0->protocol = hbh0->protocol;
693  ip1->protocol = hbh1->protocol;
694 
695  copy_src0 = (u64 *) ip0;
696  copy_src1 = (u64 *) ip1;
697  copy_dst0 = copy_src0 + (hbh0->length + 1);
698  copy_dst0[4] = copy_src0[4];
699  copy_dst0[3] = copy_src0[3];
700  copy_dst0[2] = copy_src0[2];
701  copy_dst0[1] = copy_src0[1];
702  copy_dst0[0] = copy_src0[0];
703  copy_dst1 = copy_src1 + (hbh1->length + 1);
704  copy_dst1[4] = copy_src1[4];
705  copy_dst1[3] = copy_src1[3];
706  copy_dst1[2] = copy_src1[2];
707  copy_dst1[1] = copy_src1[1];
708  copy_dst1[0] = copy_src1[0];
709  processed += 2;
710  /* $$$$$ End of processing 2 x packets $$$$$ */
711 
712  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
713  {
714  if (b0->flags & VLIB_BUFFER_IS_TRACED)
715  {
717  vlib_add_trace (vm, node, b0, sizeof (*t));
718  t->next_index = next0;
719  }
720  if (b1->flags & VLIB_BUFFER_IS_TRACED)
721  {
723  vlib_add_trace (vm, node, b1, sizeof (*t));
724  t->next_index = next1;
725  }
726  }
727 
728  /* verify speculative enqueues, maybe switch current next frame */
729  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
730  to_next, n_left_to_next,
731  bi0, bi1, next0, next1);
732  }
733 
734  while (n_left_from > 0 && n_left_to_next > 0)
735  {
736  u32 bi0;
737  vlib_buffer_t *b0;
738  u32 next0;
739  u32 adj_index0;
740  ip6_header_t *ip0;
741  ip_adjacency_t *adj0;
743  u64 *copy_dst0, *copy_src0;
744  u16 new_l0;
745 
746  /* speculatively enqueue b0 to the current next frame */
747  bi0 = from[0];
748  to_next[0] = bi0;
749  from += 1;
750  to_next += 1;
751  n_left_from -= 1;
752  n_left_to_next -= 1;
753 
754  b0 = vlib_get_buffer (vm, bi0);
755 
756  ip0 = vlib_buffer_get_current (b0);
757  adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
758  adj0 = ip_get_adjacency (lm, adj_index0);
759 
760  /* Default use the next_index from the adjacency. */
761  next0 = adj0->lookup_next_index;
762 
763  /* Perfectly normal to end up here w/ out h-b-h header */
764  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
765 
766  /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
767  ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
768  /* Pop the trace data */
769  vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
770  new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
771  ((hbh0->length + 1) << 3);
772  ip0->payload_length = clib_host_to_net_u16 (new_l0);
773  ip0->protocol = hbh0->protocol;
774  copy_src0 = (u64 *) ip0;
775  copy_dst0 = copy_src0 + (hbh0->length + 1);
776  copy_dst0[4] = copy_src0[4];
777  copy_dst0[3] = copy_src0[3];
778  copy_dst0[2] = copy_src0[2];
779  copy_dst0[1] = copy_src0[1];
780  copy_dst0[0] = copy_src0[0];
781  processed++;
782 
784  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
785  {
787  vlib_add_trace (vm, node, b0, sizeof (*t));
788  t->next_index = next0;
789  }
790 
791  /* verify speculative enqueue, maybe switch current next frame */
792  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
793  to_next, n_left_to_next,
794  bi0, next0);
795  }
796 
797  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
798  }
799 
800  vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
801  IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
802  vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
803  IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
804  return frame->n_vectors;
805 }
806 
807 /* *INDENT-OFF* */
808 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) =
809 {
810  .function = ip6_pop_hop_by_hop_node_fn,.name =
811  "ip6-pop-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
813  VLIB_NODE_TYPE_INTERNAL,.sibling_of = "ip6-lookup",.n_errors =
814  ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),.error_strings =
816  /* See ip/lookup.h */
817 .n_next_nodes = 0,};
818 
819 /* *INDENT-ON* */
820 
821 VLIB_NODE_FUNCTION_MULTIARCH (ip6_pop_hop_by_hop_node,
823 static clib_error_t *
825 {
826  clib_error_t *error;
828 
829  if ((error = vlib_call_init_function (vm, ip_main_init)))
830  return (error);
831 
832  if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
833  return error;
834 
835  hm->vlib_main = vm;
836  hm->vnet_main = vnet_get_main ();
837  hm->unix_time_0 = (u32) time (0); /* Store starting time */
838  hm->vlib_time_0 = vlib_time_now (vm);
839  hm->ioam_flag = IOAM_HBYH_MOD;
840  memset (hm->add_options, 0, sizeof (hm->add_options));
841  memset (hm->pop_options, 0, sizeof (hm->pop_options));
842  memset (hm->options_size, 0, sizeof (hm->options_size));
843 
845 
846  return (0);
847 }
848 
850 
851 int
852 ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option,
853  int has_pot_option, int has_seqno_option)
854 {
856  u8 *rewrite = NULL;
857  u32 size, rnd_size;
859  u8 *current;
860  u8 *trace_data_size = NULL;
861  u8 *pot_data_size = NULL;
862 
863  vec_free (*rwp);
864 
865  if (has_trace_option == 0 && has_pot_option == 0)
866  return -1;
867 
868  /* Work out how much space we need */
869  size = sizeof (ip6_hop_by_hop_header_t);
870 
871  //if (has_trace_option && hm->get_sizeof_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
872  if (has_trace_option
874  {
876  }
877  if (has_pot_option
879  {
881  }
882 
883  if (has_seqno_option)
884  {
886  }
887 
888  /* Round to a multiple of 8 octets */
889  rnd_size = (size + 7) & ~7;
890 
891  /* allocate it, zero-fill / pad by construction */
892  vec_validate (rewrite, rnd_size - 1);
893 
894  hbh = (ip6_hop_by_hop_header_t *) rewrite;
895  /* Length of header in 8 octet units, not incl first 8 octets */
896  hbh->length = (rnd_size >> 3) - 1;
897  current = (u8 *) (hbh + 1);
898 
899  if (has_trace_option
901  {
903  {
904  trace_data_size =
906  if (0 ==
908  trace_data_size))
909  current += *trace_data_size;
910  }
911  }
912  if (has_pot_option
914  {
915  pot_data_size =
917  if (0 ==
919  pot_data_size))
920  current += *pot_data_size;
921  }
922 
923  if (has_seqno_option &&
925  {
926  if (0 == hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] (current,
927  &
928  (hm->options_size
931  }
932 
933  *rwp = rewrite;
934  return 0;
935 }
936 
937 clib_error_t *
939 {
941 
942  vec_free (hm->rewrite);
943  hm->rewrite = 0;
944  hm->has_trace_option = 0;
945  hm->has_pot_option = 0;
946  hm->has_seqno_option = 0;
947  hm->has_analyse_option = 0;
950 
953 
955  {
957  &hm->has_analyse_option,
958  1);
959  }
960 
961  return 0;
962 }
963 
964 clib_error_t *
966  unformat_input_t * input,
967  vlib_cli_command_t * cmd)
968 {
969  return (clear_ioam_rewrite_fn ());
970 }
971 
972 /*?
973  * This command clears all the In-band OAM (iOAM) features enabled by
974  * the '<em>set ioam rewrite</em>' command. Use '<em>show ioam summary</em>' to
975  * verify the configured settings cleared.
976  *
977  * @cliexpar
978  * Example of how to clear iOAM features:
979  * @cliexcmd{clear ioam rewrite}
980 ?*/
981 /* *INDENT-OFF* */
982 VLIB_CLI_COMMAND (ip6_clear_ioam_rewrite_cmd, static) = {
983  .path = "clear ioam rewrite",
984  .short_help = "clear ioam rewrite",
985  .function = clear_ioam_rewrite_command_fn,
986 };
987 /* *INDENT-ON* */
988 
989 clib_error_t *
990 ip6_ioam_enable (int has_trace_option, int has_pot_option,
991  int has_seqno_option, int has_analyse_option)
992 {
993  int rv;
995  rv = ip6_ioam_set_rewrite (&hm->rewrite, has_trace_option,
996  has_pot_option, has_seqno_option);
997 
998  switch (rv)
999  {
1000  case 0:
1001  if (has_trace_option)
1002  {
1003  hm->has_trace_option = has_trace_option;
1006  0);
1007  }
1008 
1009  if (has_pot_option)
1010  {
1011  hm->has_pot_option = has_pot_option;
1014  0);
1015  }
1016  hm->has_analyse_option = has_analyse_option;
1017  if (has_seqno_option)
1018  {
1019  hm->has_seqno_option = has_seqno_option;
1021  {
1023  &has_analyse_option,
1024  0);
1025  }
1026  }
1027  break;
1028 
1029  default:
1030  return clib_error_return_code (0, rv, 0,
1031  "ip6_ioam_set_rewrite returned %d", rv);
1032  }
1033 
1034  return 0;
1035 }
1036 
1037 
1038 static clib_error_t *
1040  unformat_input_t * input,
1041  vlib_cli_command_t * cmd)
1042 {
1043  int has_trace_option = 0;
1044  int has_pot_option = 0;
1045  int has_seqno_option = 0;
1046  int has_analyse_option = 0;
1047  clib_error_t *rv = 0;
1048 
1049  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1050  {
1051  if (unformat (input, "trace"))
1052  has_trace_option = 1;
1053  else if (unformat (input, "pot"))
1054  has_pot_option = 1;
1055  else if (unformat (input, "seqno"))
1056  has_seqno_option = 1;
1057  else if (unformat (input, "analyse"))
1058  has_analyse_option = 1;
1059  else
1060  break;
1061  }
1062 
1063 
1064  rv = ip6_ioam_enable (has_trace_option, has_pot_option,
1065  has_seqno_option, has_analyse_option);
1066 
1067  return rv;
1068 }
1069 
1070 /*?
1071  * This command is used to enable In-band OAM (iOAM) features on IPv6.
1072  * '<em>trace</em>' is used to enable iOAM trace feature. '<em>pot</em>' is used to
1073  * enable the Proof Of Transit feature. '<em>ppc</em>' is used to indicate the
1074  * Per Packet Counter feature for Edge to Edge processing. '<em>ppc</em>' is
1075  * used to indicate if this node is an '<em>encap</em>' node (iOAM edge node
1076  * where packet enters iOAM domain), a '<em>decap</em>' node (iOAM edge node
1077  * where packet leaves iOAM domain) or '<em>none</em>' (iOAM node where packet
1078  * is in-transit through the iOAM domain). '<em>ppc</em>' can only be set if
1079  * '<em>trace</em>' or '<em>pot</em>' is enabled.
1080  *
1081  * Use '<em>clear ioam rewrite</em>' to disable all features enabled by this
1082  * command. Use '<em>show ioam summary</em>' to verify the configured settings.
1083  *
1084  * @cliexpar
1085  * Example of how to enable trace and pot with ppc set to encap:
1086  * @cliexcmd{set ioam rewrite trace pot ppc encap}
1087 ?*/
1088 /* *INDENT-OFF* */
1089 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1090  .path = "set ioam rewrite",
1091  .short_help = "set ioam [trace] [pot] [seqno] [analyse]",
1092  .function = ip6_set_ioam_rewrite_command_fn,
1093 };
1094 /* *INDENT-ON* */
1095 
1096 static clib_error_t *
1098  unformat_input_t * input,
1099  vlib_cli_command_t * cmd)
1100 {
1102  u8 *s = 0;
1103 
1104 
1105  if (!is_zero_ip6_address (&hm->adj))
1106  {
1107  s = format (s, " REWRITE FLOW CONFIGS - \n");
1108  s = format (s, " Destination Address : %U\n",
1109  format_ip6_address, &hm->adj, sizeof (ip6_address_t));
1110  s =
1111  format (s, " Flow operation : %d (%s)\n",
1112  hm->ioam_flag,
1113  (hm->ioam_flag ==
1114  IOAM_HBYH_ADD) ? "Add" : ((hm->ioam_flag ==
1115  IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1116  }
1117  else
1118  {
1119  s = format (s, " REWRITE FLOW CONFIGS - Not configured\n");
1120  }
1121 
1122 
1123  s = format (s, " TRACE OPTION - %d (%s)\n",
1124  hm->has_trace_option,
1125  (hm->has_trace_option ? "Enabled" : "Disabled"));
1126  if (hm->has_trace_option)
1127  s =
1128  format (s,
1129  "Try 'show ioam trace and show ioam-trace profile' for more information\n");
1130 
1131 
1132  s = format (s, " POT OPTION - %d (%s)\n",
1133  hm->has_pot_option,
1134  (hm->has_pot_option ? "Enabled" : "Disabled"));
1135  if (hm->has_pot_option)
1136  s =
1137  format (s,
1138  "Try 'show ioam pot and show pot profile' for more information\n");
1139 
1140  s = format (s, " EDGE TO EDGE - SeqNo OPTION - %d (%s)\n",
1141  hm->has_seqno_option,
1142  hm->has_seqno_option ? "Enabled" : "Disabled");
1143  if (hm->has_seqno_option)
1144  s = format (s, "Try 'show ioam e2e' for more information\n");
1145 
1146  s = format (s, " iOAM Analyse OPTION - %d (%s)\n",
1147  hm->has_analyse_option,
1148  hm->has_analyse_option ? "Enabled" : "Disabled");
1149 
1150  vlib_cli_output (vm, "%v", s);
1151  vec_free (s);
1152  return 0;
1153 }
1154 
1155 /*?
1156  * This command displays the current configuration data for In-band
1157  * OAM (iOAM).
1158  *
1159  * @cliexpar
1160  * Example to show the iOAM configuration:
1161  * @cliexstart{show ioam summary}
1162  * REWRITE FLOW CONFIGS -
1163  * Destination Address : ff02::1
1164  * Flow operation : 2 (Pop)
1165  * TRACE OPTION - 1 (Enabled)
1166  * Try 'show ioam trace and show ioam-trace profile' for more information
1167  * POT OPTION - 1 (Enabled)
1168  * Try 'show ioam pot and show pot profile' for more information
1169  * EDGE TO EDGE - PPC OPTION - 1 (Encap)
1170  * @cliexend
1171 ?*/
1172 /* *INDENT-OFF* */
1173 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1174  .path = "show ioam summary",
1175  .short_help = "show ioam summary",
1176  .function = ip6_show_ioam_summary_cmd_fn,
1177 };
1178 /* *INDENT-ON* */
1179 
1180 void
1182 {
1184 
1185  hm->ioam_end_of_path_cb = cb;
1186 }
1187 
1188 /*
1189  * fd.io coding-style-patch-verification: ON
1190  *
1191  * Local Variables:
1192  * eval: (c-set-style "gnu")
1193  * End:
1194  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:396
static uword ip6_pop_hop_by_hop_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
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:459
ip_lookup_next_t
Common (IP4/IP6) next index stored in adjacency.
Definition: lookup.h:60
ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main
#define clib_error_return_code(e, code, flags, args...)
Definition: error.h:105
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:343
#define CLIB_UNUSED(x)
Definition: clib.h:79
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:966
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)
VLIB_NODE_FUNCTION_MULTIARCH(ip6_add_hop_by_hop_node, ip6_add_hop_by_hop_node_fn)
format_function_t format_ip6_address
Definition: format.h:95
bad routing header type(not 4)") sr_error (NO_MORE_SEGMENTS
static clib_error_t * ip6_show_ioam_summary_cmd_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
u32 ioam_flow_add(u8 encap, u8 *flow_name)
#define IOAM_HBYH_MOD
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
#define NULL
Definition: clib.h:55
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:182
IP unicast adjacency.
Definition: lookup.h:188
static clib_error_t * ip6_set_ioam_rewrite_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
struct _vlib_node_registration vlib_node_registration_t
static u8 * format_ip6_add_hop_by_hop_trace(u8 *s, va_list *args)
u32 ctx[MAX_IP6_HBH_OPTION]
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:200
int ip6_hbh_flow_handler_register(u8 option, u32 ioam_flow_handler(u32 flow_ctx, u8 add))
static uword ip6_add_hop_by_hop_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
int ip6_hbh_flow_handler_unregister(u8 option)
int ip6_hbh_pop_unregister_option(u8 option)
int ip6_hbh_config_handler_unregister(u8 option)
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
static char * ip6_add_hop_by_hop_error_strings[]
#define HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
#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))
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:194
ip6_pop_hop_by_hop_error_t
unsigned long u64
Definition: types.h:89
vlib_node_registration_t ip6_pop_hop_by_hop_node
(constructor) VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node)
#define vlib_call_init_function(vm, x)
Definition: init.h:161
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:369
add
Definition: vector_sse2.h:285
#define foreach_ip6_pop_hop_by_hop_error
#define PREDICT_FALSE(x)
Definition: clib.h:97
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:216
#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:350
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:576
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1113
clib_error_t * ip_main_init(vlib_main_t *vm)
Definition: ip_init.c:45
u8 flow_name[64]
u32(* flow_handler[MAX_IP6_HBH_OPTION])(u32 flow_ctx, u8 add)
u16 n_vectors
Definition: node.h:344
static u8 * format_ip6_pop_hop_by_hop_trace(u8 *s, va_list *args)
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:82
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:300
int ip6_hbh_add_unregister_option(u8 option)
#define clib_memcpy(a, b, c)
Definition: string.h:69
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:207
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:211
#define ARRAY_LEN(x)
Definition: clib.h:59
void vnet_register_ioam_end_of_path_callback(void *cb)
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
static char * ip6_pop_hop_by_hop_error_strings[]
u16 cached_next_index
Definition: node.h:463
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
#define vnet_buffer(b)
Definition: buffer.h:361
ip6_main_t ip6_main
Definition: ip6_forward.c:2828
ip_lookup_main_t lookup_main
Definition: ip6.h:135
static u8 is_zero_ip6_address(ip6_address_t *a)
u64 size
Definition: vhost-user.h:74
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:259
#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
#define VLIB_BUFFER_IS_TRACED
Definition: buffer.h:95
u64 uword
Definition: types.h:112
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:55
#define IOAM_SET_DECAP(opaque_data)
u8 * get_flow_name_from_flow_ctx(u32 flow_ctx)
Definition: defs.h:47
unsigned short u16
Definition: types.h:57
u16 payload_length
Definition: ip6_packet.h:328
i64 word
Definition: types.h:111
#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)
unsigned char u8
Definition: types.h:56
ip_lookup_next_t lookup_next_index
Definition: lookup.h:199
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:253
clib_error_t * clear_ioam_rewrite_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
Definition: buffer.h:170
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
#define foreach_ip6_add_hop_by_hop_error
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:143
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:418
#define IOAM_MASK_DECAP_BIT(x)
u8 data[0]
Packet data.
Definition: buffer.h:158
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)
struct _unformat_input_t unformat_input_t
static clib_error_t * ip6_lookup_init(vlib_main_t *vm)
Definition: ip6_forward.c:2831
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
u32 flags
buffer flags: VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:85
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:57
vlib_node_registration_t ip6_add_hop_by_hop_node
(constructor) VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node)
#define IOAM_HBYH_ADD
int(* add_options[MAX_IP6_HBH_OPTION])(u8 *rewrite_string, u8 *rewrite_size)
static ip_adjacency_t * ip_get_adjacency(ip_lookup_main_t *lm, u32 adj_index)
Definition: lookup.h:415
ip6_add_hop_by_hop_error_t