ip: add support for buffer offload metadata in ip midchain
[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 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) =
442 {
443   .name = "ip6-add-hop-by-hop",
444   .vector_size = sizeof (u32),
445   .format_trace = format_ip6_add_hop_by_hop_trace,
446   .type = VLIB_NODE_TYPE_INTERNAL,
447   .n_errors = ARRAY_LEN (ip6_add_hop_by_hop_error_strings),
448   .error_strings = ip6_add_hop_by_hop_error_strings,
449   /* See ip/lookup.h */
450   .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,
451   .next_nodes = {
452 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
453     foreach_ip6_hbyh_ioam_input_next
454 #undef _
455   },
456 };
457
458 /* The main h-b-h tracer was already invoked, no need to do much here */
459 typedef struct
460 {
461   u32 next_index;
462 } ip6_pop_hop_by_hop_trace_t;
463
464 /* packet trace format function */
465 static u8 *
466 format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
467 {
468   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
469   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
470   ip6_pop_hop_by_hop_trace_t *t =
471     va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
472
473   s = format (s, "IP6_POP_HOP_BY_HOP: next index %d", t->next_index);
474   return s;
475 }
476
477 #ifndef CLIB_MARCH_VARIANT
478 int
479 ip6_hbh_pop_register_option (u8 option,
480                              int options (vlib_buffer_t * b,
481                                           ip6_header_t * ip,
482                                           ip6_hop_by_hop_option_t * opt))
483 {
484   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
485
486   ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
487
488   /* Already registered */
489   if (hm->pop_options[option])
490     return (-1);
491
492   hm->pop_options[option] = options;
493
494   return (0);
495 }
496
497 int
498 ip6_hbh_pop_unregister_option (u8 option)
499 {
500   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
501
502   ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
503
504   /* Not registered */
505   if (!hm->pop_options[option])
506     return (-1);
507
508   hm->pop_options[option] = NULL;
509   return (0);
510 }
511 #endif /* CLIB_MARCH_VARIANT */
512
513 extern vlib_node_registration_t ip6_pop_hop_by_hop_node;
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,
523   foreach_ip6_pop_hop_by_hop_error
524 #undef _
525     IP6_POP_HOP_BY_HOP_N_ERROR,
526 } ip6_pop_hop_by_hop_error_t;
527
528 static char *ip6_pop_hop_by_hop_error_strings[] = {
529 #define _(sym,string) string,
530   foreach_ip6_pop_hop_by_hop_error
531 #undef _
532 };
533
534 static inline void
535 ioam_pop_hop_by_hop_processing (vlib_main_t * vm,
536                                 ip6_header_t * ip0,
537                                 ip6_hop_by_hop_header_t * hbh0,
538                                 vlib_buffer_t * b)
539 {
540   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
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                 {
567                   vlib_node_increment_counter (vm,
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 VLIB_NODE_FN (ip6_pop_hop_by_hop_node) (vlib_main_t * vm,
581                                         vlib_node_runtime_t * node,
582                                         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
621             clib_prefetch_store (p2->data);
622             clib_prefetch_store (p3->data);
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                 {
688                   ip6_pop_hop_by_hop_trace_t *t =
689                     vlib_add_trace (vm, node, b0, sizeof (*t));
690                   t->next_index = next0;
691                 }
692               if (b1->flags & VLIB_BUFFER_IS_TRACED)
693                 {
694                   ip6_pop_hop_by_hop_trace_t *t =
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;
714           ip6_hop_by_hop_header_t *hbh0;
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
755           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
756                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
757             {
758               ip6_pop_hop_by_hop_trace_t *t =
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 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) =
780 {
781   .name = "ip6-pop-hop-by-hop",
782   .vector_size = sizeof (u32),
783   .format_trace = format_ip6_pop_hop_by_hop_trace,
784   .type = VLIB_NODE_TYPE_INTERNAL,
785   .sibling_of = "ip6-lookup",
786   .n_errors = ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),
787   .error_strings = ip6_pop_hop_by_hop_error_strings,
788   /* See ip/lookup.h */
789   .n_next_nodes = 0,
790 };
791
792 typedef struct
793 {
794   u32 protocol;
795   u32 next_index;
796 } ip6_local_hop_by_hop_trace_t;
797
798 #ifndef CLIB_MARCH_VARIANT
799
800 /* packet trace format function */
801 static u8 *
802 format_ip6_local_hop_by_hop_trace (u8 * s, va_list * args)
803 {
804   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
805   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
806   ip6_local_hop_by_hop_trace_t *t =
807     va_arg (*args, ip6_local_hop_by_hop_trace_t *);
808
809   s = format (s, "IP6_LOCAL_HOP_BY_HOP: protocol %d,  next index %d\n",
810               t->protocol, t->next_index);
811   return s;
812 }
813
814 vlib_node_registration_t ip6_local_hop_by_hop_node;
815
816 #endif /* CLIB_MARCH_VARIANT */
817
818 #define foreach_ip6_local_hop_by_hop_error                      \
819 _(UNKNOWN, "Unknown protocol ip6 local h-b-h packets dropped")  \
820 _(OK, "Good ip6 local h-b-h packets")
821
822 typedef enum
823 {
824 #define _(sym,str) IP6_LOCAL_HOP_BY_HOP_ERROR_##sym,
825   foreach_ip6_local_hop_by_hop_error
826 #undef _
827     IP6_LOCAL_HOP_BY_HOP_N_ERROR,
828 } ip6_local_hop_by_hop_error_t;
829
830 #ifndef CLIB_MARCH_VARIANT
831 static char *ip6_local_hop_by_hop_error_strings[] = {
832 #define _(sym,string) string,
833   foreach_ip6_local_hop_by_hop_error
834 #undef _
835 };
836 #endif /* CLIB_MARCH_VARIANT */
837
838 typedef enum
839 {
840   IP6_LOCAL_HOP_BY_HOP_NEXT_DROP,
841   IP6_LOCAL_HOP_BY_HOP_N_NEXT,
842 } ip6_local_hop_by_hop_next_t;
843
844 always_inline uword
845 ip6_local_hop_by_hop_inline (vlib_main_t * vm,
846                              vlib_node_runtime_t * node, vlib_frame_t * frame,
847                              int is_trace)
848 {
849   u32 n_left_from, *from;
850   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
851   u16 nexts[VLIB_FRAME_SIZE], *next;
852   u32 ok = 0;
853   u32 unknown_proto_error = node->errors[IP6_LOCAL_HOP_BY_HOP_ERROR_UNKNOWN];
854   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
855
856   /* Note: there is only one of these */
857   ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime;
858
859   from = vlib_frame_vector_args (frame);
860   n_left_from = frame->n_vectors;
861
862   vlib_get_buffers (vm, from, bufs, n_left_from);
863   b = bufs;
864   next = nexts;
865
866   while (n_left_from >= 4)
867     {
868       ip6_header_t *ip0, *ip1, *ip2, *ip3;
869       u8 *hbh0, *hbh1, *hbh2, *hbh3;
870
871       /* Prefetch next iteration. */
872       if (PREDICT_TRUE (n_left_from >= 8))
873         {
874           vlib_prefetch_buffer_header (b[4], STORE);
875           vlib_prefetch_buffer_header (b[5], STORE);
876           vlib_prefetch_buffer_header (b[6], STORE);
877           vlib_prefetch_buffer_header (b[7], STORE);
878           clib_prefetch_store (b[4]->data);
879           clib_prefetch_store (b[5]->data);
880           clib_prefetch_store (b[6]->data);
881           clib_prefetch_store (b[7]->data);
882         }
883
884       /*
885        * Leave current_data pointing at the IP header.
886        * It's reasonably likely that any registered handler
887        * will want to know where to find the ip6 header.
888        */
889       ip0 = vlib_buffer_get_current (b[0]);
890       ip1 = vlib_buffer_get_current (b[1]);
891       ip2 = vlib_buffer_get_current (b[2]);
892       ip3 = vlib_buffer_get_current (b[3]);
893
894       /* Look at hop-by-hop header */
895       hbh0 = ip6_next_header (ip0);
896       hbh1 = ip6_next_header (ip1);
897       hbh2 = ip6_next_header (ip2);
898       hbh3 = ip6_next_header (ip3);
899
900       /*
901        * ... to find the next header type and see if we
902        * have a handler for it...
903        */
904       next[0] = rt->next_index_by_protocol[*hbh0];
905       next[1] = rt->next_index_by_protocol[*hbh1];
906       next[2] = rt->next_index_by_protocol[*hbh2];
907       next[3] = rt->next_index_by_protocol[*hbh3];
908
909       b[0]->error = unknown_proto_error;
910       b[1]->error = unknown_proto_error;
911       b[2]->error = unknown_proto_error;
912       b[3]->error = unknown_proto_error;
913
914       /* Account for non-drop pkts */
915       ok += next[0] != 0;
916       ok += next[1] != 0;
917       ok += next[2] != 0;
918       ok += next[3] != 0;
919
920       if (is_trace)
921         {
922           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
923             {
924               ip6_local_hop_by_hop_trace_t *t =
925                 vlib_add_trace (vm, node, b[0], sizeof (*t));
926               t->next_index = next[0];
927               t->protocol = *hbh0;
928             }
929           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
930             {
931               ip6_local_hop_by_hop_trace_t *t =
932                 vlib_add_trace (vm, node, b[1], sizeof (*t));
933               t->next_index = next[1];
934               t->protocol = *hbh1;
935             }
936           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
937             {
938               ip6_local_hop_by_hop_trace_t *t =
939                 vlib_add_trace (vm, node, b[2], sizeof (*t));
940               t->next_index = next[2];
941               t->protocol = *hbh2;
942             }
943           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
944             {
945               ip6_local_hop_by_hop_trace_t *t =
946                 vlib_add_trace (vm, node, b[3], sizeof (*t));
947               t->next_index = next[3];
948               t->protocol = *hbh3;
949             }
950         }
951
952       b += 4;
953       next += 4;
954       n_left_from -= 4;
955     }
956
957   while (n_left_from > 0)
958     {
959       ip6_header_t *ip0;
960       u8 *hbh0;
961
962       ip0 = vlib_buffer_get_current (b[0]);
963
964       hbh0 = ip6_next_header (ip0);
965
966       next[0] = rt->next_index_by_protocol[*hbh0];
967
968       b[0]->error = unknown_proto_error;
969       ok += next[0] != 0;
970
971       if (is_trace)
972         {
973           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
974             {
975               ip6_local_hop_by_hop_trace_t *t =
976                 vlib_add_trace (vm, node, b[0], sizeof (*t));
977               t->next_index = next[0];
978               t->protocol = *hbh0;
979             }
980         }
981
982       b += 1;
983       next += 1;
984       n_left_from -= 1;
985     }
986
987   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
988
989   vlib_node_increment_counter (vm, node->node_index,
990                                IP6_LOCAL_HOP_BY_HOP_ERROR_OK, ok);
991   return frame->n_vectors;
992 }
993
994 VLIB_NODE_FN (ip6_local_hop_by_hop_node) (vlib_main_t * vm,
995                                           vlib_node_runtime_t * node,
996                                           vlib_frame_t * frame)
997 {
998   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
999     return ip6_local_hop_by_hop_inline (vm, node, frame, 1 /* is_trace */ );
1000   else
1001     return ip6_local_hop_by_hop_inline (vm, node, frame, 0 /* is_trace */ );
1002 }
1003
1004 #ifndef CLIB_MARCH_VARIANT
1005 VLIB_REGISTER_NODE (ip6_local_hop_by_hop_node) =
1006 {
1007   .name = "ip6-local-hop-by-hop",
1008   .vector_size = sizeof (u32),
1009   .format_trace = format_ip6_local_hop_by_hop_trace,
1010   .type = VLIB_NODE_TYPE_INTERNAL,
1011
1012   .n_errors = ARRAY_LEN(ip6_local_hop_by_hop_error_strings),
1013   .error_strings = ip6_local_hop_by_hop_error_strings,
1014
1015   .n_next_nodes = IP6_LOCAL_HOP_BY_HOP_N_NEXT,
1016
1017   /* edit / add dispositions here */
1018   .next_nodes =
1019   {
1020     [IP6_LOCAL_HOP_BY_HOP_NEXT_DROP] = "error-drop",
1021   },
1022 };
1023
1024 clib_error_t *
1025 show_ip6_hbh_command_fn (vlib_main_t * vm,
1026                          unformat_input_t * input, vlib_cli_command_t * cmd)
1027 {
1028   int i;
1029   u32 next_index;
1030   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1031   ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime;
1032   vlib_node_t *n = vlib_get_node (vm, ip6_local_hop_by_hop_node.index);
1033
1034   vlib_cli_output (vm, "%-6s%s", "Proto", "Node Name");
1035
1036   for (i = 0; i < ARRAY_LEN (rt->next_index_by_protocol); i++)
1037     {
1038       if ((next_index = rt->next_index_by_protocol[i]))
1039         {
1040           u32 next_node_index = n->next_nodes[next_index];
1041           vlib_node_t *next_n = vlib_get_node (vm, next_node_index);
1042           vlib_cli_output (vm, "[%3d] %v", i, next_n->name);
1043         }
1044     }
1045
1046   return 0;
1047 }
1048
1049 /*?
1050  * Display the set of ip6 local hop-by-hop next protocol handler nodes
1051  *
1052  * @cliexpar
1053  * Display ip6 local hop-by-hop next protocol handler nodes
1054  * @cliexcmd{show ip6 hbh}
1055 ?*/
1056 VLIB_CLI_COMMAND (show_ip6_hbh, static) = {
1057   .path = "show ip6 hbh",
1058   .short_help = "show ip6 hbh",
1059   .function = show_ip6_hbh_command_fn,
1060 };
1061
1062
1063 #endif /* CLIB_MARCH_VARIANT */
1064
1065
1066 #ifndef CLIB_MARCH_VARIANT
1067 static clib_error_t *
1068 ip6_hop_by_hop_ioam_init (vlib_main_t * vm)
1069 {
1070   clib_error_t *error;
1071   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1072
1073   if ((error = vlib_call_init_function (vm, ip_main_init)))
1074     return (error);
1075
1076   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
1077     return error;
1078
1079   hm->vlib_main = vm;
1080   hm->vnet_main = vnet_get_main ();
1081   hm->unix_time_0 = (u32) time (0);     /* Store starting time */
1082   hm->vlib_time_0 = vlib_time_now (vm);
1083   hm->ioam_flag = IOAM_HBYH_MOD;
1084   clib_memset (hm->add_options, 0, sizeof (hm->add_options));
1085   clib_memset (hm->pop_options, 0, sizeof (hm->pop_options));
1086   clib_memset (hm->options_size, 0, sizeof (hm->options_size));
1087
1088   vnet_classify_register_unformat_opaque_index_fn (unformat_opaque_ioam);
1089   hm->ip6_local_hbh_runtime = clib_mem_alloc_aligned
1090     (sizeof (ip6_local_hop_by_hop_runtime_t), CLIB_CACHE_LINE_BYTES);
1091
1092   memset (hm->ip6_local_hbh_runtime, 0,
1093           sizeof (ip6_local_hop_by_hop_runtime_t));
1094
1095   ip6_register_protocol (IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS,
1096                          ip6_local_hop_by_hop_node.index);
1097   return (0);
1098 }
1099
1100 VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init) =
1101 {
1102   .runs_after = VLIB_INITS("ip_main_init", "ip6_lookup_init"),
1103 };
1104
1105 void
1106 ip6_local_hop_by_hop_register_protocol (u32 protocol, u32 node_index)
1107 {
1108   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1109   vlib_main_t *vm = hm->vlib_main;
1110   ip6_local_hop_by_hop_runtime_t *local_hbh_runtime
1111     = hm->ip6_local_hbh_runtime;
1112   u32 old_next_index;
1113
1114   ASSERT (protocol < ARRAY_LEN (local_hbh_runtime->next_index_by_protocol));
1115
1116   old_next_index = local_hbh_runtime->next_index_by_protocol[protocol];
1117
1118   local_hbh_runtime->next_index_by_protocol[protocol] =
1119     vlib_node_add_next (vm, ip6_local_hop_by_hop_node.index, node_index);
1120
1121   /* Someone will eventually do this. Trust me. */
1122   if (old_next_index &&
1123       (old_next_index != local_hbh_runtime->next_index_by_protocol[protocol]))
1124     clib_warning ("WARNING: replaced next index for protocol %d", protocol);
1125 }
1126
1127 int
1128 ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option,
1129                       int has_pot_option, int has_seqno_option)
1130 {
1131   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1132   u8 *rewrite = NULL;
1133   u32 size, rnd_size;
1134   ip6_hop_by_hop_header_t *hbh;
1135   u8 *current;
1136   u8 *trace_data_size = NULL;
1137   u8 *pot_data_size = NULL;
1138
1139   vec_free (*rwp);
1140
1141   if (has_trace_option == 0 && has_pot_option == 0)
1142     return -1;
1143
1144   /* Work out how much space we need */
1145   size = sizeof (ip6_hop_by_hop_header_t);
1146
1147   //if (has_trace_option && hm->get_sizeof_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
1148   if (has_trace_option
1149       && hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
1150     {
1151       size += hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
1152     }
1153   if (has_pot_option
1154       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
1155     {
1156       size += hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
1157     }
1158
1159   if (has_seqno_option)
1160     {
1161       size += hm->options_size[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE];
1162     }
1163
1164   /* Round to a multiple of 8 octets */
1165   rnd_size = (size + 7) & ~7;
1166
1167   /* allocate it, zero-fill / pad by construction */
1168   vec_validate (rewrite, rnd_size - 1);
1169
1170   hbh = (ip6_hop_by_hop_header_t *) rewrite;
1171   /* Length of header in 8 octet units, not incl first 8 octets */
1172   hbh->length = (rnd_size >> 3) - 1;
1173   current = (u8 *) (hbh + 1);
1174
1175   if (has_trace_option
1176       && hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
1177     {
1178       if (0 != (hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST]))
1179         {
1180           trace_data_size =
1181             &hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
1182           if (0 ==
1183               hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (current,
1184                                                                      trace_data_size))
1185             current += *trace_data_size;
1186         }
1187     }
1188   if (has_pot_option
1189       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
1190     {
1191       pot_data_size =
1192         &hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
1193       if (0 ==
1194           hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (current,
1195                                                                   pot_data_size))
1196         current += *pot_data_size;
1197     }
1198
1199   if (has_seqno_option &&
1200       (hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] != 0))
1201     {
1202       if (0 == hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] (current,
1203                                                                    &
1204                                                                    (hm->options_size
1205                                                                     [HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])))
1206         current += hm->options_size[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE];
1207     }
1208
1209   *rwp = rewrite;
1210   return 0;
1211 }
1212
1213 clib_error_t *
1214 clear_ioam_rewrite_fn (void)
1215 {
1216   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1217
1218   vec_free (hm->rewrite);
1219   hm->rewrite = 0;
1220   hm->has_trace_option = 0;
1221   hm->has_pot_option = 0;
1222   hm->has_seqno_option = 0;
1223   hm->has_analyse_option = 0;
1224   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST])
1225     hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (NULL, 1);
1226
1227   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT])
1228     hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (NULL, 1);
1229
1230   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])
1231     {
1232       hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] ((void *)
1233                                                              &hm->has_analyse_option,
1234                                                              1);
1235     }
1236
1237   return 0;
1238 }
1239
1240 clib_error_t *
1241 clear_ioam_rewrite_command_fn (vlib_main_t * vm,
1242                                unformat_input_t * input,
1243                                vlib_cli_command_t * cmd)
1244 {
1245   return (clear_ioam_rewrite_fn ());
1246 }
1247
1248 /*?
1249  * This command clears all the In-band OAM (iOAM) features enabled by
1250  * the '<em>set ioam rewrite</em>' command. Use '<em>show ioam summary</em>' to
1251  * verify the configured settings cleared.
1252  *
1253  * @cliexpar
1254  * Example of how to clear iOAM features:
1255  * @cliexcmd{clear ioam rewrite}
1256 ?*/
1257 VLIB_CLI_COMMAND (ip6_clear_ioam_rewrite_cmd, static) = {
1258   .path = "clear ioam rewrite",
1259   .short_help = "clear ioam rewrite",
1260   .function = clear_ioam_rewrite_command_fn,
1261 };
1262
1263 clib_error_t *
1264 ip6_ioam_enable (int has_trace_option, int has_pot_option,
1265                  int has_seqno_option, int has_analyse_option)
1266 {
1267   int rv;
1268   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1269   rv = ip6_ioam_set_rewrite (&hm->rewrite, has_trace_option,
1270                              has_pot_option, has_seqno_option);
1271
1272   switch (rv)
1273     {
1274     case 0:
1275       if (has_trace_option)
1276         {
1277           hm->has_trace_option = has_trace_option;
1278           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST])
1279             hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (NULL,
1280                                                                       0);
1281         }
1282
1283       if (has_pot_option)
1284         {
1285           hm->has_pot_option = has_pot_option;
1286           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT])
1287             hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (NULL,
1288                                                                        0);
1289         }
1290       hm->has_analyse_option = has_analyse_option;
1291       if (has_seqno_option)
1292         {
1293           hm->has_seqno_option = has_seqno_option;
1294           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])
1295             {
1296               hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] ((void *)
1297                                                                      &has_analyse_option,
1298                                                                      0);
1299             }
1300         }
1301       break;
1302
1303     default:
1304       return clib_error_return_code (0, rv, 0,
1305                                      "ip6_ioam_set_rewrite returned %d", rv);
1306     }
1307
1308   return 0;
1309 }
1310
1311
1312 static clib_error_t *
1313 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
1314                                  unformat_input_t * input,
1315                                  vlib_cli_command_t * cmd)
1316 {
1317   int has_trace_option = 0;
1318   int has_pot_option = 0;
1319   int has_seqno_option = 0;
1320   int has_analyse_option = 0;
1321   clib_error_t *rv = 0;
1322
1323   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1324     {
1325       if (unformat (input, "trace"))
1326         has_trace_option = 1;
1327       else if (unformat (input, "pot"))
1328         has_pot_option = 1;
1329       else if (unformat (input, "seqno"))
1330         has_seqno_option = 1;
1331       else if (unformat (input, "analyse"))
1332         has_analyse_option = 1;
1333       else
1334         break;
1335     }
1336
1337
1338   rv = ip6_ioam_enable (has_trace_option, has_pot_option,
1339                         has_seqno_option, has_analyse_option);
1340
1341   return rv;
1342 }
1343
1344 /*?
1345  * This command is used to enable In-band OAM (iOAM) features on IPv6.
1346  * '<em>trace</em>' is used to enable iOAM trace feature. '<em>pot</em>' is used to
1347  * enable the Proof Of Transit feature. '<em>ppc</em>' is used to indicate the
1348  * Per Packet Counter feature for Edge to Edge processing. '<em>ppc</em>' is
1349  * used to indicate if this node is an '<em>encap</em>' node (iOAM edge node
1350  * where packet enters iOAM domain), a '<em>decap</em>' node (iOAM edge node
1351  * where packet leaves iOAM domain) or '<em>none</em>' (iOAM node where packet
1352  * is in-transit through the iOAM domain). '<em>ppc</em>' can only be set if
1353  * '<em>trace</em>' or '<em>pot</em>' is enabled.
1354  *
1355  * Use '<em>clear ioam rewrite</em>' to disable all features enabled by this
1356  * command. Use '<em>show ioam summary</em>' to verify the configured settings.
1357  *
1358  * @cliexpar
1359  * Example of how to enable trace and pot with ppc set to encap:
1360  * @cliexcmd{set ioam rewrite trace pot ppc encap}
1361 ?*/
1362 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1363   .path = "set ioam rewrite",
1364   .short_help = "set ioam [trace] [pot] [seqno] [analyse]",
1365   .function = ip6_set_ioam_rewrite_command_fn,
1366 };
1367
1368 static clib_error_t *
1369 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1370                               unformat_input_t * input,
1371                               vlib_cli_command_t * cmd)
1372 {
1373   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1374   u8 *s = 0;
1375
1376
1377   if (!is_zero_ip6_address (&hm->adj))
1378     {
1379       s = format (s, "              REWRITE FLOW CONFIGS - \n");
1380       s = format (s, "               Destination Address : %U\n",
1381                   format_ip6_address, &hm->adj, sizeof (ip6_address_t));
1382       s =
1383         format (s, "                    Flow operation : %d (%s)\n",
1384                 hm->ioam_flag,
1385                 (hm->ioam_flag ==
1386                  IOAM_HBYH_ADD) ? "Add" : ((hm->ioam_flag ==
1387                                             IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1388     }
1389   else
1390     {
1391       s = format (s, "              REWRITE FLOW CONFIGS - Not configured\n");
1392     }
1393
1394
1395   s = format (s, "                        TRACE OPTION - %d (%s)\n",
1396               hm->has_trace_option,
1397               (hm->has_trace_option ? "Enabled" : "Disabled"));
1398   if (hm->has_trace_option)
1399     s =
1400       format (s,
1401               "Try 'show ioam trace and show ioam-trace profile' for more information\n");
1402
1403
1404   s = format (s, "                        POT OPTION - %d (%s)\n",
1405               hm->has_pot_option,
1406               (hm->has_pot_option ? "Enabled" : "Disabled"));
1407   if (hm->has_pot_option)
1408     s =
1409       format (s,
1410               "Try 'show ioam pot and show pot profile' for more information\n");
1411
1412   s = format (s, "         EDGE TO EDGE - SeqNo OPTION - %d (%s)\n",
1413               hm->has_seqno_option,
1414               hm->has_seqno_option ? "Enabled" : "Disabled");
1415   if (hm->has_seqno_option)
1416     s = format (s, "Try 'show ioam e2e' for more information\n");
1417
1418   s = format (s, "         iOAM Analyse OPTION - %d (%s)\n",
1419               hm->has_analyse_option,
1420               hm->has_analyse_option ? "Enabled" : "Disabled");
1421
1422   vlib_cli_output (vm, "%v", s);
1423   vec_free (s);
1424   return 0;
1425 }
1426
1427 /*?
1428  * This command displays the current configuration data for In-band
1429  * OAM (iOAM).
1430  *
1431  * @cliexpar
1432  * Example to show the iOAM configuration:
1433  * @cliexstart{show ioam summary}
1434  *               REWRITE FLOW CONFIGS -
1435  *                Destination Address : ff02::1
1436  *                     Flow operation : 2 (Pop)
1437  *                         TRACE OPTION - 1 (Enabled)
1438  * Try 'show ioam trace and show ioam-trace profile' for more information
1439  *                         POT OPTION - 1 (Enabled)
1440  * Try 'show ioam pot and show pot profile' for more information
1441  *          EDGE TO EDGE - PPC OPTION - 1 (Encap)
1442  * @cliexend
1443 ?*/
1444 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1445   .path = "show ioam summary",
1446   .short_help = "show ioam summary",
1447   .function = ip6_show_ioam_summary_cmd_fn,
1448 };
1449
1450 void
1451 vnet_register_ioam_end_of_path_callback (void *cb)
1452 {
1453   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1454
1455   hm->ioam_end_of_path_cb = cb;
1456 }
1457
1458 #endif /* CLIB_MARCH_VARIANT */
1459 /*
1460  * fd.io coding-style-patch-verification: ON
1461  *
1462  * Local Variables:
1463  * eval: (c-set-style "gnu")
1464  * End:
1465  */