Add an ip6 local hop-by-hop protocol demux table
[vpp.git] / src / vnet / ip / ip6_hop_by_hop.c
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>
28 #include <vnet/classify/vnet_classify.h>
29
30 /**
31  * @file
32  * @brief In-band OAM (iOAM).
33  *
34  * In-band OAM (iOAM) is an implementation study to record operational
35  * information in the packet while the packet traverses a path between
36  * two points in the network.
37  *
38  * VPP can function as in-band OAM encapsulating, transit and
39  * decapsulating node. In this version of VPP in-band OAM data is
40  * transported as options in an IPv6 hop-by-hop extension header. Hence
41  * in-band OAM can be enabled for IPv6 traffic.
42  */
43
44 #ifndef CLIB_MARCH_VARIANT
45 ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main;
46 #endif /* CLIB_MARCH_VARIANT */
47
48 #define foreach_ip6_hbyh_ioam_input_next        \
49   _(IP6_REWRITE, "ip6-rewrite")                 \
50   _(IP6_LOOKUP, "ip6-lookup")                   \
51   _(DROP, "ip6-drop")
52
53 typedef enum
54 {
55 #define _(s,n) IP6_HBYH_IOAM_INPUT_NEXT_##s,
56   foreach_ip6_hbyh_ioam_input_next
57 #undef _
58     IP6_HBYH_IOAM_INPUT_N_NEXT,
59 } ip6_hbyh_ioam_input_next_t;
60
61 #ifndef CLIB_MARCH_VARIANT
62 static uword
63 unformat_opaque_ioam (unformat_input_t * input, va_list * args)
64 {
65   u64 *opaquep = va_arg (*args, u64 *);
66   u8 *flow_name = NULL;
67   uword ret = 0;
68
69   if (unformat (input, "ioam-encap %s", &flow_name))
70     {
71       *opaquep = ioam_flow_add (1, flow_name);
72       ret = 1;
73     }
74   else if (unformat (input, "ioam-decap %s", &flow_name))
75     {
76       *opaquep = ioam_flow_add (0, flow_name);
77       ret = 1;
78     }
79
80   vec_free (flow_name);
81   return ret;
82 }
83
84 u8 *
85 get_flow_name_from_flow_ctx (u32 flow_ctx)
86 {
87   flow_data_t *flow = NULL;
88   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
89   u32 index;
90
91   index = IOAM_MASK_DECAP_BIT (flow_ctx);
92
93   if (pool_is_free_index (hm->flows, index))
94     return NULL;
95
96   flow = pool_elt_at_index (hm->flows, index);
97   return (flow->flow_name);
98 }
99
100 /* The main h-b-h tracer will be invoked, no need to do much here */
101 int
102 ip6_hbh_add_register_option (u8 option,
103                              u8 size,
104                              int rewrite_options (u8 * rewrite_string,
105                                                   u8 * rewrite_size))
106 {
107   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
108
109   ASSERT ((u32) option < ARRAY_LEN (hm->add_options));
110
111   /* Already registered */
112   if (hm->add_options[option])
113     return (-1);
114
115   hm->add_options[option] = rewrite_options;
116   hm->options_size[option] = size;
117
118   return (0);
119 }
120
121 int
122 ip6_hbh_add_unregister_option (u8 option)
123 {
124   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
125
126   ASSERT ((u32) option < ARRAY_LEN (hm->add_options));
127
128   /* Not registered */
129   if (!hm->add_options[option])
130     return (-1);
131
132   hm->add_options[option] = NULL;
133   hm->options_size[option] = 0;
134   return (0);
135 }
136
137 /* Config handler registration */
138 int
139 ip6_hbh_config_handler_register (u8 option,
140                                  int config_handler (void *data, u8 disable))
141 {
142   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
143
144   ASSERT ((u32) option < ARRAY_LEN (hm->config_handler));
145
146   /* Already registered  */
147   if (hm->config_handler[option])
148     return (VNET_API_ERROR_INVALID_REGISTRATION);
149
150   hm->config_handler[option] = config_handler;
151
152   return (0);
153 }
154
155 int
156 ip6_hbh_config_handler_unregister (u8 option)
157 {
158   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
159
160   ASSERT ((u32) option < ARRAY_LEN (hm->config_handler));
161
162   /* Not registered */
163   if (!hm->config_handler[option])
164     return (VNET_API_ERROR_INVALID_REGISTRATION);
165
166   hm->config_handler[option] = NULL;
167   return (0);
168 }
169
170 /* Flow handler registration */
171 int
172 ip6_hbh_flow_handler_register (u8 option,
173                                u32 ioam_flow_handler (u32 flow_ctx, u8 add))
174 {
175   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
176
177   ASSERT ((u32) option < ARRAY_LEN (hm->flow_handler));
178
179   /* Already registered */
180   if (hm->flow_handler[option])
181     return (VNET_API_ERROR_INVALID_REGISTRATION);
182
183   hm->flow_handler[option] = ioam_flow_handler;
184
185   return (0);
186 }
187
188 int
189 ip6_hbh_flow_handler_unregister (u8 option)
190 {
191   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
192
193   ASSERT ((u32) option < ARRAY_LEN (hm->flow_handler));
194
195   /* Not registered */
196   if (!hm->flow_handler[option])
197     return (VNET_API_ERROR_INVALID_REGISTRATION);
198
199   hm->flow_handler[option] = NULL;
200   return (0);
201 }
202 #endif /* CLIB_MARCH_VARIANT */
203
204 typedef struct
205 {
206   u32 next_index;
207 } ip6_add_hop_by_hop_trace_t;
208
209 /* packet trace format function */
210 static u8 *
211 format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
212 {
213   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
214   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
215   ip6_add_hop_by_hop_trace_t *t = va_arg (*args,
216                                           ip6_add_hop_by_hop_trace_t *);
217
218   s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d", t->next_index);
219   return s;
220 }
221
222 extern vlib_node_registration_t ip6_add_hop_by_hop_node;
223
224 #define foreach_ip6_add_hop_by_hop_error \
225 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
226
227 typedef enum
228 {
229 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
230   foreach_ip6_add_hop_by_hop_error
231 #undef _
232     IP6_ADD_HOP_BY_HOP_N_ERROR,
233 } ip6_add_hop_by_hop_error_t;
234
235 static char *ip6_add_hop_by_hop_error_strings[] = {
236 #define _(sym,string) string,
237   foreach_ip6_add_hop_by_hop_error
238 #undef _
239 };
240
241 VLIB_NODE_FN (ip6_add_hop_by_hop_node) (vlib_main_t * vm,
242                                         vlib_node_runtime_t * node,
243                                         vlib_frame_t * frame)
244 {
245   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
246   u32 n_left_from, *from, *to_next;
247   ip_lookup_next_t next_index;
248   u32 processed = 0;
249   u8 *rewrite = hm->rewrite;
250   u32 rewrite_length = vec_len (rewrite);
251
252   from = vlib_frame_vector_args (frame);
253   n_left_from = frame->n_vectors;
254   next_index = node->cached_next_index;
255
256   while (n_left_from > 0)
257     {
258       u32 n_left_to_next;
259
260       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
261       while (n_left_from >= 4 && n_left_to_next >= 2)
262         {
263           u32 bi0, bi1;
264           vlib_buffer_t *b0, *b1;
265           u32 next0, next1;
266           ip6_header_t *ip0, *ip1;
267           ip6_hop_by_hop_header_t *hbh0, *hbh1;
268           u64 *copy_src0, *copy_dst0, *copy_src1, *copy_dst1;
269           u16 new_l0, new_l1;
270
271           /* Prefetch next iteration. */
272           {
273             vlib_buffer_t *p2, *p3;
274
275             p2 = vlib_get_buffer (vm, from[2]);
276             p3 = vlib_get_buffer (vm, from[3]);
277
278             vlib_prefetch_buffer_header (p2, LOAD);
279             vlib_prefetch_buffer_header (p3, LOAD);
280
281             CLIB_PREFETCH (p2->data - rewrite_length,
282                            2 * CLIB_CACHE_LINE_BYTES, STORE);
283             CLIB_PREFETCH (p3->data - rewrite_length,
284                            2 * CLIB_CACHE_LINE_BYTES, STORE);
285           }
286
287           /* speculatively enqueue b0 and b1 to the current next frame */
288           to_next[0] = bi0 = from[0];
289           to_next[1] = bi1 = from[1];
290           from += 2;
291           to_next += 2;
292           n_left_from -= 2;
293           n_left_to_next -= 2;
294
295           b0 = vlib_get_buffer (vm, bi0);
296           b1 = vlib_get_buffer (vm, bi1);
297
298           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
299           ip0 = vlib_buffer_get_current (b0);
300           ip1 = vlib_buffer_get_current (b1);
301
302           /* Copy the ip header left by the required amount */
303           copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
304           copy_dst1 = (u64 *) (((u8 *) ip1) - rewrite_length);
305           copy_src0 = (u64 *) ip0;
306           copy_src1 = (u64 *) ip1;
307
308           copy_dst0[0] = copy_src0[0];
309           copy_dst0[1] = copy_src0[1];
310           copy_dst0[2] = copy_src0[2];
311           copy_dst0[3] = copy_src0[3];
312           copy_dst0[4] = copy_src0[4];
313
314           copy_dst1[0] = copy_src1[0];
315           copy_dst1[1] = copy_src1[1];
316           copy_dst1[2] = copy_src1[2];
317           copy_dst1[3] = copy_src1[3];
318           copy_dst1[4] = copy_src1[4];
319
320           vlib_buffer_advance (b0, -(word) rewrite_length);
321           vlib_buffer_advance (b1, -(word) rewrite_length);
322           ip0 = vlib_buffer_get_current (b0);
323           ip1 = vlib_buffer_get_current (b1);
324
325           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
326           hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
327           /* $$$ tune, rewrite_length is a multiple of 8 */
328           clib_memcpy_fast (hbh0, rewrite, rewrite_length);
329           clib_memcpy_fast (hbh1, rewrite, rewrite_length);
330           /* Patch the protocol chain, insert the h-b-h (type 0) header */
331           hbh0->protocol = ip0->protocol;
332           hbh1->protocol = ip1->protocol;
333           ip0->protocol = 0;
334           ip1->protocol = 0;
335           new_l0 =
336             clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
337           new_l1 =
338             clib_net_to_host_u16 (ip1->payload_length) + rewrite_length;
339           ip0->payload_length = clib_host_to_net_u16 (new_l0);
340           ip1->payload_length = clib_host_to_net_u16 (new_l1);
341
342           /* Populate the (first) h-b-h list elt */
343           next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
344           next1 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
345
346
347           /* $$$$$ End of processing 2 x packets $$$$$ */
348
349           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
350             {
351               if (b0->flags & VLIB_BUFFER_IS_TRACED)
352                 {
353                   ip6_add_hop_by_hop_trace_t *t =
354                     vlib_add_trace (vm, node, b0, sizeof (*t));
355                   t->next_index = next0;
356                 }
357               if (b1->flags & VLIB_BUFFER_IS_TRACED)
358                 {
359                   ip6_add_hop_by_hop_trace_t *t =
360                     vlib_add_trace (vm, node, b1, sizeof (*t));
361                   t->next_index = next1;
362                 }
363             }
364           processed += 2;
365           /* verify speculative enqueues, maybe switch current next frame */
366           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
367                                            to_next, n_left_to_next,
368                                            bi0, bi1, next0, next1);
369         }
370       while (n_left_from > 0 && n_left_to_next > 0)
371         {
372           u32 bi0;
373           vlib_buffer_t *b0;
374           u32 next0;
375           ip6_header_t *ip0;
376           ip6_hop_by_hop_header_t *hbh0;
377           u64 *copy_src0, *copy_dst0;
378           u16 new_l0;
379
380           /* speculatively enqueue b0 to the current next frame */
381           bi0 = from[0];
382           to_next[0] = bi0;
383           from += 1;
384           to_next += 1;
385           n_left_from -= 1;
386           n_left_to_next -= 1;
387
388           b0 = vlib_get_buffer (vm, bi0);
389
390           ip0 = vlib_buffer_get_current (b0);
391
392           /* Copy the ip header left by the required amount */
393           copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
394           copy_src0 = (u64 *) ip0;
395
396           copy_dst0[0] = copy_src0[0];
397           copy_dst0[1] = copy_src0[1];
398           copy_dst0[2] = copy_src0[2];
399           copy_dst0[3] = copy_src0[3];
400           copy_dst0[4] = copy_src0[4];
401           vlib_buffer_advance (b0, -(word) rewrite_length);
402           ip0 = vlib_buffer_get_current (b0);
403
404           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
405           /* $$$ tune, rewrite_length is a multiple of 8 */
406           clib_memcpy_fast (hbh0, rewrite, rewrite_length);
407           /* Patch the protocol chain, insert the h-b-h (type 0) header */
408           hbh0->protocol = ip0->protocol;
409           ip0->protocol = 0;
410           new_l0 =
411             clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
412           ip0->payload_length = clib_host_to_net_u16 (new_l0);
413
414           /* Populate the (first) h-b-h list elt */
415           next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
416
417           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
418                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
419             {
420               ip6_add_hop_by_hop_trace_t *t =
421                 vlib_add_trace (vm, node, b0, sizeof (*t));
422               t->next_index = next0;
423             }
424
425           processed++;
426
427           /* verify speculative enqueue, maybe switch current next frame */
428           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
429                                            to_next, n_left_to_next,
430                                            bi0, next0);
431         }
432
433       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
434     }
435
436   vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
437                                IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
438   return frame->n_vectors;
439 }
440
441 /* *INDENT-OFF* */
442 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) =  /* *INDENT-OFF* */
443 {
444   .name = "ip6-add-hop-by-hop",
445   .vector_size = sizeof (u32),
446   .format_trace = format_ip6_add_hop_by_hop_trace,
447   .type = VLIB_NODE_TYPE_INTERNAL,
448   .n_errors = ARRAY_LEN (ip6_add_hop_by_hop_error_strings),
449   .error_strings = ip6_add_hop_by_hop_error_strings,
450   /* See ip/lookup.h */
451   .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,
452   .next_nodes = {
453 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
454     foreach_ip6_hbyh_ioam_input_next
455 #undef _
456   },
457 };
458 /* *INDENT-ON* */
459
460 /* The main h-b-h tracer was already invoked, no need to do much here */
461 typedef struct
462 {
463   u32 next_index;
464 } ip6_pop_hop_by_hop_trace_t;
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 *);
472   ip6_pop_hop_by_hop_trace_t *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 #ifndef CLIB_MARCH_VARIANT
480 int
481 ip6_hbh_pop_register_option (u8 option,
482                              int options (vlib_buffer_t * b,
483                                           ip6_header_t * ip,
484                                           ip6_hop_by_hop_option_t * opt))
485 {
486   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
487
488   ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
489
490   /* Already registered */
491   if (hm->pop_options[option])
492     return (-1);
493
494   hm->pop_options[option] = options;
495
496   return (0);
497 }
498
499 int
500 ip6_hbh_pop_unregister_option (u8 option)
501 {
502   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
503
504   ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
505
506   /* Not registered */
507   if (!hm->pop_options[option])
508     return (-1);
509
510   hm->pop_options[option] = NULL;
511   return (0);
512 }
513 #endif /* CLIB_MARCH_VARIANT */
514
515 extern vlib_node_registration_t ip6_pop_hop_by_hop_node;
516
517 #define foreach_ip6_pop_hop_by_hop_error                \
518 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options")  \
519 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options")         \
520 _(OPTION_FAILED, "ip6 pop hop-by-hop failed to process")
521
522 typedef enum
523 {
524 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
525   foreach_ip6_pop_hop_by_hop_error
526 #undef _
527     IP6_POP_HOP_BY_HOP_N_ERROR,
528 } ip6_pop_hop_by_hop_error_t;
529
530 static char *ip6_pop_hop_by_hop_error_strings[] = {
531 #define _(sym,string) string,
532   foreach_ip6_pop_hop_by_hop_error
533 #undef _
534 };
535
536 static inline void
537 ioam_pop_hop_by_hop_processing (vlib_main_t * vm,
538                                 ip6_header_t * ip0,
539                                 ip6_hop_by_hop_header_t * hbh0,
540                                 vlib_buffer_t * b)
541 {
542   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
543   ip6_hop_by_hop_option_t *opt0, *limit0;
544   u8 type0;
545
546   if (!hbh0 || !ip0)
547     return;
548
549   opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
550   limit0 = (ip6_hop_by_hop_option_t *)
551     ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
552
553   /* Scan the set of h-b-h options, process ones that we understand */
554   while (opt0 < limit0)
555     {
556       type0 = opt0->type;
557       switch (type0)
558         {
559         case 0:         /* Pad1 */
560           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
561           continue;
562         case 1:         /* PadN */
563           break;
564         default:
565           if (hm->pop_options[type0])
566             {
567               if ((*hm->pop_options[type0]) (b, ip0, opt0) < 0)
568                 {
569                   vlib_node_increment_counter (vm,
570                                                ip6_pop_hop_by_hop_node.index,
571                                                IP6_POP_HOP_BY_HOP_ERROR_OPTION_FAILED,
572                                                1);
573                 }
574             }
575         }
576       opt0 =
577         (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
578                                      sizeof (ip6_hop_by_hop_option_t));
579     }
580 }
581
582 VLIB_NODE_FN (ip6_pop_hop_by_hop_node) (vlib_main_t * vm,
583                                         vlib_node_runtime_t * node,
584                                         vlib_frame_t * frame)
585 {
586   u32 n_left_from, *from, *to_next;
587   ip_lookup_next_t next_index;
588   u32 processed = 0;
589   u32 no_header = 0;
590
591   from = vlib_frame_vector_args (frame);
592   n_left_from = frame->n_vectors;
593   next_index = node->cached_next_index;
594
595   while (n_left_from > 0)
596     {
597       u32 n_left_to_next;
598
599       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
600
601       while (n_left_from >= 4 && n_left_to_next >= 2)
602         {
603           u32 bi0, bi1;
604           vlib_buffer_t *b0, *b1;
605           u32 next0, next1;
606           u32 adj_index0, adj_index1;
607           ip6_header_t *ip0, *ip1;
608           ip_adjacency_t *adj0, *adj1;
609           ip6_hop_by_hop_header_t *hbh0, *hbh1;
610           u64 *copy_dst0, *copy_src0, *copy_dst1, *copy_src1;
611           u16 new_l0, new_l1;
612
613           /* Prefetch next iteration. */
614           {
615             vlib_buffer_t *p2, *p3;
616
617             p2 = vlib_get_buffer (vm, from[2]);
618             p3 = vlib_get_buffer (vm, from[3]);
619
620             vlib_prefetch_buffer_header (p2, LOAD);
621             vlib_prefetch_buffer_header (p3, LOAD);
622
623             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
624             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
625           }
626
627           /* speculatively enqueue b0 and b1 to the current next frame */
628           to_next[0] = bi0 = from[0];
629           to_next[1] = bi1 = from[1];
630           from += 2;
631           to_next += 2;
632           n_left_from -= 2;
633           n_left_to_next -= 2;
634
635           b0 = vlib_get_buffer (vm, bi0);
636           b1 = vlib_get_buffer (vm, bi1);
637
638           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
639           ip0 = vlib_buffer_get_current (b0);
640           ip1 = vlib_buffer_get_current (b1);
641           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
642           adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
643           adj0 = adj_get (adj_index0);
644           adj1 = adj_get (adj_index1);
645
646           next0 = adj0->lookup_next_index;
647           next1 = adj1->lookup_next_index;
648
649           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
650           hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
651
652           ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
653           ioam_pop_hop_by_hop_processing (vm, ip1, hbh1, b1);
654
655           vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
656           vlib_buffer_advance (b1, (hbh1->length + 1) << 3);
657
658           new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
659             ((hbh0->length + 1) << 3);
660           new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
661             ((hbh1->length + 1) << 3);
662
663           ip0->payload_length = clib_host_to_net_u16 (new_l0);
664           ip1->payload_length = clib_host_to_net_u16 (new_l1);
665
666           ip0->protocol = hbh0->protocol;
667           ip1->protocol = hbh1->protocol;
668
669           copy_src0 = (u64 *) ip0;
670           copy_src1 = (u64 *) ip1;
671           copy_dst0 = copy_src0 + (hbh0->length + 1);
672           copy_dst0[4] = copy_src0[4];
673           copy_dst0[3] = copy_src0[3];
674           copy_dst0[2] = copy_src0[2];
675           copy_dst0[1] = copy_src0[1];
676           copy_dst0[0] = copy_src0[0];
677           copy_dst1 = copy_src1 + (hbh1->length + 1);
678           copy_dst1[4] = copy_src1[4];
679           copy_dst1[3] = copy_src1[3];
680           copy_dst1[2] = copy_src1[2];
681           copy_dst1[1] = copy_src1[1];
682           copy_dst1[0] = copy_src1[0];
683           processed += 2;
684           /* $$$$$ End of processing 2 x packets $$$$$ */
685
686           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
687             {
688               if (b0->flags & VLIB_BUFFER_IS_TRACED)
689                 {
690                   ip6_pop_hop_by_hop_trace_t *t =
691                     vlib_add_trace (vm, node, b0, sizeof (*t));
692                   t->next_index = next0;
693                 }
694               if (b1->flags & VLIB_BUFFER_IS_TRACED)
695                 {
696                   ip6_pop_hop_by_hop_trace_t *t =
697                     vlib_add_trace (vm, node, b1, sizeof (*t));
698                   t->next_index = next1;
699                 }
700             }
701
702           /* verify speculative enqueues, maybe switch current next frame */
703           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
704                                            to_next, n_left_to_next,
705                                            bi0, bi1, next0, next1);
706         }
707
708       while (n_left_from > 0 && n_left_to_next > 0)
709         {
710           u32 bi0;
711           vlib_buffer_t *b0;
712           u32 next0;
713           u32 adj_index0;
714           ip6_header_t *ip0;
715           ip_adjacency_t *adj0;
716           ip6_hop_by_hop_header_t *hbh0;
717           u64 *copy_dst0, *copy_src0;
718           u16 new_l0;
719
720           /* speculatively enqueue b0 to the current next frame */
721           bi0 = from[0];
722           to_next[0] = bi0;
723           from += 1;
724           to_next += 1;
725           n_left_from -= 1;
726           n_left_to_next -= 1;
727
728           b0 = vlib_get_buffer (vm, bi0);
729
730           ip0 = vlib_buffer_get_current (b0);
731           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
732           adj0 = adj_get (adj_index0);
733
734           /* Default use the next_index from the adjacency. */
735           next0 = adj0->lookup_next_index;
736
737           /* Perfectly normal to end up here w/ out h-b-h header */
738           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
739
740           /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
741           ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
742           /* Pop the trace data */
743           vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
744           new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
745             ((hbh0->length + 1) << 3);
746           ip0->payload_length = clib_host_to_net_u16 (new_l0);
747           ip0->protocol = hbh0->protocol;
748           copy_src0 = (u64 *) ip0;
749           copy_dst0 = copy_src0 + (hbh0->length + 1);
750           copy_dst0[4] = copy_src0[4];
751           copy_dst0[3] = copy_src0[3];
752           copy_dst0[2] = copy_src0[2];
753           copy_dst0[1] = copy_src0[1];
754           copy_dst0[0] = copy_src0[0];
755           processed++;
756
757           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
758                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
759             {
760               ip6_pop_hop_by_hop_trace_t *t =
761                 vlib_add_trace (vm, node, b0, sizeof (*t));
762               t->next_index = next0;
763             }
764
765           /* verify speculative enqueue, maybe switch current next frame */
766           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
767                                            to_next, n_left_to_next,
768                                            bi0, next0);
769         }
770
771       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
772     }
773
774   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
775                                IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
776   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
777                                IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
778   return frame->n_vectors;
779 }
780
781 /* *INDENT-OFF* */
782 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) =
783 {
784   .name = "ip6-pop-hop-by-hop",
785   .vector_size = sizeof (u32),
786   .format_trace = format_ip6_pop_hop_by_hop_trace,
787   .type = VLIB_NODE_TYPE_INTERNAL,
788   .sibling_of = "ip6-lookup",
789   .n_errors = ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),
790   .error_strings = ip6_pop_hop_by_hop_error_strings,
791   /* See ip/lookup.h */
792   .n_next_nodes = 0,
793 };
794 /* *INDENT-ON* */
795
796 typedef struct
797 {
798   u32 protocol;
799   u32 next_index;
800 } ip6_local_hop_by_hop_trace_t;
801
802 #ifndef CLIB_MARCH_VARIANT
803
804 /* packet trace format function */
805 static u8 *
806 format_ip6_local_hop_by_hop_trace (u8 * s, va_list * args)
807 {
808   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
809   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
810   ip6_local_hop_by_hop_trace_t *t =
811     va_arg (*args, ip6_local_hop_by_hop_trace_t *);
812
813   s = format (s, "IP6_LOCAL_HOP_BY_HOP: protocol %d,  next index %d\n",
814               t->protocol, t->next_index);
815   return s;
816 }
817
818 vlib_node_registration_t ip6_local_hop_by_hop_node;
819
820 #endif /* CLIB_MARCH_VARIANT */
821
822 #define foreach_ip6_local_hop_by_hop_error                      \
823 _(UNKNOWN, "Unknown protocol ip6 local h-b-h packets dropped")  \
824 _(OK, "Good ip6 local h-b-h packets")
825
826 typedef enum
827 {
828 #define _(sym,str) IP6_LOCAL_HOP_BY_HOP_ERROR_##sym,
829   foreach_ip6_local_hop_by_hop_error
830 #undef _
831     IP6_LOCAL_HOP_BY_HOP_N_ERROR,
832 } ip6_local_hop_by_hop_error_t;
833
834 #ifndef CLIB_MARCH_VARIANT
835 static char *ip6_local_hop_by_hop_error_strings[] = {
836 #define _(sym,string) string,
837   foreach_ip6_local_hop_by_hop_error
838 #undef _
839 };
840 #endif /* CLIB_MARCH_VARIANT */
841
842 typedef enum
843 {
844   IP6_LOCAL_HOP_BY_HOP_NEXT_DROP,
845   IP6_LOCAL_HOP_BY_HOP_N_NEXT,
846 } ip6_local_hop_by_hop_next_t;
847
848 always_inline uword
849 ip6_local_hop_by_hop_inline (vlib_main_t * vm,
850                              vlib_node_runtime_t * node, vlib_frame_t * frame,
851                              int is_trace)
852 {
853   u32 n_left_from, *from;
854   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
855   u16 nexts[VLIB_FRAME_SIZE], *next;
856   u32 ok = 0;
857   u32 unknown_proto_error = node->errors[IP6_LOCAL_HOP_BY_HOP_ERROR_UNKNOWN];
858   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
859
860   /* Note: there is only one of these */
861   ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime;
862
863   from = vlib_frame_vector_args (frame);
864   n_left_from = frame->n_vectors;
865
866   vlib_get_buffers (vm, from, bufs, n_left_from);
867   b = bufs;
868   next = nexts;
869
870   while (n_left_from >= 4)
871     {
872       ip6_header_t *ip0, *ip1, *ip2, *ip3;
873       u8 *hbh0, *hbh1, *hbh2, *hbh3;
874
875       /* Prefetch next iteration. */
876       if (PREDICT_TRUE (n_left_from >= 8))
877         {
878           vlib_prefetch_buffer_header (b[4], STORE);
879           vlib_prefetch_buffer_header (b[5], STORE);
880           vlib_prefetch_buffer_header (b[6], STORE);
881           vlib_prefetch_buffer_header (b[7], STORE);
882           CLIB_PREFETCH (b[4]->data, CLIB_CACHE_LINE_BYTES, STORE);
883           CLIB_PREFETCH (b[5]->data, CLIB_CACHE_LINE_BYTES, STORE);
884           CLIB_PREFETCH (b[6]->data, CLIB_CACHE_LINE_BYTES, STORE);
885           CLIB_PREFETCH (b[7]->data, CLIB_CACHE_LINE_BYTES, STORE);
886         }
887
888       /*
889        * Leave current_data pointing at the IP header.
890        * It's reasonably likely that any registered handler
891        * will want to know where to find the ip6 header.
892        */
893       ip0 = vlib_buffer_get_current (b[0]);
894       ip1 = vlib_buffer_get_current (b[1]);
895       ip2 = vlib_buffer_get_current (b[2]);
896       ip3 = vlib_buffer_get_current (b[3]);
897
898       /* Look at hop-by-hop header */
899       hbh0 = ip6_next_header (ip0);
900       hbh1 = ip6_next_header (ip1);
901       hbh2 = ip6_next_header (ip2);
902       hbh3 = ip6_next_header (ip3);
903
904       /*
905        * ... to find the next header type and see if we
906        * have a handler for it...
907        */
908       next[0] = rt->next_index_by_protocol[*hbh0];
909       next[1] = rt->next_index_by_protocol[*hbh1];
910       next[2] = rt->next_index_by_protocol[*hbh2];
911       next[3] = rt->next_index_by_protocol[*hbh3];
912
913       b[0]->error = unknown_proto_error;
914       b[1]->error = unknown_proto_error;
915       b[2]->error = unknown_proto_error;
916       b[3]->error = unknown_proto_error;
917
918       /* Account for non-drop pkts */
919       ok += next[0] != 0;
920       ok += next[1] != 0;
921       ok += next[2] != 0;
922       ok += next[3] != 0;
923
924       if (is_trace)
925         {
926           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
927             {
928               ip6_local_hop_by_hop_trace_t *t =
929                 vlib_add_trace (vm, node, b[0], sizeof (*t));
930               t->next_index = next[0];
931               t->protocol = *hbh0;
932             }
933           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
934             {
935               ip6_local_hop_by_hop_trace_t *t =
936                 vlib_add_trace (vm, node, b[1], sizeof (*t));
937               t->next_index = next[1];
938               t->protocol = *hbh1;
939             }
940           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
941             {
942               ip6_local_hop_by_hop_trace_t *t =
943                 vlib_add_trace (vm, node, b[2], sizeof (*t));
944               t->next_index = next[2];
945               t->protocol = *hbh2;
946             }
947           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
948             {
949               ip6_local_hop_by_hop_trace_t *t =
950                 vlib_add_trace (vm, node, b[3], sizeof (*t));
951               t->next_index = next[3];
952               t->protocol = *hbh3;
953             }
954         }
955
956       b += 4;
957       next += 4;
958       n_left_from -= 4;
959     }
960
961   while (n_left_from > 0)
962     {
963       ip6_header_t *ip0;
964       u8 *hbh0;
965
966       ip0 = vlib_buffer_get_current (b[0]);
967
968       hbh0 = ip6_next_header (ip0);
969
970       next[0] = rt->next_index_by_protocol[*hbh0];
971
972       b[0]->error = unknown_proto_error;
973       ok += next[0] != 0;
974
975       if (is_trace)
976         {
977           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
978             {
979               ip6_local_hop_by_hop_trace_t *t =
980                 vlib_add_trace (vm, node, b[0], sizeof (*t));
981               t->next_index = next[0];
982               t->protocol = *hbh0;
983             }
984         }
985
986       b += 1;
987       next += 1;
988       n_left_from -= 1;
989     }
990
991   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
992
993   vlib_node_increment_counter (vm, node->node_index,
994                                IP6_LOCAL_HOP_BY_HOP_ERROR_OK, ok);
995   return frame->n_vectors;
996 }
997
998 VLIB_NODE_FN (ip6_local_hop_by_hop_node) (vlib_main_t * vm,
999                                           vlib_node_runtime_t * node,
1000                                           vlib_frame_t * frame)
1001 {
1002   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
1003     return ip6_local_hop_by_hop_inline (vm, node, frame, 1 /* is_trace */ );
1004   else
1005     return ip6_local_hop_by_hop_inline (vm, node, frame, 0 /* is_trace */ );
1006 }
1007
1008 #ifndef CLIB_MARCH_VARIANT
1009 /* *INDENT-OFF* */
1010 VLIB_REGISTER_NODE (ip6_local_hop_by_hop_node) =
1011 {
1012   .name = "ip6-local-hop-by-hop",
1013   .vector_size = sizeof (u32),
1014   .format_trace = format_ip6_local_hop_by_hop_trace,
1015   .type = VLIB_NODE_TYPE_INTERNAL,
1016
1017   .n_errors = ARRAY_LEN(ip6_local_hop_by_hop_error_strings),
1018   .error_strings = ip6_local_hop_by_hop_error_strings,
1019
1020   .n_next_nodes = IP6_LOCAL_HOP_BY_HOP_N_NEXT,
1021
1022   /* edit / add dispositions here */
1023   .next_nodes =
1024   {
1025     [IP6_LOCAL_HOP_BY_HOP_NEXT_DROP] = "error-drop",
1026   },
1027 };
1028 /* *INDENT-ON* */
1029
1030 clib_error_t *
1031 show_ip6_hbh_command_fn (vlib_main_t * vm,
1032                          unformat_input_t * input, vlib_cli_command_t * cmd)
1033 {
1034   int i;
1035   u32 next_index;
1036   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1037   ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime;
1038   vlib_node_t *n = vlib_get_node (vm, ip6_local_hop_by_hop_node.index);
1039
1040   vlib_cli_output (vm, "%-6s%s", "Proto", "Node Name");
1041
1042   for (i = 0; i < ARRAY_LEN (rt->next_index_by_protocol); i++)
1043     {
1044       if ((next_index = rt->next_index_by_protocol[i]))
1045         {
1046           u32 next_node_index = n->next_nodes[next_index];
1047           vlib_node_t *next_n = vlib_get_node (vm, next_node_index);
1048           vlib_cli_output (vm, "[%3d] %v", i, next_n->name);
1049         }
1050     }
1051
1052   return 0;
1053 }
1054
1055 /*?
1056  * Display the set of ip6 local hop-by-hop next protocol handler nodes
1057  *
1058  * @cliexpar
1059  * Display ip6 local hop-by-hop next protocol handler nodes
1060  * @cliexcmd{show ip6 hbh}
1061 ?*/
1062 /* *INDENT-OFF* */
1063 VLIB_CLI_COMMAND (show_ip6_hbh, static) = {
1064   .path = "show ip6 hbh",
1065   .short_help = "show ip6 hbh",
1066   .function = show_ip6_hbh_command_fn,
1067 };
1068 /* *INDENT-ON* */
1069
1070
1071 #endif /* CLIB_MARCH_VARIANT */
1072
1073
1074 #ifndef CLIB_MARCH_VARIANT
1075 static clib_error_t *
1076 ip6_hop_by_hop_ioam_init (vlib_main_t * vm)
1077 {
1078   clib_error_t *error;
1079   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1080
1081   if ((error = vlib_call_init_function (vm, ip_main_init)))
1082     return (error);
1083
1084   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
1085     return error;
1086
1087   hm->vlib_main = vm;
1088   hm->vnet_main = vnet_get_main ();
1089   hm->unix_time_0 = (u32) time (0);     /* Store starting time */
1090   hm->vlib_time_0 = vlib_time_now (vm);
1091   hm->ioam_flag = IOAM_HBYH_MOD;
1092   clib_memset (hm->add_options, 0, sizeof (hm->add_options));
1093   clib_memset (hm->pop_options, 0, sizeof (hm->pop_options));
1094   clib_memset (hm->options_size, 0, sizeof (hm->options_size));
1095
1096   vnet_classify_register_unformat_opaque_index_fn (unformat_opaque_ioam);
1097   hm->ip6_local_hbh_runtime = clib_mem_alloc_aligned
1098     (sizeof (ip6_local_hop_by_hop_runtime_t), CLIB_CACHE_LINE_BYTES);
1099
1100   memset (hm->ip6_local_hbh_runtime, 0,
1101           sizeof (ip6_local_hop_by_hop_runtime_t));
1102
1103   ip6_register_protocol (IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS,
1104                          ip6_local_hop_by_hop_node.index);
1105   return (0);
1106 }
1107
1108 /* *INDENT-OFF* */
1109 VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init) =
1110 {
1111   .runs_after = VLIB_INITS("ip_main_init", "ip6_lookup_init"),
1112 };
1113 /* *INDENT-ON* */
1114
1115 void
1116 ip6_local_hop_by_hop_register_protocol (u32 protocol, u32 node_index)
1117 {
1118   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1119   vlib_main_t *vm = hm->vlib_main;
1120   ip6_local_hop_by_hop_runtime_t *local_hbh_runtime
1121     = hm->ip6_local_hbh_runtime;
1122   u32 old_next_index;
1123
1124   ASSERT (protocol < ARRAY_LEN (local_hbh_runtime->next_index_by_protocol));
1125
1126   old_next_index = local_hbh_runtime->next_index_by_protocol[protocol];
1127
1128   local_hbh_runtime->next_index_by_protocol[protocol] =
1129     vlib_node_add_next (vm, ip6_local_hop_by_hop_node.index, node_index);
1130
1131   /* Someone will eventually do this. Trust me. */
1132   if (old_next_index &&
1133       (old_next_index != local_hbh_runtime->next_index_by_protocol[protocol]))
1134     clib_warning ("WARNING: replaced next index for protocol %d", protocol);
1135 }
1136
1137 int
1138 ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option,
1139                       int has_pot_option, int has_seqno_option)
1140 {
1141   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1142   u8 *rewrite = NULL;
1143   u32 size, rnd_size;
1144   ip6_hop_by_hop_header_t *hbh;
1145   u8 *current;
1146   u8 *trace_data_size = NULL;
1147   u8 *pot_data_size = NULL;
1148
1149   vec_free (*rwp);
1150
1151   if (has_trace_option == 0 && has_pot_option == 0)
1152     return -1;
1153
1154   /* Work out how much space we need */
1155   size = sizeof (ip6_hop_by_hop_header_t);
1156
1157   //if (has_trace_option && hm->get_sizeof_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
1158   if (has_trace_option
1159       && hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
1160     {
1161       size += hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
1162     }
1163   if (has_pot_option
1164       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
1165     {
1166       size += hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
1167     }
1168
1169   if (has_seqno_option)
1170     {
1171       size += hm->options_size[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE];
1172     }
1173
1174   /* Round to a multiple of 8 octets */
1175   rnd_size = (size + 7) & ~7;
1176
1177   /* allocate it, zero-fill / pad by construction */
1178   vec_validate (rewrite, rnd_size - 1);
1179
1180   hbh = (ip6_hop_by_hop_header_t *) rewrite;
1181   /* Length of header in 8 octet units, not incl first 8 octets */
1182   hbh->length = (rnd_size >> 3) - 1;
1183   current = (u8 *) (hbh + 1);
1184
1185   if (has_trace_option
1186       && hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
1187     {
1188       if (0 != (hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST]))
1189         {
1190           trace_data_size =
1191             &hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
1192           if (0 ==
1193               hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (current,
1194                                                                      trace_data_size))
1195             current += *trace_data_size;
1196         }
1197     }
1198   if (has_pot_option
1199       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
1200     {
1201       pot_data_size =
1202         &hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
1203       if (0 ==
1204           hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (current,
1205                                                                   pot_data_size))
1206         current += *pot_data_size;
1207     }
1208
1209   if (has_seqno_option &&
1210       (hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] != 0))
1211     {
1212       if (0 == hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] (current,
1213                                                                    &
1214                                                                    (hm->options_size
1215                                                                     [HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])))
1216         current += hm->options_size[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE];
1217     }
1218
1219   *rwp = rewrite;
1220   return 0;
1221 }
1222
1223 clib_error_t *
1224 clear_ioam_rewrite_fn (void)
1225 {
1226   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1227
1228   vec_free (hm->rewrite);
1229   hm->rewrite = 0;
1230   hm->has_trace_option = 0;
1231   hm->has_pot_option = 0;
1232   hm->has_seqno_option = 0;
1233   hm->has_analyse_option = 0;
1234   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST])
1235     hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (NULL, 1);
1236
1237   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT])
1238     hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (NULL, 1);
1239
1240   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])
1241     {
1242       hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] ((void *)
1243                                                              &hm->has_analyse_option,
1244                                                              1);
1245     }
1246
1247   return 0;
1248 }
1249
1250 clib_error_t *
1251 clear_ioam_rewrite_command_fn (vlib_main_t * vm,
1252                                unformat_input_t * input,
1253                                vlib_cli_command_t * cmd)
1254 {
1255   return (clear_ioam_rewrite_fn ());
1256 }
1257
1258 /*?
1259  * This command clears all the In-band OAM (iOAM) features enabled by
1260  * the '<em>set ioam rewrite</em>' command. Use '<em>show ioam summary</em>' to
1261  * verify the configured settings cleared.
1262  *
1263  * @cliexpar
1264  * Example of how to clear iOAM features:
1265  * @cliexcmd{clear ioam rewrite}
1266 ?*/
1267 /* *INDENT-OFF* */
1268 VLIB_CLI_COMMAND (ip6_clear_ioam_rewrite_cmd, static) = {
1269   .path = "clear ioam rewrite",
1270   .short_help = "clear ioam rewrite",
1271   .function = clear_ioam_rewrite_command_fn,
1272 };
1273 /* *INDENT-ON* */
1274
1275 clib_error_t *
1276 ip6_ioam_enable (int has_trace_option, int has_pot_option,
1277                  int has_seqno_option, int has_analyse_option)
1278 {
1279   int rv;
1280   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1281   rv = ip6_ioam_set_rewrite (&hm->rewrite, has_trace_option,
1282                              has_pot_option, has_seqno_option);
1283
1284   switch (rv)
1285     {
1286     case 0:
1287       if (has_trace_option)
1288         {
1289           hm->has_trace_option = has_trace_option;
1290           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST])
1291             hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (NULL,
1292                                                                       0);
1293         }
1294
1295       if (has_pot_option)
1296         {
1297           hm->has_pot_option = has_pot_option;
1298           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT])
1299             hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (NULL,
1300                                                                        0);
1301         }
1302       hm->has_analyse_option = has_analyse_option;
1303       if (has_seqno_option)
1304         {
1305           hm->has_seqno_option = has_seqno_option;
1306           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])
1307             {
1308               hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] ((void *)
1309                                                                      &has_analyse_option,
1310                                                                      0);
1311             }
1312         }
1313       break;
1314
1315     default:
1316       return clib_error_return_code (0, rv, 0,
1317                                      "ip6_ioam_set_rewrite returned %d", rv);
1318     }
1319
1320   return 0;
1321 }
1322
1323
1324 static clib_error_t *
1325 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
1326                                  unformat_input_t * input,
1327                                  vlib_cli_command_t * cmd)
1328 {
1329   int has_trace_option = 0;
1330   int has_pot_option = 0;
1331   int has_seqno_option = 0;
1332   int has_analyse_option = 0;
1333   clib_error_t *rv = 0;
1334
1335   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1336     {
1337       if (unformat (input, "trace"))
1338         has_trace_option = 1;
1339       else if (unformat (input, "pot"))
1340         has_pot_option = 1;
1341       else if (unformat (input, "seqno"))
1342         has_seqno_option = 1;
1343       else if (unformat (input, "analyse"))
1344         has_analyse_option = 1;
1345       else
1346         break;
1347     }
1348
1349
1350   rv = ip6_ioam_enable (has_trace_option, has_pot_option,
1351                         has_seqno_option, has_analyse_option);
1352
1353   return rv;
1354 }
1355
1356 /*?
1357  * This command is used to enable In-band OAM (iOAM) features on IPv6.
1358  * '<em>trace</em>' is used to enable iOAM trace feature. '<em>pot</em>' is used to
1359  * enable the Proof Of Transit feature. '<em>ppc</em>' is used to indicate the
1360  * Per Packet Counter feature for Edge to Edge processing. '<em>ppc</em>' is
1361  * used to indicate if this node is an '<em>encap</em>' node (iOAM edge node
1362  * where packet enters iOAM domain), a '<em>decap</em>' node (iOAM edge node
1363  * where packet leaves iOAM domain) or '<em>none</em>' (iOAM node where packet
1364  * is in-transit through the iOAM domain). '<em>ppc</em>' can only be set if
1365  * '<em>trace</em>' or '<em>pot</em>' is enabled.
1366  *
1367  * Use '<em>clear ioam rewrite</em>' to disable all features enabled by this
1368  * command. Use '<em>show ioam summary</em>' to verify the configured settings.
1369  *
1370  * @cliexpar
1371  * Example of how to enable trace and pot with ppc set to encap:
1372  * @cliexcmd{set ioam rewrite trace pot ppc encap}
1373 ?*/
1374 /* *INDENT-OFF* */
1375 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1376   .path = "set ioam rewrite",
1377   .short_help = "set ioam [trace] [pot] [seqno] [analyse]",
1378   .function = ip6_set_ioam_rewrite_command_fn,
1379 };
1380 /* *INDENT-ON* */
1381
1382 static clib_error_t *
1383 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1384                               unformat_input_t * input,
1385                               vlib_cli_command_t * cmd)
1386 {
1387   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1388   u8 *s = 0;
1389
1390
1391   if (!is_zero_ip6_address (&hm->adj))
1392     {
1393       s = format (s, "              REWRITE FLOW CONFIGS - \n");
1394       s = format (s, "               Destination Address : %U\n",
1395                   format_ip6_address, &hm->adj, sizeof (ip6_address_t));
1396       s =
1397         format (s, "                    Flow operation : %d (%s)\n",
1398                 hm->ioam_flag,
1399                 (hm->ioam_flag ==
1400                  IOAM_HBYH_ADD) ? "Add" : ((hm->ioam_flag ==
1401                                             IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1402     }
1403   else
1404     {
1405       s = format (s, "              REWRITE FLOW CONFIGS - Not configured\n");
1406     }
1407
1408
1409   s = format (s, "                        TRACE OPTION - %d (%s)\n",
1410               hm->has_trace_option,
1411               (hm->has_trace_option ? "Enabled" : "Disabled"));
1412   if (hm->has_trace_option)
1413     s =
1414       format (s,
1415               "Try 'show ioam trace and show ioam-trace profile' for more information\n");
1416
1417
1418   s = format (s, "                        POT OPTION - %d (%s)\n",
1419               hm->has_pot_option,
1420               (hm->has_pot_option ? "Enabled" : "Disabled"));
1421   if (hm->has_pot_option)
1422     s =
1423       format (s,
1424               "Try 'show ioam pot and show pot profile' for more information\n");
1425
1426   s = format (s, "         EDGE TO EDGE - SeqNo OPTION - %d (%s)\n",
1427               hm->has_seqno_option,
1428               hm->has_seqno_option ? "Enabled" : "Disabled");
1429   if (hm->has_seqno_option)
1430     s = format (s, "Try 'show ioam e2e' for more information\n");
1431
1432   s = format (s, "         iOAM Analyse OPTION - %d (%s)\n",
1433               hm->has_analyse_option,
1434               hm->has_analyse_option ? "Enabled" : "Disabled");
1435
1436   vlib_cli_output (vm, "%v", s);
1437   vec_free (s);
1438   return 0;
1439 }
1440
1441 /*?
1442  * This command displays the current configuration data for In-band
1443  * OAM (iOAM).
1444  *
1445  * @cliexpar
1446  * Example to show the iOAM configuration:
1447  * @cliexstart{show ioam summary}
1448  *               REWRITE FLOW CONFIGS -
1449  *                Destination Address : ff02::1
1450  *                     Flow operation : 2 (Pop)
1451  *                         TRACE OPTION - 1 (Enabled)
1452  * Try 'show ioam trace and show ioam-trace profile' for more information
1453  *                         POT OPTION - 1 (Enabled)
1454  * Try 'show ioam pot and show pot profile' for more information
1455  *          EDGE TO EDGE - PPC OPTION - 1 (Encap)
1456  * @cliexend
1457 ?*/
1458 /* *INDENT-OFF* */
1459 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1460   .path = "show ioam summary",
1461   .short_help = "show ioam summary",
1462   .function = ip6_show_ioam_summary_cmd_fn,
1463 };
1464 /* *INDENT-ON* */
1465
1466 void
1467 vnet_register_ioam_end_of_path_callback (void *cb)
1468 {
1469   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1470
1471   hm->ioam_end_of_path_cb = cb;
1472 }
1473
1474 #endif /* CLIB_MARCH_VARIANT */
1475 /*
1476  * fd.io coding-style-patch-verification: ON
1477  *
1478  * Local Variables:
1479  * eval: (c-set-style "gnu")
1480  * End:
1481  */