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