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