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