FD.io VPP  v16.12-rc0-308-g931be3a
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 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = /* *INDENT-OFF* */
464 {
465  .function = ip6_add_hop_by_hop_node_fn,.name =
466  "ip6-add-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
468  VLIB_NODE_TYPE_INTERNAL,.n_errors =
469  ARRAY_LEN (ip6_add_hop_by_hop_error_strings),.error_strings =
471  /* See ip/lookup.h */
472  .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,.next_nodes =
473  {
474 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
476 #undef _
477  }
478 ,};
479 
480 /* *INDENT-ON* */
481 
482 VLIB_NODE_FUNCTION_MULTIARCH (ip6_add_hop_by_hop_node,
484 /* The main h-b-h tracer was already invoked, no need to do much here */
485  typedef struct
486  {
487  u32 next_index;
489 
490 /* packet trace format function */
491  static u8 *format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
492 {
493  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
494  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
496  va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
497 
498  s = format (s, "IP6_POP_HOP_BY_HOP: next index %d", t->next_index);
499  return s;
500 }
501 
502 int
504  int options (vlib_buffer_t * b,
505  ip6_header_t * ip,
507 {
509 
510  ASSERT (option < ARRAY_LEN (hm->pop_options));
511 
512  /* Already registered */
513  if (hm->pop_options[option])
514  return (-1);
515 
516  hm->pop_options[option] = options;
517 
518  return (0);
519 }
520 
521 int
523 {
525 
526  ASSERT (option < ARRAY_LEN (hm->pop_options));
527 
528  /* Not registered */
529  if (!hm->pop_options[option])
530  return (-1);
531 
532  hm->pop_options[option] = NULL;
533  return (0);
534 }
535 
537 
538 #define foreach_ip6_pop_hop_by_hop_error \
539 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \
540 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
541 _(OPTION_FAILED, "ip6 pop hop-by-hop failed to process")
542 
543 typedef enum
544 {
545 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
547 #undef _
550 
551 static char *ip6_pop_hop_by_hop_error_strings[] = {
552 #define _(sym,string) string,
554 #undef _
555 };
556 
557 static inline void
559  ip6_header_t * ip0,
561  vlib_buffer_t * b)
562 {
564  ip6_hop_by_hop_option_t *opt0, *limit0;
565  u8 type0;
566 
567  if (!hbh0 || !ip0)
568  return;
569 
570  opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
571  limit0 = (ip6_hop_by_hop_option_t *)
572  ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
573 
574  /* Scan the set of h-b-h options, process ones that we understand */
575  while (opt0 < limit0)
576  {
577  type0 = opt0->type;
578  switch (type0)
579  {
580  case 0: /* Pad1 */
581  opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
582  continue;
583  case 1: /* PadN */
584  break;
585  default:
586  if (hm->pop_options[type0])
587  {
588  if ((*hm->pop_options[type0]) (b, ip0, opt0) < 0)
589  {
591  ip6_pop_hop_by_hop_node.index,
592  IP6_POP_HOP_BY_HOP_ERROR_OPTION_FAILED,
593  1);
594  }
595  }
596  }
597  opt0 =
598  (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
599  sizeof (ip6_hop_by_hop_option_t));
600  }
601 }
602 
603 static uword
605  vlib_node_runtime_t * node, vlib_frame_t * frame)
606 {
607  ip6_main_t *im = &ip6_main;
608  ip_lookup_main_t *lm = &im->lookup_main;
609  u32 n_left_from, *from, *to_next;
610  ip_lookup_next_t next_index;
611  u32 processed = 0;
612  u32 no_header = 0;
613 
614  from = vlib_frame_vector_args (frame);
615  n_left_from = frame->n_vectors;
616  next_index = node->cached_next_index;
617 
618  while (n_left_from > 0)
619  {
620  u32 n_left_to_next;
621 
622  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
623 
624  while (n_left_from >= 4 && n_left_to_next >= 2)
625  {
626  u32 bi0, bi1;
627  vlib_buffer_t *b0, *b1;
628  u32 next0, next1;
629  u32 adj_index0, adj_index1;
630  ip6_header_t *ip0, *ip1;
631  ip_adjacency_t *adj0, *adj1;
632  ip6_hop_by_hop_header_t *hbh0, *hbh1;
633  u64 *copy_dst0, *copy_src0, *copy_dst1, *copy_src1;
634  u16 new_l0, new_l1;
635 
636  /* Prefetch next iteration. */
637  {
638  vlib_buffer_t *p2, *p3;
639 
640  p2 = vlib_get_buffer (vm, from[2]);
641  p3 = vlib_get_buffer (vm, from[3]);
642 
643  vlib_prefetch_buffer_header (p2, LOAD);
644  vlib_prefetch_buffer_header (p3, LOAD);
645 
648  }
649 
650  /* speculatively enqueue b0 and b1 to the current next frame */
651  to_next[0] = bi0 = from[0];
652  to_next[1] = bi1 = from[1];
653  from += 2;
654  to_next += 2;
655  n_left_from -= 2;
656  n_left_to_next -= 2;
657 
658  b0 = vlib_get_buffer (vm, bi0);
659  b1 = vlib_get_buffer (vm, bi1);
660 
661  /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
662  ip0 = vlib_buffer_get_current (b0);
663  ip1 = vlib_buffer_get_current (b1);
664  adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
665  adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
666  adj0 = ip_get_adjacency (lm, adj_index0);
667  adj1 = ip_get_adjacency (lm, adj_index1);
668 
669  next0 = adj0->lookup_next_index;
670  next1 = adj1->lookup_next_index;
671 
672  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
673  hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
674 
675  ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
676  ioam_pop_hop_by_hop_processing (vm, ip1, hbh1, b1);
677 
678  vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
679  vlib_buffer_advance (b1, (hbh1->length + 1) << 3);
680 
681  new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
682  ((hbh0->length + 1) << 3);
683  new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
684  ((hbh1->length + 1) << 3);
685 
686  ip0->payload_length = clib_host_to_net_u16 (new_l0);
687  ip1->payload_length = clib_host_to_net_u16 (new_l1);
688 
689  ip0->protocol = hbh0->protocol;
690  ip1->protocol = hbh1->protocol;
691 
692  copy_src0 = (u64 *) ip0;
693  copy_src1 = (u64 *) ip1;
694  copy_dst0 = copy_src0 + (hbh0->length + 1);
695  copy_dst0[4] = copy_src0[4];
696  copy_dst0[3] = copy_src0[3];
697  copy_dst0[2] = copy_src0[2];
698  copy_dst0[1] = copy_src0[1];
699  copy_dst0[0] = copy_src0[0];
700  copy_dst1 = copy_src1 + (hbh1->length + 1);
701  copy_dst1[4] = copy_src1[4];
702  copy_dst1[3] = copy_src1[3];
703  copy_dst1[2] = copy_src1[2];
704  copy_dst1[1] = copy_src1[1];
705  copy_dst1[0] = copy_src1[0];
706  processed += 2;
707  /* $$$$$ End of processing 2 x packets $$$$$ */
708 
709  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
710  {
711  if (b0->flags & VLIB_BUFFER_IS_TRACED)
712  {
714  vlib_add_trace (vm, node, b0, sizeof (*t));
715  t->next_index = next0;
716  }
717  if (b1->flags & VLIB_BUFFER_IS_TRACED)
718  {
720  vlib_add_trace (vm, node, b1, sizeof (*t));
721  t->next_index = next1;
722  }
723  }
724 
725  /* verify speculative enqueues, maybe switch current next frame */
726  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
727  to_next, n_left_to_next,
728  bi0, bi1, next0, next1);
729  }
730 
731  while (n_left_from > 0 && n_left_to_next > 0)
732  {
733  u32 bi0;
734  vlib_buffer_t *b0;
735  u32 next0;
736  u32 adj_index0;
737  ip6_header_t *ip0;
738  ip_adjacency_t *adj0;
740  u64 *copy_dst0, *copy_src0;
741  u16 new_l0;
742 
743  /* speculatively enqueue b0 to the current next frame */
744  bi0 = from[0];
745  to_next[0] = bi0;
746  from += 1;
747  to_next += 1;
748  n_left_from -= 1;
749  n_left_to_next -= 1;
750 
751  b0 = vlib_get_buffer (vm, bi0);
752 
753  ip0 = vlib_buffer_get_current (b0);
754  adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
755  adj0 = ip_get_adjacency (lm, adj_index0);
756 
757  /* Default use the next_index from the adjacency. */
758  next0 = adj0->lookup_next_index;
759 
760  /* Perfectly normal to end up here w/ out h-b-h header */
761  hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
762 
763  /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
764  ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
765  /* Pop the trace data */
766  vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
767  new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
768  ((hbh0->length + 1) << 3);
769  ip0->payload_length = clib_host_to_net_u16 (new_l0);
770  ip0->protocol = hbh0->protocol;
771  copy_src0 = (u64 *) ip0;
772  copy_dst0 = copy_src0 + (hbh0->length + 1);
773  copy_dst0[4] = copy_src0[4];
774  copy_dst0[3] = copy_src0[3];
775  copy_dst0[2] = copy_src0[2];
776  copy_dst0[1] = copy_src0[1];
777  copy_dst0[0] = copy_src0[0];
778  processed++;
779 
781  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
782  {
784  vlib_add_trace (vm, node, b0, sizeof (*t));
785  t->next_index = next0;
786  }
787 
788  /* verify speculative enqueue, maybe switch current next frame */
789  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
790  to_next, n_left_to_next,
791  bi0, next0);
792  }
793 
794  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
795  }
796 
797  vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
798  IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
799  vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
800  IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
801  return frame->n_vectors;
802 }
803 
804 /* *INDENT-OFF* */
805 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) =
806 {
807  .function = ip6_pop_hop_by_hop_node_fn,.name =
808  "ip6-pop-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
810  VLIB_NODE_TYPE_INTERNAL,.sibling_of = "ip6-lookup",.n_errors =
811  ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),.error_strings =
813  /* See ip/lookup.h */
814 .n_next_nodes = 0,};
815 
816 /* *INDENT-ON* */
817 
818 VLIB_NODE_FUNCTION_MULTIARCH (ip6_pop_hop_by_hop_node,
820  static clib_error_t *ip6_hop_by_hop_ioam_init (vlib_main_t * vm)
821 {
822  clib_error_t *error;
824 
825  if ((error = vlib_call_init_function (vm, ip_main_init)))
826  return (error);
827 
828  if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
829  return error;
830 
831  hm->vlib_main = vm;
832  hm->vnet_main = vnet_get_main ();
833  hm->unix_time_0 = (u32) time (0); /* Store starting time */
834  hm->vlib_time_0 = vlib_time_now (vm);
835  hm->ioam_flag = IOAM_HBYH_MOD;
836  memset (hm->add_options, 0, sizeof (hm->add_options));
837  memset (hm->pop_options, 0, sizeof (hm->pop_options));
838  memset (hm->options_size, 0, sizeof (hm->options_size));
839 
841 
842  return (0);
843 }
844 
845 VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init);
846 
847 int
848 ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option,
849  int has_pot_option, int has_seqno_option)
850 {
852  u8 *rewrite = NULL;
853  u32 size, rnd_size;
855  u8 *current;
856  u8 *trace_data_size = NULL;
857  u8 *pot_data_size = NULL;
858 
859  vec_free (*rwp);
860 
861  if (has_trace_option == 0 && has_pot_option == 0)
862  return -1;
863 
864  /* Work out how much space we need */
865  size = sizeof (ip6_hop_by_hop_header_t);
866 
867  //if (has_trace_option && hm->get_sizeof_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
868  if (has_trace_option
870  {
872  }
873  if (has_pot_option
875  {
877  }
878 
879  if (has_seqno_option)
880  {
882  }
883 
884  /* Round to a multiple of 8 octets */
885  rnd_size = (size + 7) & ~7;
886 
887  /* allocate it, zero-fill / pad by construction */
888  vec_validate (rewrite, rnd_size - 1);
889 
890  hbh = (ip6_hop_by_hop_header_t *) rewrite;
891  /* Length of header in 8 octet units, not incl first 8 octets */
892  hbh->length = (rnd_size >> 3) - 1;
893  current = (u8 *) (hbh + 1);
894 
895  if (has_trace_option
897  {
899  {
900  trace_data_size =
902  if (0 ==
904  trace_data_size))
905  current += *trace_data_size;
906  }
907  }
908  if (has_pot_option
910  {
911  pot_data_size =
913  if (0 ==
915  pot_data_size))
916  current += *pot_data_size;
917  }
918 
919  if (has_seqno_option &&
921  {
922  if (0 == hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] (current,
923  &
924  (hm->options_size
927  }
928 
929  *rwp = rewrite;
930  return 0;
931 }
932 
933 clib_error_t *
935 {
937 
938  vec_free (hm->rewrite);
939  hm->rewrite = 0;
940  hm->has_trace_option = 0;
941  hm->has_pot_option = 0;
942  hm->has_seqno_option = 0;
943  hm->has_analyse_option = 0;
946 
949 
951  {
953  &hm->has_analyse_option,
954  1);
955  }
956 
957  return 0;
958 }
959 
960 clib_error_t *
962  unformat_input_t * input,
963  vlib_cli_command_t * cmd)
964 {
965  return (clear_ioam_rewrite_fn ());
966 }
967 
968 /*?
969  * This command clears all the In-band OAM (iOAM) features enabled by
970  * the '<em>set ioam rewrite</em>' command. Use '<em>show ioam summary</em>' to
971  * verify the configured settings cleared.
972  *
973  * @cliexpar
974  * Example of how to clear iOAM features:
975  * @cliexcmd{clear ioam rewrite}
976 ?*/
977 /* *INDENT-OFF* */
978 VLIB_CLI_COMMAND (ip6_clear_ioam_rewrite_cmd, static) = {
979  .path = "clear ioam rewrite",
980  .short_help = "clear ioam rewrite",
981  .function = clear_ioam_rewrite_command_fn,
982 };
983 /* *INDENT-ON* */
984 
985 clib_error_t *
986 ip6_ioam_enable (int has_trace_option, int has_pot_option,
987  int has_seqno_option, int has_analyse_option)
988 {
989  int rv;
991  rv = ip6_ioam_set_rewrite (&hm->rewrite, has_trace_option,
992  has_pot_option, has_seqno_option);
993 
994  switch (rv)
995  {
996  case 0:
997  if (has_trace_option)
998  {
999  hm->has_trace_option = has_trace_option;
1002  0);
1003  }
1004 
1005  if (has_pot_option)
1006  {
1007  hm->has_pot_option = has_pot_option;
1010  0);
1011  }
1012  hm->has_analyse_option = has_analyse_option;
1013  if (has_seqno_option)
1014  {
1015  hm->has_seqno_option = has_seqno_option;
1017  {
1019  &has_analyse_option,
1020  0);
1021  }
1022  }
1023  break;
1024 
1025  default:
1026  return clib_error_return_code (0, rv, 0,
1027  "ip6_ioam_set_rewrite returned %d", rv);
1028  }
1029 
1030  return 0;
1031 }
1032 
1033 
1034 static clib_error_t *
1036  unformat_input_t * input,
1037  vlib_cli_command_t * cmd)
1038 {
1039  int has_trace_option = 0;
1040  int has_pot_option = 0;
1041  int has_seqno_option = 0;
1042  int has_analyse_option = 0;
1043  clib_error_t *rv = 0;
1044 
1045  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1046  {
1047  if (unformat (input, "trace"))
1048  has_trace_option = 1;
1049  else if (unformat (input, "pot"))
1050  has_pot_option = 1;
1051  else if (unformat (input, "seqno"))
1052  has_seqno_option = 1;
1053  else if (unformat (input, "analyse"))
1054  has_analyse_option = 1;
1055  else
1056  break;
1057  }
1058 
1059 
1060  rv = ip6_ioam_enable (has_trace_option, has_pot_option,
1061  has_seqno_option, has_analyse_option);
1062 
1063  return rv;
1064 }
1065 
1066 /*?
1067  * This command is used to enable In-band OAM (iOAM) features on IPv6.
1068  * '<em>trace</em>' is used to enable iOAM trace feature. '<em>pot</em>' is used to
1069  * enable the Proof Of Transit feature. '<em>ppc</em>' is used to indicate the
1070  * Per Packet Counter feature for Edge to Edge processing. '<em>ppc</em>' is
1071  * used to indicate if this node is an '<em>encap</em>' node (iOAM edge node
1072  * where packet enters iOAM domain), a '<em>decap</em>' node (iOAM edge node
1073  * where packet leaves iOAM domain) or '<em>none</em>' (iOAM node where packet
1074  * is in-transit through the iOAM domain). '<em>ppc</em>' can only be set if
1075  * '<em>trace</em>' or '<em>pot</em>' is enabled.
1076  *
1077  * Use '<em>clear ioam rewrite</em>' to disable all features enabled by this
1078  * command. Use '<em>show ioam summary</em>' to verify the configured settings.
1079  *
1080  * @cliexpar
1081  * Example of how to enable trace and pot with ppc set to encap:
1082  * @cliexcmd{set ioam rewrite trace pot ppc encap}
1083 ?*/
1084 /* *INDENT-OFF* */
1085 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1086  .path = "set ioam rewrite",
1087  .short_help = "set ioam [trace] [pot] [seqno] [analyse]",
1088  .function = ip6_set_ioam_rewrite_command_fn,
1089 };
1090 /* *INDENT-ON* */
1091 
1092 static clib_error_t *
1094  unformat_input_t * input,
1095  vlib_cli_command_t * cmd)
1096 {
1098  u8 *s = 0;
1099 
1100 
1101  if (!is_zero_ip6_address (&hm->adj))
1102  {
1103  s = format (s, " REWRITE FLOW CONFIGS - \n");
1104  s = format (s, " Destination Address : %U\n",
1105  format_ip6_address, &hm->adj, sizeof (ip6_address_t));
1106  s =
1107  format (s, " Flow operation : %d (%s)\n",
1108  hm->ioam_flag,
1109  (hm->ioam_flag ==
1110  IOAM_HBYH_ADD) ? "Add" : ((hm->ioam_flag ==
1111  IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1112  }
1113  else
1114  {
1115  s = format (s, " REWRITE FLOW CONFIGS - Not configured\n");
1116  }
1117 
1118 
1119  s = format (s, " TRACE OPTION - %d (%s)\n",
1120  hm->has_trace_option,
1121  (hm->has_trace_option ? "Enabled" : "Disabled"));
1122  if (hm->has_trace_option)
1123  s =
1124  format (s,
1125  "Try 'show ioam trace and show ioam-trace profile' for more information\n");
1126 
1127 
1128  s = format (s, " POT OPTION - %d (%s)\n",
1129  hm->has_pot_option,
1130  (hm->has_pot_option ? "Enabled" : "Disabled"));
1131  if (hm->has_pot_option)
1132  s =
1133  format (s,
1134  "Try 'show ioam pot and show pot profile' for more information\n");
1135 
1136  s = format (s, " EDGE TO EDGE - SeqNo OPTION - %d (%s)\n",
1137  hm->has_seqno_option,
1138  hm->has_seqno_option ? "Enabled" : "Disabled");
1139  if (hm->has_seqno_option)
1140  s = format (s, "Try 'show ioam e2e' for more information\n");
1141 
1142  s = format (s, " iOAM Analyse OPTION - %d (%s)\n",
1143  hm->has_analyse_option,
1144  hm->has_analyse_option ? "Enabled" : "Disabled");
1145 
1146  vlib_cli_output (vm, "%v", s);
1147  vec_free (s);
1148  return 0;
1149 }
1150 
1151 /*?
1152  * This command displays the current configuration data for In-band
1153  * OAM (iOAM).
1154  *
1155  * @cliexpar
1156  * Example to show the iOAM configuration:
1157  * @cliexstart{show ioam summary}
1158  * REWRITE FLOW CONFIGS -
1159  * Destination Address : ff02::1
1160  * Flow operation : 2 (Pop)
1161  * TRACE OPTION - 1 (Enabled)
1162  * Try 'show ioam trace and show ioam-trace profile' for more information
1163  * POT OPTION - 1 (Enabled)
1164  * Try 'show ioam pot and show pot profile' for more information
1165  * EDGE TO EDGE - PPC OPTION - 1 (Encap)
1166  * @cliexend
1167 ?*/
1168 /* *INDENT-OFF* */
1169 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1170  .path = "show ioam summary",
1171  .short_help = "show ioam summary",
1172  .function = ip6_show_ioam_summary_cmd_fn,
1173 };
1174 /* *INDENT-ON* */
1175 
1176 void
1178 {
1180 
1181  hm->ioam_end_of_path_cb = cb;
1182 }
1183 
1184 /*
1185  * fd.io coding-style-patch-verification: ON
1186  *
1187  * Local Variables:
1188  * eval: (c-set-style "gnu")
1189  * End:
1190  */
#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:457
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
ip6_pop_hop_by_hop_trace_t
#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:94
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:174
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:190
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)
Definition: vnet_classify.c:99
#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:575
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:64
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:203
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:333
ip6_main_t ip6_main
Definition: ip6_forward.c:2655
ip_lookup_main_t lookup_main
Definition: ip6.h:132
static u8 is_zero_ip6_address(ip6_address_t *a)
u32 size
Definition: vhost-user.h:76
#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:291
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
unsigned char u8
Definition: types.h:56
ip_lookup_next_t lookup_next_index
Definition: lookup.h:183
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:166
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:154
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:2658
#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:69
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:385
ip6_add_hop_by_hop_error_t