VPP-19: Split the lookup.h IP_LOOKUP_NEXT enum.
[vpp.git] / vnet / vnet / ip / ip6_hop_by_hop.c
1 /*
2  * Copyright (c) 2015 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
28 #include <vnet/lib-scv/scv_util.h>
29
30 /* Timestamp precision multipliers for seconds, milliseconds, microseconds
31  * and nanoseconds respectively.
32  */
33 static f64 trace_tsp_mul[4] = {1, 1e3, 1e6, 1e9};
34
35 char *ppc_state[] = {"None", "Encap", "Decap"};
36
37 ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main;
38
39 #define foreach_ip6_hbyh_ioam_input_next        \
40   _(IP6_REWRITE, "ip6-rewrite")                 \
41   _(IP6_LOOKUP, "ip6-lookup")                   \
42   _(DROP, "error-drop")                        
43
44 typedef enum {
45 #define _(s,n) IP6_HBYH_IOAM_INPUT_NEXT_##s,
46   foreach_ip6_hbyh_ioam_input_next
47 #undef _
48   IP6_HBYH_IOAM_INPUT_N_NEXT,
49 } ip6_hbyh_ioam_input_next_t;
50
51 typedef union {
52     u64 as_u64;
53     u32 as_u32[2];
54 } time_u64_t;
55
56 static inline u8
57 fetch_trace_data_size(u8 trace_type)
58 {
59   u8 trace_data_size = 0;
60
61   if (trace_type == TRACE_TYPE_IF_TS_APP)   
62       trace_data_size = sizeof(ioam_trace_if_ts_app_t);
63   else if(trace_type == TRACE_TYPE_IF)      
64       trace_data_size = sizeof(ioam_trace_if_t);
65   else if(trace_type == TRACE_TYPE_TS)      
66       trace_data_size = sizeof(ioam_trace_ts_t);
67   else if(trace_type == TRACE_TYPE_APP)     
68       trace_data_size = sizeof(ioam_trace_app_t);
69   else if(trace_type == TRACE_TYPE_TS_APP)  
70       trace_data_size = sizeof(ioam_trace_ts_app_t);
71
72   return trace_data_size;
73 }
74
75 static u8 * format_ioam_data_list_element (u8 * s, va_list * args)
76
77   u32 *elt = va_arg (*args, u32 *);
78   u8  *trace_type_p = va_arg (*args, u8 *);
79   u8  trace_type = *trace_type_p;
80
81
82   if (trace_type & BIT_TTL_NODEID)
83     {
84       u32 ttl_node_id_host_byte_order = clib_net_to_host_u32 (*elt);
85       s = format (s, "ttl 0x%x node id 0x%x ",
86               ttl_node_id_host_byte_order>>24,
87               ttl_node_id_host_byte_order & 0x00FFFFFF);
88
89       elt++;
90     }
91  
92   if (trace_type & BIT_ING_INTERFACE && trace_type & BIT_ING_INTERFACE)
93     {
94         u32 ingress_host_byte_order = clib_net_to_host_u32(*elt);
95         s = format (s, "ingress 0x%x egress 0x%x ", 
96                    ingress_host_byte_order >> 16, 
97                    ingress_host_byte_order &0xFFFF);
98         elt++;
99     }
100  
101   if (trace_type & BIT_TIMESTAMP)
102     {
103         u32 ts_in_host_byte_order = clib_net_to_host_u32 (*elt);
104         s = format (s, "ts 0x%x \n", ts_in_host_byte_order);
105         elt++;
106     }
107  
108   if (trace_type & BIT_APPDATA)
109     {
110         u32 appdata_in_host_byte_order = clib_net_to_host_u32 (*elt);
111         s = format (s, "app 0x%x ", appdata_in_host_byte_order);
112         elt++;
113     }
114  
115   return s;
116 }
117
118 static u8 * format_ioam_pow (u8 * s, va_list * args)
119 {
120   ioam_pow_option_t * pow0 = va_arg (*args, ioam_pow_option_t *);
121   u64 random, cumulative;
122   random = cumulative = 0;
123   if (pow0) 
124     { 
125       random = clib_net_to_host_u64 (pow0->random);
126       cumulative = clib_net_to_host_u64 (pow0->cumulative);
127     }
128
129   s = format (s, "random = 0x%Lx, Cumulative = 0x%Lx, Index = 0x%x", 
130               random, cumulative, pow0->reserved_profile_id);
131   return s;
132 }
133
134 u8 *
135 ip6_hbh_ioam_trace_data_list_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt)
136 {
137   ioam_trace_option_t *trace;
138   u8 trace_data_size_in_words = 0;
139   u32 *elt;
140   int elt_index = 0;
141
142   trace = (ioam_trace_option_t *)opt;
143 #if 0
144   s = format (s, "  Trace Type 0x%x , %d elts left ts msb(s) 0x%x\n", trace->ioam_trace_type, trace->data_list_elts_left,
145               t->timestamp_msbs);
146 #endif
147   s = format (s, "  Trace Type 0x%x , %d elts left\n", trace->ioam_trace_type, trace->data_list_elts_left);
148   trace_data_size_in_words = fetch_trace_data_size(trace->ioam_trace_type)/4;
149   elt = &trace->elts[0];
150   while ((u8 *) elt < ((u8 *)(&trace->elts[0]) + trace->hdr.length - 2
151                         /* -2 accounts for ioam_trace_type,elts_left */)) {
152     s = format (s, "    [%d] %U\n",elt_index,
153                 format_ioam_data_list_element,
154                 elt, &trace->ioam_trace_type);
155     elt_index++;
156     elt += trace_data_size_in_words;
157   }
158   return (s);
159 }
160
161 u8 *
162 ip6_hbh_ioam_proof_of_work_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt)
163 {
164   ioam_pow_option_t *pow;
165
166   s = format (s, "    POW opt present\n");
167   pow = (ioam_pow_option_t *) opt;
168   s = format (s, "         %U\n", format_ioam_pow, pow);
169   return (s);
170 }
171
172 int
173 ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt)
174 {
175   ip6_main_t * im = &ip6_main;
176   ip_lookup_main_t * lm = &im->lookup_main;
177   ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
178   u8 elt_index = 0;
179   ioam_trace_option_t *trace = (ioam_trace_option_t *)opt;
180   u32 adj_index = vnet_buffer (b)->ip.adj_index[VLIB_TX];
181   ip_adjacency_t *adj = ip_get_adjacency (lm, adj_index);
182   time_u64_t time_u64;
183   u32 *elt;
184   int rv = 0;
185
186   time_u64.as_u64 = 0;
187
188   if (PREDICT_TRUE (trace->data_list_elts_left)) {
189     trace->data_list_elts_left--;
190     /* fetch_trace_data_size returns in bytes. Convert it to 4-bytes
191      * to skip to this node's location.
192      */
193     elt_index = trace->data_list_elts_left * fetch_trace_data_size(trace->ioam_trace_type) / 4;
194     elt = &trace->elts[elt_index];
195     if (trace->ioam_trace_type & BIT_TTL_NODEID) {
196       *elt = clib_host_to_net_u32 ((ip->hop_limit<<24) | hm->node_id);
197       elt++;
198     }
199
200     if (trace->ioam_trace_type & BIT_ING_INTERFACE) {
201       *elt = (vnet_buffer(b)->sw_if_index[VLIB_RX]&0xFFFF) << 16 | (adj->rewrite_header.sw_if_index & 0xFFFF);
202       *elt = clib_host_to_net_u32(*elt);
203       elt++;
204     }
205                  
206     if (trace->ioam_trace_type & BIT_TIMESTAMP) {
207       /* Send least significant 32 bits */
208       f64 time_f64 = (f64)(((f64)hm->unix_time_0) + (vlib_time_now(hm->vlib_main) - hm->vlib_time_0));
209
210       time_u64.as_u64 = time_f64 * trace_tsp_mul[hm->trace_tsp];
211       *elt = clib_host_to_net_u32(time_u64.as_u32[0]);
212       elt++;
213     }
214
215     if (trace->ioam_trace_type & BIT_APPDATA) {
216       /* $$$ set elt0->app_data */
217       *elt = clib_host_to_net_u32(hm->app_data);
218       elt++;
219     }
220   }
221   return (rv);
222 }
223
224 int
225 ip6_hbh_ioam_proof_of_work_handler (vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt)
226 {
227   ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
228   ioam_pow_option_t * pow;
229   u64 random = 0, cumulative = 0;
230   int rv = 0;
231
232   pow_profile = scv_profile_find(pow_profile_index);
233   if (PREDICT_FALSE(!pow_profile)) {
234     return (-1);
235   }
236
237   pow = (ioam_pow_option_t *) opt;
238
239   u8 pow_encap = (pow->random == 0);
240   if (pow_encap) {
241     if (PREDICT_FALSE(total_pkts_using_this_profile >= pow_profile->validity)) {
242       /* Choose a new profile */
243       u16 new_profile_index;
244       new_profile_index = scv_get_next_profile_id(hm->vlib_main, pow_profile_index);
245       if (new_profile_index != pow_profile_index) {
246         /* Got a new profile */
247         scv_profile_invalidate(hm->vlib_main, hm,
248                                pow_profile_index,
249                                pow_encap);
250         pow_profile_index = new_profile_index;
251         pow_profile = scv_profile_find(pow_profile_index);
252         total_pkts_using_this_profile = 0;
253       } else {
254         scv_profile_invalidate(hm->vlib_main, hm, pow_profile_index, pow_encap);
255       }
256     }
257     pow->reserved_profile_id = pow_profile_index & PROFILE_ID_MASK;
258     total_pkts_using_this_profile++;
259   } else { /* Non encap node */
260     if (PREDICT_FALSE(pow->reserved_profile_id != pow_profile_index)) {
261       /* New profile announced by encap node. */
262       scv_profile *new_profile = 0;
263       new_profile = scv_profile_find(pow->reserved_profile_id);
264       if (PREDICT_FALSE(new_profile == 0 || new_profile->validity == 0)) {
265         /* Profile is invalid. Use old profile*/
266         rv = -1;
267         scv_profile_invalidate(hm->vlib_main, hm,
268                                pow->reserved_profile_id,
269                                pow_encap);
270       } else {
271         scv_profile_invalidate(hm->vlib_main, hm,
272                                pow_profile_index,
273                                pow_encap);
274         pow_profile_index = pow->reserved_profile_id;
275         pow_profile = new_profile;
276         total_pkts_using_this_profile = 0;
277       }
278     }
279     total_pkts_using_this_profile++;
280   }
281
282   if (pow->random == 0) {
283     pow->random = clib_host_to_net_u64(scv_generate_random(pow_profile));
284     pow->cumulative = 0;
285   }
286   random = clib_net_to_host_u64(pow->random);
287   cumulative = clib_net_to_host_u64(pow->cumulative);
288   pow->cumulative = clib_host_to_net_u64(scv_update_cumulative(pow_profile, cumulative, random));
289
290   return (rv);
291 }
292
293 /* The main h-b-h tracer will be invoked, no need to do much here */
294 typedef struct {
295   u32 next_index;
296 } ip6_add_hop_by_hop_trace_t;
297
298 /* packet trace format function */
299 static u8 * format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
300 {
301   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
302   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
303   ip6_add_hop_by_hop_trace_t * t = va_arg (*args, 
304                                             ip6_add_hop_by_hop_trace_t *);
305   
306   s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d",
307               t->next_index);
308   return s;
309 }
310
311 vlib_node_registration_t ip6_add_hop_by_hop_node;
312
313 #define foreach_ip6_add_hop_by_hop_error \
314 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
315
316 typedef enum {
317 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
318   foreach_ip6_add_hop_by_hop_error
319 #undef _
320   IP6_ADD_HOP_BY_HOP_N_ERROR,
321 } ip6_add_hop_by_hop_error_t;
322
323 static char * ip6_add_hop_by_hop_error_strings[] = {
324 #define _(sym,string) string,
325   foreach_ip6_add_hop_by_hop_error
326 #undef _
327 };
328
329 static uword
330 ip6_add_hop_by_hop_node_fn (vlib_main_t * vm,
331                   vlib_node_runtime_t * node,
332                   vlib_frame_t * frame)
333 {
334   ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
335   u32 n_left_from, * from, * to_next;
336   ip_lookup_next_t next_index;
337   u32 processed = 0;
338   u8 * rewrite = hm->rewrite;
339   u32 rewrite_length = vec_len (rewrite);
340
341   from = vlib_frame_vector_args (frame);
342   n_left_from = frame->n_vectors;
343   next_index = node->cached_next_index;
344
345   while (n_left_from > 0)
346     {
347       u32 n_left_to_next;
348
349       vlib_get_next_frame (vm, node, next_index,
350                            to_next, n_left_to_next);
351
352 #if 0
353       while (n_left_from >= 4 && n_left_to_next >= 2)
354         {
355           u32 next0 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
356           u32 next1 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
357           u32 sw_if_index0, sw_if_index1;
358           u8 tmp0[6], tmp1[6];
359           ethernet_header_t *en0, *en1;
360           u32 bi0, bi1;
361           vlib_buffer_t * b0, * b1;
362           
363           /* Prefetch next iteration. */
364           {
365             vlib_buffer_t * p2, * p3;
366             
367             p2 = vlib_get_buffer (vm, from[2]);
368             p3 = vlib_get_buffer (vm, from[3]);
369             
370             vlib_prefetch_buffer_header (p2, LOAD);
371             vlib_prefetch_buffer_header (p3, LOAD);
372
373             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
374             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
375           }
376
377           /* speculatively enqueue b0 and b1 to the current next frame */
378           to_next[0] = bi0 = from[0];
379           to_next[1] = bi1 = from[1];
380           from += 2;
381           to_next += 2;
382           n_left_from -= 2;
383           n_left_to_next -= 2;
384
385           b0 = vlib_get_buffer (vm, bi0);
386           b1 = vlib_get_buffer (vm, bi1);
387
388           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
389           ASSERT (b0->current_data == 0);
390           ASSERT (b1->current_data == 0);
391           
392           ip0 = vlib_buffer_get_current (b0);
393           ip1 = vlib_buffer_get_current (b0);
394
395           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
396           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
397
398           /* $$$$$ End of processing 2 x packets $$$$$ */
399
400           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
401             {
402               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
403                 {
404                     ip6_add_hop_by_hop_trace_t *t = 
405                       vlib_add_trace (vm, node, b0, sizeof (*t));
406                     t->sw_if_index = sw_if_index0;
407                     t->next_index = next0;
408                   }
409                 if (b1->flags & VLIB_BUFFER_IS_TRACED) 
410                   {
411                     ip6_add_hop_by_hop_trace_t *t = 
412                       vlib_add_trace (vm, node, b1, sizeof (*t));
413                     t->sw_if_index = sw_if_index1;
414                     t->next_index = next1;
415                   }
416               }
417             
418             /* verify speculative enqueues, maybe switch current next frame */
419             vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
420                                              to_next, n_left_to_next,
421                                              bi0, bi1, next0, next1);
422         }
423 #endif
424
425       while (n_left_from > 0 && n_left_to_next > 0)
426         {
427           u32 bi0;
428           vlib_buffer_t * b0;
429           u32 next0;
430           ip6_header_t * ip0;
431           ip6_hop_by_hop_header_t * hbh0;
432           u64 * copy_src0, * copy_dst0;
433           u16 new_l0;
434           
435           /* speculatively enqueue b0 to the current next frame */
436           bi0 = from[0];
437           to_next[0] = bi0;
438           from += 1;
439           to_next += 1;
440           n_left_from -= 1;
441           n_left_to_next -= 1;
442
443           b0 = vlib_get_buffer (vm, bi0);
444
445           ip0 = vlib_buffer_get_current (b0);
446
447           /* Copy the ip header left by the required amount */
448           copy_dst0 = (u64 *)(((u8 *)ip0) - rewrite_length);
449           copy_src0 = (u64 *) ip0;
450
451           copy_dst0 [0] = copy_src0 [0];
452           copy_dst0 [1] = copy_src0 [1];
453           copy_dst0 [2] = copy_src0 [2];
454           copy_dst0 [3] = copy_src0 [3];
455           copy_dst0 [4] = copy_src0 [4];
456           vlib_buffer_advance (b0, - (word)rewrite_length);
457           ip0 = vlib_buffer_get_current (b0);
458
459           hbh0 = (ip6_hop_by_hop_header_t *)(ip0 + 1);
460           /* $$$ tune, rewrite_length is a multiple of 8 */
461           clib_memcpy (hbh0, rewrite, rewrite_length);
462           /* Patch the protocol chain, insert the h-b-h (type 0) header */
463           hbh0->protocol = ip0->protocol;
464           ip0->protocol = 0;
465           new_l0 = clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
466           ip0->payload_length = clib_host_to_net_u16 (new_l0);
467           
468           /* Populate the (first) h-b-h list elt */
469           next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
470
471           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
472                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
473             {
474               ip6_add_hop_by_hop_trace_t *t = 
475                  vlib_add_trace (vm, node, b0, sizeof (*t));
476               t->next_index = next0;
477             }
478             
479           processed++;
480
481           /* verify speculative enqueue, maybe switch current next frame */
482           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
483                                            to_next, n_left_to_next,
484                                            bi0, next0);
485         }
486
487       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
488     }
489
490   vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index, 
491                                IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
492   return frame->n_vectors;
493 }
494
495 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = {
496   .function = ip6_add_hop_by_hop_node_fn,
497   .name = "ip6-add-hop-by-hop",
498   .vector_size = sizeof (u32),
499   .format_trace = format_ip6_add_hop_by_hop_trace,
500   .type = VLIB_NODE_TYPE_INTERNAL,
501   
502   .n_errors = ARRAY_LEN(ip6_add_hop_by_hop_error_strings),
503   .error_strings = ip6_add_hop_by_hop_error_strings,
504
505   /* See ip/lookup.h */
506   .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,
507   .next_nodes = {
508 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
509     foreach_ip6_hbyh_ioam_input_next
510 #undef _
511   },
512 };
513
514 VLIB_NODE_FUNCTION_MULTIARCH (ip6_add_hop_by_hop_node, ip6_add_hop_by_hop_node_fn)
515
516 /* The main h-b-h tracer was already invoked, no need to do much here */
517 typedef struct {
518   u32 next_index;
519 } ip6_pop_hop_by_hop_trace_t;
520
521 /* packet trace format function */
522 static u8 * format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
523 {
524   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
525   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
526   ip6_pop_hop_by_hop_trace_t * t = va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
527   
528   s = format (s, "IP6_POP_HOP_BY_HOP: next index %d",
529               t->next_index);
530   return s;
531 }
532
533 vlib_node_registration_t ip6_pop_hop_by_hop_node;
534
535 #define foreach_ip6_pop_hop_by_hop_error                \
536 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options")  \
537 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
538 _(SCV_PASSED, "Pkts with SCV in Policy") \
539 _(SCV_FAILED, "Pkts with SCV out of Policy") 
540
541 typedef enum {
542 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
543   foreach_ip6_pop_hop_by_hop_error
544 #undef _
545   IP6_POP_HOP_BY_HOP_N_ERROR,
546 } ip6_pop_hop_by_hop_error_t;
547
548 static char * ip6_pop_hop_by_hop_error_strings[] = {
549 #define _(sym,string) string,
550   foreach_ip6_pop_hop_by_hop_error
551 #undef _
552 };
553
554 static inline void ioam_end_of_path_validation (vlib_main_t * vm,
555                                                 ip6_header_t *ip0,
556                                                 ip6_hop_by_hop_header_t *hbh0)
557 {
558   ip6_hop_by_hop_option_t *opt0, *limit0;
559   ioam_pow_option_t * pow0;
560   u8 type0;
561   u64 final_cumulative = 0;
562   u64 random = 0;
563   u8 result = 0;
564
565   if (!hbh0 || !ip0) return;
566
567   opt0 = (ip6_hop_by_hop_option_t *)(hbh0+1);
568   limit0 = (ip6_hop_by_hop_option_t *)
569     ((u8 *)hbh0 + ((hbh0->length+1)<<3));
570
571   /* Scan the set of h-b-h options, process ones that we understand */
572   while (opt0 < limit0)
573     {
574       type0 = opt0->type;
575       switch (type0)
576         {
577         case HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE:
578         case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
579           opt0 = (ip6_hop_by_hop_option_t *)
580             (((u8 *)opt0) + opt0->length
581              + sizeof (ip6_hop_by_hop_option_t));
582           break;
583         case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK:
584           pow0 = (ioam_pow_option_t *) opt0;
585           random = clib_net_to_host_u64(pow0->random);
586           final_cumulative = clib_net_to_host_u64(pow0->cumulative);
587           result =  scv_validate (pow_profile,
588                                        final_cumulative, random);
589           
590           if (result == 1) 
591             {
592               vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
593                                          IP6_POP_HOP_BY_HOP_ERROR_SCV_PASSED, result);
594             }
595           else 
596             {
597               vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
598                                          IP6_POP_HOP_BY_HOP_ERROR_SCV_FAILED, 1);
599             }
600           /* TODO: notify the scv failure*/
601           opt0 = (ip6_hop_by_hop_option_t *)
602             (((u8 *)opt0) + sizeof (ioam_pow_option_t));
603           break;
604
605         case 0: /* Pad */
606           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1;
607           break;
608
609         default:
610            format(0, "Something is wrong\n"); 
611            break;
612         }
613     }
614 }
615
616
617 static uword
618 ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm,
619                   vlib_node_runtime_t * node,
620                   vlib_frame_t * frame)
621 {
622   ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
623   ip6_main_t * im = &ip6_main;
624   ip_lookup_main_t * lm = &im->lookup_main;
625   u32 n_left_from, * from, * to_next;
626   ip_lookup_next_t next_index;
627   u32 processed = 0;
628   u32 no_header = 0;
629   u32 (*ioam_end_of_path_cb) (vlib_main_t *, vlib_node_runtime_t *,
630                               vlib_buffer_t *, ip6_header_t *, 
631                               ip_adjacency_t *);
632   
633   ioam_end_of_path_cb = hm->ioam_end_of_path_cb;
634   
635   from = vlib_frame_vector_args (frame);
636   n_left_from = frame->n_vectors;
637   next_index = node->cached_next_index;
638   
639   while (n_left_from > 0)
640     {
641       u32 n_left_to_next;
642
643       vlib_get_next_frame (vm, node, next_index,
644                            to_next, n_left_to_next);
645
646 #if 0
647       while (n_left_from >= 4 && n_left_to_next >= 2)
648         {
649           u32 next0 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
650           u32 next1 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
651           u32 sw_if_index0, sw_if_index1;
652           u8 tmp0[6], tmp1[6];
653           ethernet_header_t *en0, *en1;
654           u32 bi0, bi1;
655           vlib_buffer_t * b0, * b1;
656           
657           /* Prefetch next iteration. */
658           {
659             vlib_buffer_t * p2, * p3;
660             
661             p2 = vlib_get_buffer (vm, from[2]);
662             p3 = vlib_get_buffer (vm, from[3]);
663             
664             vlib_prefetch_buffer_header (p2, LOAD);
665             vlib_prefetch_buffer_header (p3, LOAD);
666
667             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
668             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
669           }
670
671           /* speculatively enqueue b0 and b1 to the current next frame */
672           to_next[0] = bi0 = from[0];
673           to_next[1] = bi1 = from[1];
674           from += 2;
675           to_next += 2;
676           n_left_from -= 2;
677           n_left_to_next -= 2;
678
679           b0 = vlib_get_buffer (vm, bi0);
680           b1 = vlib_get_buffer (vm, bi1);
681
682           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
683           ASSERT (b0->current_data == 0);
684           ASSERT (b1->current_data == 0);
685           
686           ip0 = vlib_buffer_get_current (b0);
687           ip1 = vlib_buffer_get_current (b0);
688
689           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
690           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
691
692           /* $$$$$ End of processing 2 x packets $$$$$ */
693
694           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
695             {
696               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
697                 {
698                     ip6_pop_hop_by_hop_trace_t *t = 
699                       vlib_add_trace (vm, node, b0, sizeof (*t));
700                     t->sw_if_index = sw_if_index0;
701                     t->next_index = next0;
702                   }
703                 if (b1->flags & VLIB_BUFFER_IS_TRACED) 
704                   {
705                     ip6_pop_hop_by_hop_trace_t *t = 
706                       vlib_add_trace (vm, node, b1, sizeof (*t));
707                     t->sw_if_index = sw_if_index1;
708                     t->next_index = next1;
709                   }
710               }
711             
712             /* verify speculative enqueues, maybe switch current next frame */
713             vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
714                                              to_next, n_left_to_next,
715                                              bi0, bi1, next0, next1);
716         }
717 #endif
718
719       while (n_left_from > 0 && n_left_to_next > 0)
720         {
721           u32 bi0;
722           vlib_buffer_t * b0;
723           u32 next0;
724           u32 adj_index0;
725           ip6_header_t * ip0;
726           ip_adjacency_t * adj0;
727           ip6_hop_by_hop_header_t *hbh0;
728           u64 * copy_dst0, * copy_src0;
729           u16 new_l0;
730           
731           /* speculatively enqueue b0 to the current next frame */
732           bi0 = from[0];
733           to_next[0] = bi0;
734           from += 1;
735           to_next += 1;
736           n_left_from -= 1;
737           n_left_to_next -= 1;
738
739           b0 = vlib_get_buffer (vm, bi0);
740
741           ip0 = vlib_buffer_get_current (b0);
742           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
743           adj0 = ip_get_adjacency (lm, adj_index0);
744
745           /* Default use the next_index from the adjacency. */
746           next0 = adj0->lookup_next_index;
747
748           /* Perfectly normal to end up here w/ out h-b-h header */
749           hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1);
750           
751           /* Collect data from trace via callback */
752           next0 = ioam_end_of_path_cb ? ioam_end_of_path_cb (vm, node, b0, ip0, adj0) : next0;
753
754           /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
755           ioam_end_of_path_validation(vm, ip0, hbh0);
756           /* Pop the trace data */
757           vlib_buffer_advance (b0, (hbh0->length+1)<<3);
758           new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
759             ((hbh0->length+1)<<3);
760           ip0->payload_length = clib_host_to_net_u16 (new_l0);
761           ip0->protocol = hbh0->protocol;
762           copy_src0 = (u64 *)ip0;
763           copy_dst0 = copy_src0 + (hbh0->length+1);
764           copy_dst0 [4] = copy_src0[4];
765           copy_dst0 [3] = copy_src0[3];
766           copy_dst0 [2] = copy_src0[2];
767           copy_dst0 [1] = copy_src0[1];
768           copy_dst0 [0] = copy_src0[0];
769           processed++;
770               
771           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
772                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
773             {
774               ip6_pop_hop_by_hop_trace_t *t = 
775                  vlib_add_trace (vm, node, b0, sizeof (*t));
776               t->next_index = next0;
777             }
778
779           /* verify speculative enqueue, maybe switch current next frame */
780           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
781                                            to_next, n_left_to_next,
782                                            bi0, next0);
783         }
784
785       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
786     }
787
788   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, 
789                                IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
790   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, 
791                                IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
792   return frame->n_vectors;
793 }
794
795 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) = {
796   .function = ip6_pop_hop_by_hop_node_fn,
797   .name = "ip6-pop-hop-by-hop",
798   .vector_size = sizeof (u32),
799   .format_trace = format_ip6_pop_hop_by_hop_trace,
800   .type = VLIB_NODE_TYPE_INTERNAL,
801   .sibling_of = "ip6-lookup",
802   .n_errors = ARRAY_LEN(ip6_pop_hop_by_hop_error_strings),
803   .error_strings = ip6_pop_hop_by_hop_error_strings,
804
805   /* See ip/lookup.h */
806   .n_next_nodes = 0,
807 };
808
809 VLIB_NODE_FUNCTION_MULTIARCH (ip6_pop_hop_by_hop_node,
810                               ip6_pop_hop_by_hop_node_fn)
811
812 static clib_error_t *
813 ip6_hop_by_hop_ioam_init (vlib_main_t * vm)
814 {
815   ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
816
817   hm->vlib_main = vm;
818   hm->vnet_main = vnet_get_main();
819   hm->unix_time_0 = (u32) time (0); /* Store starting time */
820   hm->vlib_time_0 = vlib_time_now (vm);
821   hm->ioam_flag = IOAM_HBYH_MOD;
822   hm->trace_tsp = TSP_MICROSECONDS; /* Micro seconds */
823
824   /*
825    * Register the handlers
826    * XXX: This should be done dynamically based on OAM feature being enabled or not.
827    */
828   if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST, ip6_hbh_ioam_trace_data_list_handler,
829                               ip6_hbh_ioam_trace_data_list_trace_handler) < 0)
830     return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST failed"));
831   if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK, ip6_hbh_ioam_proof_of_work_handler,
832                               ip6_hbh_ioam_proof_of_work_trace_handler) < 0)
833     return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK failed"));
834
835   return (0);
836 }
837
838 VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init);
839
840 int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts, 
841                           int has_pow_option, int has_ppc_option)
842 {
843   u8 *rewrite = 0;
844   u32 size, rnd_size;
845   ip6_hop_by_hop_header_t *hbh;
846   ioam_trace_option_t * trace_option;
847   ioam_pow_option_t * pow_option;
848   u8 *current;
849   u8 trace_data_size = 0;  
850
851   vec_free (*rwp);
852
853   if (trace_option_elts == 0 && has_pow_option == 0)
854     return -1;
855
856   /* Work out how much space we need */
857   size = sizeof (ip6_hop_by_hop_header_t);
858
859   if (trace_option_elts)
860     {
861       size += sizeof (ip6_hop_by_hop_option_t);
862
863       trace_data_size = fetch_trace_data_size(trace_type);
864       if (trace_data_size == 0)
865           return VNET_API_ERROR_INVALID_VALUE;
866
867       if (trace_option_elts * trace_data_size > 254)
868           return VNET_API_ERROR_INVALID_VALUE;
869   
870       size += trace_option_elts * trace_data_size;
871     }
872   if (has_pow_option)
873     {
874       size += sizeof (ip6_hop_by_hop_option_t);
875       size += sizeof (ioam_pow_option_t);
876     }
877
878   /* Round to a multiple of 8 octets */
879   rnd_size = (size + 7) & ~7;
880
881   /* allocate it, zero-fill / pad by construction */
882   vec_validate (rewrite, rnd_size-1);
883
884   hbh = (ip6_hop_by_hop_header_t *) rewrite;
885   /* Length of header in 8 octet units, not incl first 8 octets */
886   hbh->length = (rnd_size>>3) - 1;
887   current = (u8 *)(hbh+1);
888   
889   if (trace_option_elts)
890     {
891       trace_option = (ioam_trace_option_t *)current;
892       trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
893         | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
894       trace_option->hdr.length = 
895                2 /*ioam_trace_type,data_list_elts_left */ + 
896               trace_option_elts * trace_data_size;
897       trace_option->ioam_trace_type = trace_type & TRACE_TYPE_MASK;
898       trace_option->data_list_elts_left = trace_option_elts;
899       current += sizeof (ioam_trace_option_t) + 
900         trace_option_elts * trace_data_size;
901     }
902   if (has_pow_option)
903     {
904       pow_option = (ioam_pow_option_t *)current;
905       pow_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK
906         | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
907       pow_option->hdr.length = sizeof (ioam_pow_option_t) - 
908         sizeof (ip6_hop_by_hop_option_t);
909       current += sizeof (ioam_pow_option_t);
910     }
911   
912   *rwp = rewrite;
913   return 0;
914 }
915
916 clib_error_t *
917 clear_ioam_rewrite_fn(void)
918 {
919   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
920
921   vec_free(hm->rewrite);
922   hm->rewrite = 0;
923   hm->node_id = 0;
924   hm->app_data = 0;
925   hm->trace_type = 0;
926   hm->trace_option_elts = 0;
927   hm->has_pow_option = 0;
928   hm->has_ppc_option = 0;
929   hm->trace_tsp = TSP_MICROSECONDS; 
930
931   return 0;
932 }
933
934 clib_error_t * clear_ioam_rewrite_command_fn (vlib_main_t * vm,
935                                  unformat_input_t * input,
936                                  vlib_cli_command_t * cmd)
937 {
938   return(clear_ioam_rewrite_fn());
939 }
940   
941 VLIB_CLI_COMMAND (ip6_clear_ioam_trace_cmd, static) = {
942   .path = "clear ioam rewrite",
943   .short_help = "clear ioam rewrite",
944   .function = clear_ioam_rewrite_command_fn,
945 };
946
947 clib_error_t *
948 ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id,
949                            u32 app_data, int has_pow_option, u32 trace_tsp, 
950                            int has_ppc_option)
951 {
952   int rv;
953   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
954   rv = ip6_ioam_set_rewrite (&hm->rewrite, trace_type, trace_option_elts,
955                              has_pow_option, has_ppc_option);
956
957   switch (rv)
958     {
959     case 0:
960       hm->node_id = node_id;
961       hm->app_data = app_data;
962       hm->trace_type = trace_type;
963       hm->trace_option_elts = trace_option_elts;
964       hm->has_pow_option = has_pow_option;
965       hm->has_ppc_option = has_ppc_option;
966       hm->trace_tsp = trace_tsp;
967       break;
968
969     default:
970       return clib_error_return_code(0, rv, 0, "ip6_ioam_set_rewrite returned %d", rv);
971     }
972
973   return 0;
974 }
975
976
977 static clib_error_t *
978 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
979                                  unformat_input_t * input,
980                                  vlib_cli_command_t * cmd)
981 {
982   u32 trace_option_elts = 0;
983   u32 trace_type = 0, node_id = 0; 
984   u32 app_data = 0, trace_tsp = TSP_MICROSECONDS;
985   int has_pow_option = 0;
986   int has_ppc_option = 0;
987   clib_error_t * rv = 0;
988   
989   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
990     {
991       if (unformat (input, "trace-type 0x%x trace-elts %d "
992                            "trace-tsp %d node-id 0x%x app-data 0x%x", 
993                       &trace_type, &trace_option_elts, &trace_tsp,
994                       &node_id, &app_data))
995             ;
996       else if (unformat (input, "pow"))
997         has_pow_option = 1;
998       else if (unformat (input, "ppc encap"))
999         has_ppc_option = PPC_ENCAP;
1000       else if (unformat (input, "ppc decap"))
1001         has_ppc_option = PPC_DECAP;
1002       else if (unformat (input, "ppc none"))
1003         has_ppc_option = PPC_NONE;
1004       else
1005         break;
1006     }
1007   
1008     
1009     rv = ip6_ioam_trace_profile_set(trace_option_elts, trace_type, node_id,
1010                            app_data, has_pow_option, trace_tsp, has_ppc_option);
1011
1012     return rv;
1013 }
1014
1015
1016 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1017   .path = "set ioam rewrite",
1018   .short_help = "set ioam rewrite trace-type <0x1f|0x3|0x9|0x11|0x19> trace-elts <nn> trace-tsp <0|1|2|3> node-id <node id in hex> app-data <app_data in hex> [pow] [ppc <encap|decap>]",
1019   .function = ip6_set_ioam_rewrite_command_fn,
1020 };
1021   
1022 static clib_error_t *
1023 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1024                       unformat_input_t * input,
1025                       vlib_cli_command_t * cmd)
1026 {
1027   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1028   u8 *s = 0;
1029
1030
1031   if (!is_zero_ip6_address(&hm->adj))
1032   {
1033   s = format(s, "              REWRITE FLOW CONFIGS - \n");
1034   s = format(s, "               Destination Address : %U\n",
1035             format_ip6_address, &hm->adj, sizeof(ip6_address_t));
1036   s = format(s, "                    Flow operation : %d (%s)\n", hm->ioam_flag,
1037            (hm->ioam_flag == IOAM_HBYH_ADD) ? "Add" : 
1038           ((hm->ioam_flag == IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1039   } 
1040   else 
1041   {
1042   s = format(s, "              REWRITE FLOW CONFIGS - Not configured\n");
1043   }
1044
1045   if (hm->trace_option_elts)
1046   {
1047   s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - \n");
1048   s = format(s, "                        Trace Type : 0x%x (%d)\n", 
1049           hm->trace_type, hm->trace_type);
1050   s = format(s, "         Trace timestamp precision : %d (%s)\n", hm->trace_tsp,
1051        (hm->trace_tsp == TSP_SECONDS) ? "Seconds" : 
1052       ((hm->trace_tsp == TSP_MILLISECONDS) ? "Milliseconds" : 
1053      (((hm->trace_tsp == TSP_MICROSECONDS) ? "Microseconds" : "Nanoseconds"))));
1054   s = format(s, "                Num of trace nodes : %d\n", 
1055           hm->trace_option_elts);
1056   s = format(s, "                           Node-id : 0x%x (%d)\n", 
1057           hm->node_id, hm->node_id);
1058   s = format(s, "                          App Data : 0x%x (%d)\n", 
1059           hm->app_data, hm->app_data);
1060   }
1061   else
1062   {
1063   s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n");
1064   }
1065
1066   s = format(s, "                        POW OPTION - %d (%s)\n", 
1067           hm->has_pow_option, (hm->has_pow_option?"Enabled":"Disabled"));
1068   if (hm->has_pow_option)
1069     s = format(s, "Try 'show ioam sc-profile' for more information\n");
1070
1071   s = format(s, "         EDGE TO EDGE - PPC OPTION - %d (%s)\n", 
1072          hm->has_ppc_option, ppc_state[hm->has_ppc_option]);
1073   if (hm->has_ppc_option)
1074     s = format(s, "Try 'show ioam ppc' for more information\n");
1075
1076   vlib_cli_output(vm, "%v", s);
1077   vec_free(s);
1078   return 0;
1079 }
1080
1081 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1082   .path = "show ioam summary",
1083   .short_help = "Summary of IOAM configuration",
1084   .function = ip6_show_ioam_summary_cmd_fn,
1085 };
1086
1087 int ip6_ioam_set_destination (ip6_address_t *addr, u32 mask_width, u32 vrf_id,
1088                               int is_add, int is_pop, int is_none)
1089 {
1090   ip6_main_t * im = &ip6_main;
1091   ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
1092   ip_lookup_main_t * lm = &im->lookup_main;
1093   ip_adjacency_t * adj;
1094   u32 fib_index;
1095   u32 len, adj_index;
1096   int i, rv;
1097   uword * p;
1098   BVT(clib_bihash_kv) kv, value;
1099
1100   if ((is_add + is_pop + is_none) != 1)
1101     return VNET_API_ERROR_INVALID_VALUE_2;
1102
1103   /* Go find the adjacency we're supposed to tickle */
1104   p = hash_get (im->fib_index_by_table_id, vrf_id);
1105
1106   if (p == 0)
1107     return VNET_API_ERROR_NO_SUCH_FIB;
1108
1109   fib_index = p[0];
1110
1111   len = vec_len (im->prefix_lengths_in_search_order);
1112   
1113   for (i = 0; i < len; i++)
1114     {
1115       int dst_address_length = im->prefix_lengths_in_search_order[i];
1116       ip6_address_t * mask = &im->fib_masks[dst_address_length];
1117       
1118       if (dst_address_length != mask_width)
1119         continue;
1120
1121       kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
1122       kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
1123       kv.key[2] = ((u64)((fib_index))<<32) | dst_address_length;
1124       
1125       rv = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value);
1126       if (rv == 0)
1127         goto found;
1128
1129     }
1130   return VNET_API_ERROR_NO_SUCH_ENTRY;
1131   
1132  found:
1133
1134   /* Got it, modify as directed... */
1135   adj_index = value.value;
1136   adj = ip_get_adjacency (lm, adj_index);
1137
1138   /* Restore original lookup-next action */
1139   if (adj->saved_lookup_next_index)
1140     {
1141       adj->lookup_next_index = adj->saved_lookup_next_index;
1142       adj->saved_lookup_next_index = 0;
1143     }
1144
1145   /* Save current action */
1146   if (is_add || is_pop)
1147     adj->saved_lookup_next_index = adj->lookup_next_index;
1148
1149   if (is_add)
1150     adj->lookup_next_index = IP6_LOOKUP_NEXT_ADD_HOP_BY_HOP;
1151
1152   if (is_pop)
1153     adj->lookup_next_index = IP6_LOOKUP_NEXT_POP_HOP_BY_HOP;
1154
1155   hm->adj = *addr;
1156   hm->ioam_flag = (is_add ? IOAM_HBYH_ADD :
1157                   (is_pop ? IOAM_HBYH_POP : IOAM_HBYH_MOD));
1158   return 0;
1159 }
1160                               
1161 static clib_error_t *
1162 ip6_set_ioam_destination_command_fn (vlib_main_t * vm,
1163                                      unformat_input_t * input,
1164                                      vlib_cli_command_t * cmd)
1165 {
1166   ip6_address_t addr;
1167   u32 mask_width = ~0;
1168   int is_add = 0;
1169   int is_pop = 0;
1170   int is_none = 0;
1171   u32 vrf_id = 0;
1172   int rv;
1173
1174   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1175     {
1176       if (unformat (input, "%U/%d", 
1177                     unformat_ip6_address, &addr, &mask_width))
1178         ;
1179       else if (unformat (input, "vrf-id %d", &vrf_id))
1180         ;
1181       else if (unformat (input, "add"))
1182         is_add = 1;
1183       else if (unformat (input, "pop"))
1184         is_pop = 1;
1185       else if (unformat (input, "none"))
1186         is_none = 1;
1187       else
1188         break;
1189     }
1190
1191   if ((is_add + is_pop + is_none) != 1)
1192     return clib_error_return (0, "One of (add, pop, none) required");
1193   if (mask_width == ~0)
1194     return clib_error_return (0, "<address>/<mask-width> required");
1195
1196   rv = ip6_ioam_set_destination (&addr, mask_width, vrf_id, 
1197                                  is_add, is_pop, is_none);
1198
1199   switch (rv)
1200     {
1201     case 0:
1202       break;
1203     default:
1204       return clib_error_return (0, "ip6_ioam_set_destination returned %d", rv);
1205     }
1206   
1207   return 0;
1208 }
1209
1210 VLIB_CLI_COMMAND (ip6_set_ioam_destination_cmd, static) = {
1211   .path = "set ioam destination",
1212   .short_help = "set ioam destination <ip6-address>/<width> add | pop | none",
1213   .function = ip6_set_ioam_destination_command_fn,
1214 };
1215
1216
1217 void vnet_register_ioam_end_of_path_callback (void *cb)
1218 {
1219   ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
1220
1221   hm->ioam_end_of_path_cb = cb;
1222 }