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