Using classifier/ACL from now on. Changes pertaining to that.
[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               t->timestamp_msbs = time_u64.as_u32[1];
476               memcpy (t->option_data, hbh0, trace_len);
477             }
478             
479           processed++;
480
481           /* verify speculative enqueue, maybe switch current next frame */
482           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
483                                            to_next, n_left_to_next,
484                                            bi0, next0);
485         }
486
487       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
488     }
489
490     if (PREDICT_FALSE(unknown_opts > 0)) {
491       vlib_node_increment_counter (vm, ip6_hop_by_hop_node.index,
492                                    IP6_HOP_BY_HOP_ERROR_UNKNOWN_OPTION, unknown_opts);
493     }
494
495   vlib_node_increment_counter (vm, ip6_hop_by_hop_node.index, 
496                                IP6_HOP_BY_HOP_ERROR_PROCESSED, processed);
497   return frame->n_vectors;
498 }
499
500 VLIB_REGISTER_NODE (ip6_hop_by_hop_node) = {
501   .function = ip6_hop_by_hop_node_fn,
502   .name = "ip6-hop-by-hop",
503   .vector_size = sizeof (u32),
504   .format_trace = format_ip6_hop_by_hop_trace,
505   .type = VLIB_NODE_TYPE_INTERNAL,
506   
507   .n_errors = ARRAY_LEN(ip6_hop_by_hop_error_strings),
508   .error_strings = ip6_hop_by_hop_error_strings,
509
510   .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
511   .next_nodes = {
512 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
513     foreach_ip6_hbyh_input_next
514 #undef _
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 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
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 = IP6_HBYH_INPUT_N_NEXT,
732   .next_nodes = {
733 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
734     foreach_ip6_hbyh_input_next
735 #undef _
736   },
737 };
738
739
740 /* The main h-b-h tracer was already invoked, no need to do much here */
741 typedef struct {
742   u32 next_index;
743 } ip6_pop_hop_by_hop_trace_t;
744
745 /* packet trace format function */
746 static u8 * format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
747 {
748   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
749   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
750   ip6_pop_hop_by_hop_trace_t * t = va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
751   
752   s = format (s, "IP6_POP_HOP_BY_HOP: next index %d",
753               t->next_index);
754   return s;
755 }
756
757 vlib_node_registration_t ip6_pop_hop_by_hop_node;
758
759 #define foreach_ip6_pop_hop_by_hop_error                \
760 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options")  \
761 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options")
762
763 typedef enum {
764 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
765   foreach_ip6_pop_hop_by_hop_error
766 #undef _
767   IP6_POP_HOP_BY_HOP_N_ERROR,
768 } ip6_pop_hop_by_hop_error_t;
769
770 static char * ip6_pop_hop_by_hop_error_strings[] = {
771 #define _(sym,string) string,
772   foreach_ip6_pop_hop_by_hop_error
773 #undef _
774 };
775
776 static uword
777 ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm,
778                   vlib_node_runtime_t * node,
779                   vlib_frame_t * frame)
780 {
781   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
782   ip6_main_t * im = &ip6_main;
783   ip_lookup_main_t * lm = &im->lookup_main;
784   u32 n_left_from, * from, * to_next;
785   ip_lookup_next_t next_index;
786   u32 processed = 0;
787   u32 no_header = 0;
788   u32 (*ioam_end_of_path_cb) (vlib_main_t *, vlib_node_runtime_t *,
789                               vlib_buffer_t *, ip6_header_t *, 
790                               ip_adjacency_t *);
791   
792   ioam_end_of_path_cb = hm->ioam_end_of_path_cb;
793   
794   from = vlib_frame_vector_args (frame);
795   n_left_from = frame->n_vectors;
796   next_index = node->cached_next_index;
797   
798   while (n_left_from > 0)
799     {
800       u32 n_left_to_next;
801
802       vlib_get_next_frame (vm, node, next_index,
803                            to_next, n_left_to_next);
804
805 #if 0
806       while (n_left_from >= 4 && n_left_to_next >= 2)
807         {
808           u32 next0 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
809           u32 next1 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
810           u32 sw_if_index0, sw_if_index1;
811           u8 tmp0[6], tmp1[6];
812           ethernet_header_t *en0, *en1;
813           u32 bi0, bi1;
814           vlib_buffer_t * b0, * b1;
815           
816           /* Prefetch next iteration. */
817           {
818             vlib_buffer_t * p2, * p3;
819             
820             p2 = vlib_get_buffer (vm, from[2]);
821             p3 = vlib_get_buffer (vm, from[3]);
822             
823             vlib_prefetch_buffer_header (p2, LOAD);
824             vlib_prefetch_buffer_header (p3, LOAD);
825
826             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
827             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
828           }
829
830           /* speculatively enqueue b0 and b1 to the current next frame */
831           to_next[0] = bi0 = from[0];
832           to_next[1] = bi1 = from[1];
833           from += 2;
834           to_next += 2;
835           n_left_from -= 2;
836           n_left_to_next -= 2;
837
838           b0 = vlib_get_buffer (vm, bi0);
839           b1 = vlib_get_buffer (vm, bi1);
840
841           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
842           ASSERT (b0->current_data == 0);
843           ASSERT (b1->current_data == 0);
844           
845           ip0 = vlib_buffer_get_current (b0);
846           ip1 = vlib_buffer_get_current (b0);
847
848           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
849           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
850
851           /* $$$$$ End of processing 2 x packets $$$$$ */
852
853           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
854             {
855               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
856                 {
857                     ip6_pop_hop_by_hop_trace_t *t = 
858                       vlib_add_trace (vm, node, b0, sizeof (*t));
859                     t->sw_if_index = sw_if_index0;
860                     t->next_index = next0;
861                   }
862                 if (b1->flags & VLIB_BUFFER_IS_TRACED) 
863                   {
864                     ip6_pop_hop_by_hop_trace_t *t = 
865                       vlib_add_trace (vm, node, b1, sizeof (*t));
866                     t->sw_if_index = sw_if_index1;
867                     t->next_index = next1;
868                   }
869               }
870             
871             /* verify speculative enqueues, maybe switch current next frame */
872             vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
873                                              to_next, n_left_to_next,
874                                              bi0, bi1, next0, next1);
875         }
876 #endif
877
878       while (n_left_from > 0 && n_left_to_next > 0)
879         {
880           u32 bi0;
881           vlib_buffer_t * b0;
882           u32 next0;
883           u32 adj_index0;
884           ip6_header_t * ip0;
885           ip_adjacency_t * adj0;
886           ip6_hop_by_hop_header_t *hbh0;
887           u64 * copy_dst0, * copy_src0;
888           u16 new_l0;
889           
890           /* speculatively enqueue b0 to the current next frame */
891           bi0 = from[0];
892           to_next[0] = bi0;
893           from += 1;
894           to_next += 1;
895           n_left_from -= 1;
896           n_left_to_next -= 1;
897
898           b0 = vlib_get_buffer (vm, bi0);
899
900           ip0 = vlib_buffer_get_current (b0);
901           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
902           adj0 = ip_get_adjacency (lm, adj_index0);
903
904           /* Perfectly normal to end up here w/ out h-b-h header */
905           if (PREDICT_TRUE (ip0->protocol == 0))
906             {
907               hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1);
908           
909               if (vnet_buffer(b0)->l2_classify.opaque_index == OI_DECAP)
910                 { /* First pass. Send to hbyh node. */
911                   next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
912                   goto out1;
913                 }
914               else
915                 { /* Second pass */
916                   /* Collect data from trace via callback */
917                   next0 = ioam_end_of_path_cb ? 
918                           ioam_end_of_path_cb (vm, node, b0, ip0, adj0) :
919                           IP6_HBYH_INPUT_NEXT_IP6_REWRITE;
920                 }
921               
922               /* Pop the trace data */
923               vlib_buffer_advance (b0, (hbh0->length+1)<<3);
924               new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
925                 ((hbh0->length+1)<<3);
926               ip0->payload_length = clib_host_to_net_u16 (new_l0);
927               ip0->protocol = hbh0->protocol;
928               copy_src0 = (u64 *)ip0;
929               copy_dst0 = copy_src0 + (hbh0->length+1);
930               copy_dst0 [4] = copy_src0[4];
931               copy_dst0 [3] = copy_src0[3];
932               copy_dst0 [2] = copy_src0[2];
933               copy_dst0 [1] = copy_src0[1];
934               copy_dst0 [0] = copy_src0[0];
935               processed++;
936             }
937           else
938             {
939               next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
940               no_header++;
941             }
942               
943           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
944                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
945             {
946               ip6_pop_hop_by_hop_trace_t *t = 
947                  vlib_add_trace (vm, node, b0, sizeof (*t));
948               t->next_index = next0;
949             }
950
951 out1:
952           /* verify speculative enqueue, maybe switch current next frame */
953           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
954                                            to_next, n_left_to_next,
955                                            bi0, next0);
956         }
957
958       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
959     }
960
961   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, 
962                                IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
963   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, 
964                                IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
965   return frame->n_vectors;
966 }
967
968 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) = {
969   .function = ip6_pop_hop_by_hop_node_fn,
970   .name = "ip6-pop-hop-by-hop",
971   .vector_size = sizeof (u32),
972   .format_trace = format_ip6_pop_hop_by_hop_trace,
973   .type = VLIB_NODE_TYPE_INTERNAL,
974   
975   .n_errors = ARRAY_LEN(ip6_pop_hop_by_hop_error_strings),
976   .error_strings = ip6_pop_hop_by_hop_error_strings,
977
978   /* See ip/lookup.h */
979   .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
980   .next_nodes = {
981 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
982     foreach_ip6_hbyh_input_next
983 #undef _
984   },
985 };
986
987
988 static clib_error_t *
989 ip6_hop_by_hop_init (vlib_main_t * vm)
990 {
991   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
992
993   hm->vlib_main = vm;
994   hm->vnet_main = vnet_get_main();
995   hm->unix_time_0 = (u32) time (0); /* Store starting time */
996   hm->vlib_time_0 = vlib_time_now (vm);
997   hm->ioam_flag = IOAM_HBYH_MOD;
998   hm->trace_tsp = TSP_MICROSECONDS; /* Micro seconds */
999
1000   return 0;
1001 }
1002
1003 VLIB_INIT_FUNCTION (ip6_hop_by_hop_init);
1004
1005 int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts, 
1006                           int has_pow_option, int has_ppc_option)
1007 {
1008   u8 *rewrite = 0;
1009   u32 size, rnd_size;
1010   ip6_hop_by_hop_header_t *hbh;
1011   ioam_trace_option_t * trace_option;
1012   ioam_pow_option_t * pow_option;
1013   u8 *current;
1014   u8 trace_data_size = 0;  
1015
1016   vec_free (*rwp);
1017
1018   if (trace_option_elts == 0 && has_pow_option == 0)
1019     return -1;
1020
1021   /* Work out how much space we need */
1022   size = sizeof (ip6_hop_by_hop_header_t);
1023
1024   if (trace_option_elts)
1025     {
1026       size += sizeof (ip6_hop_by_hop_option_t);
1027
1028       trace_data_size = fetch_trace_data_size(trace_type);
1029       if (trace_data_size == 0)
1030           return VNET_API_ERROR_INVALID_VALUE;
1031
1032       if (trace_option_elts * trace_data_size > 254)
1033           return VNET_API_ERROR_INVALID_VALUE;
1034   
1035       size += trace_option_elts * trace_data_size;
1036     }
1037   if (has_pow_option)
1038     {
1039       size += sizeof (ip6_hop_by_hop_option_t);
1040       size += sizeof (ioam_pow_option_t);
1041     }
1042
1043   /* Round to a multiple of 8 octets */
1044   rnd_size = (size + 7) & ~7;
1045
1046   /* allocate it, zero-fill / pad by construction */
1047   vec_validate (rewrite, rnd_size-1);
1048
1049   hbh = (ip6_hop_by_hop_header_t *) rewrite;
1050   /* Length of header in 8 octet units, not incl first 8 octets */
1051   hbh->length = (rnd_size>>3) - 1;
1052   current = (u8 *)(hbh+1);
1053   
1054   if (trace_option_elts)
1055     {
1056       trace_option = (ioam_trace_option_t *)current;
1057       trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
1058         | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1059       trace_option->hdr.length = 
1060                2 /*ioam_trace_type,data_list_elts_left */ + 
1061               trace_option_elts * trace_data_size;
1062       trace_option->ioam_trace_type = trace_type & TRACE_TYPE_MASK;
1063       trace_option->data_list_elts_left = trace_option_elts;
1064       current += sizeof (ioam_trace_option_t) + 
1065         trace_option_elts * trace_data_size;
1066     }
1067   if (has_pow_option)
1068     {
1069       pow_option = (ioam_pow_option_t *)current;
1070       pow_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK
1071         | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1072       pow_option->hdr.length = sizeof (ioam_pow_option_t) - 
1073         sizeof (ip6_hop_by_hop_option_t);
1074       current += sizeof (ioam_pow_option_t);
1075     }
1076   
1077   *rwp = rewrite;
1078   return 0;
1079 }
1080
1081 clib_error_t *
1082 clear_ioam_rewrite_fn(void)
1083 {
1084   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1085
1086   vec_free(hm->rewrite);
1087   hm->rewrite = 0;
1088   hm->node_id = 0;
1089   hm->app_data = 0;
1090   hm->trace_type = 0;
1091   hm->trace_option_elts = 0;
1092   hm->has_pow_option = 0;
1093   hm->has_ppc_option = 0;
1094   hm->trace_tsp = TSP_MICROSECONDS; 
1095
1096   return 0;
1097 }
1098
1099 clib_error_t * clear_ioam_rewrite_command_fn (vlib_main_t * vm,
1100                                  unformat_input_t * input,
1101                                  vlib_cli_command_t * cmd)
1102 {
1103   return(clear_ioam_rewrite_fn());
1104 }
1105   
1106 VLIB_CLI_COMMAND (ip6_clear_ioam_trace_cmd, static) = {
1107   .path = "clear ioam rewrite",
1108   .short_help = "clear ioam rewrite",
1109   .function = clear_ioam_rewrite_command_fn,
1110 };
1111
1112 clib_error_t *
1113 ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id,
1114                            u32 app_data, int has_pow_option, u32 trace_tsp, 
1115                            int has_ppc_option)
1116 {
1117   int rv;
1118   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1119   rv = ip6_ioam_set_rewrite (&hm->rewrite, trace_type, trace_option_elts,
1120                              has_pow_option, has_ppc_option);
1121
1122   switch (rv)
1123     {
1124     case 0:
1125       hm->node_id = node_id;
1126       hm->app_data = app_data;
1127       hm->trace_type = trace_type;
1128       hm->trace_option_elts = trace_option_elts;
1129       hm->has_pow_option = has_pow_option;
1130       hm->has_ppc_option = has_ppc_option;
1131       hm->trace_tsp = trace_tsp;
1132       break;
1133
1134     default:
1135       return clib_error_return_code(0, rv, 0, "ip6_ioam_set_rewrite returned %d", rv);
1136     }
1137
1138   return 0;
1139 }
1140
1141
1142 static clib_error_t *
1143 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
1144                                  unformat_input_t * input,
1145                                  vlib_cli_command_t * cmd)
1146 {
1147   u32 trace_option_elts = 0;
1148   u32 trace_type = 0, node_id = 0; 
1149   u32 app_data = 0, trace_tsp = TSP_MICROSECONDS;
1150   int has_pow_option = 0;
1151   int has_ppc_option = 0;
1152   clib_error_t * rv = 0;
1153   
1154   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1155     {
1156       if (unformat (input, "trace-type 0x%x trace-elts %d "
1157                            "trace-tsp %d node-id 0x%x app-data 0x%x", 
1158                       &trace_type, &trace_option_elts, &trace_tsp,
1159                       &node_id, &app_data))
1160             ;
1161       else if (unformat (input, "pow"))
1162         has_pow_option = 1;
1163       else if (unformat (input, "ppc encap"))
1164         has_ppc_option = PPC_ENCAP;
1165       else if (unformat (input, "ppc decap"))
1166         has_ppc_option = PPC_DECAP;
1167       else if (unformat (input, "ppc none"))
1168         has_ppc_option = PPC_NONE;
1169       else
1170         break;
1171     }
1172   
1173     
1174     rv = ip6_ioam_trace_profile_set(trace_option_elts, trace_type, node_id,
1175                            app_data, has_pow_option, trace_tsp, has_ppc_option);
1176
1177     return rv;
1178 }
1179
1180
1181 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1182   .path = "set ioam rewrite",
1183   .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>]",
1184   .function = ip6_set_ioam_rewrite_command_fn,
1185 };
1186   
1187 static clib_error_t *
1188 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1189                       unformat_input_t * input,
1190                       vlib_cli_command_t * cmd)
1191 {
1192   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1193   u8 *s = 0;
1194
1195
1196   if (!is_zero_ip6_address(&hm->adj))
1197   {
1198   s = format(s, "              REWRITE FLOW CONFIGS - \n");
1199   s = format(s, "               Destination Address : %U\n",
1200             format_ip6_address, &hm->adj, sizeof(ip6_address_t));
1201   s = format(s, "                    Flow operation : %d (%s)\n", hm->ioam_flag,
1202            (hm->ioam_flag == IOAM_HBYH_ADD) ? "Add" : 
1203           ((hm->ioam_flag == IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1204   } 
1205   else 
1206   {
1207   s = format(s, "              REWRITE FLOW CONFIGS - Not configured\n");
1208   }
1209
1210   if (hm->trace_option_elts)
1211   {
1212   s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - \n");
1213   s = format(s, "                        Trace Type : 0x%x (%d)\n", 
1214           hm->trace_type, hm->trace_type);
1215   s = format(s, "         Trace timestamp precision : %d (%s)\n", hm->trace_tsp,
1216        (hm->trace_tsp == TSP_SECONDS) ? "Seconds" : 
1217       ((hm->trace_tsp == TSP_MILLISECONDS) ? "Milliseconds" : 
1218      (((hm->trace_tsp == TSP_MICROSECONDS) ? "Microseconds" : "Nanoseconds"))));
1219   s = format(s, "                Num of trace nodes : %d\n", 
1220           hm->trace_option_elts);
1221   s = format(s, "                           Node-id : 0x%x (%d)\n", 
1222           hm->node_id, hm->node_id);
1223   s = format(s, "                          App Data : 0x%x (%d)\n", 
1224           hm->app_data, hm->app_data);
1225   }
1226   else
1227   {
1228   s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n");
1229   }
1230
1231   s = format(s, "                        POW OPTION - %d (%s)\n", 
1232           hm->has_pow_option, (hm->has_pow_option?"Enabled":"Disabled"));
1233   if (hm->has_pow_option)
1234     s = format(s, "Try 'show ioam sc-profile' for more information\n");
1235
1236   s = format(s, "         EDGE TO EDGE - PPC OPTION - %d (%s)\n", 
1237          hm->has_ppc_option, ppc_state[hm->has_ppc_option]);
1238   if (hm->has_ppc_option)
1239     s = format(s, "Try 'show ioam ppc' for more information\n");
1240
1241   vlib_cli_output(vm, "%v", s);
1242   vec_free(s);
1243   return 0;
1244 }
1245
1246 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1247   .path = "show ioam summary",
1248   .short_help = "Summary of IOAM configuration",
1249   .function = ip6_show_ioam_summary_cmd_fn,
1250 };
1251
1252 int ip6_ioam_set_destination (ip6_address_t *addr, u32 mask_width, u32 vrf_id,
1253                               int is_add, int is_pop, int is_none)
1254 {
1255   ip6_main_t * im = &ip6_main;
1256   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1257   ip_lookup_main_t * lm = &im->lookup_main;
1258   ip_adjacency_t * adj;
1259   u32 fib_index;
1260   u32 len, adj_index;
1261   int i, rv;
1262   uword * p;
1263   BVT(clib_bihash_kv) kv, value;
1264
1265   if ((is_add + is_pop + is_none) != 1)
1266     return VNET_API_ERROR_INVALID_VALUE_2;
1267
1268   /* Go find the adjacency we're supposed to tickle */
1269   p = hash_get (im->fib_index_by_table_id, vrf_id);
1270
1271   if (p == 0)
1272     return VNET_API_ERROR_NO_SUCH_FIB;
1273
1274   fib_index = p[0];
1275
1276   len = vec_len (im->prefix_lengths_in_search_order);
1277   
1278   for (i = 0; i < len; i++)
1279     {
1280       int dst_address_length = im->prefix_lengths_in_search_order[i];
1281       ip6_address_t * mask = &im->fib_masks[dst_address_length];
1282       
1283       if (dst_address_length != mask_width)
1284         continue;
1285
1286       kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
1287       kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
1288       kv.key[2] = ((u64)((fib_index))<<32) | dst_address_length;
1289       
1290       rv = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value);
1291       if (rv == 0)
1292         goto found;
1293
1294     }
1295   return VNET_API_ERROR_NO_SUCH_ENTRY;
1296   
1297  found:
1298
1299   /* Got it, modify as directed... */
1300   adj_index = value.value;
1301   adj = ip_get_adjacency (lm, adj_index);
1302
1303   /* Restore original lookup-next action */
1304   if (adj->saved_lookup_next_index)
1305     {
1306       adj->lookup_next_index = adj->saved_lookup_next_index;
1307       adj->saved_lookup_next_index = 0;
1308     }
1309
1310   /* Save current action */
1311   if (is_add || is_pop)
1312     adj->saved_lookup_next_index = adj->lookup_next_index;
1313
1314   if (is_add)
1315     adj->lookup_next_index = IP_LOOKUP_NEXT_ADD_HOP_BY_HOP;
1316
1317   if (is_pop)
1318     adj->lookup_next_index = IP_LOOKUP_NEXT_POP_HOP_BY_HOP;
1319
1320   hm->adj = *addr;
1321   hm->ioam_flag = (is_add ? IOAM_HBYH_ADD :
1322                   (is_pop ? IOAM_HBYH_POP : IOAM_HBYH_MOD));
1323   return 0;
1324 }
1325                               
1326 static clib_error_t *
1327 ip6_set_ioam_destination_command_fn (vlib_main_t * vm,
1328                                      unformat_input_t * input,
1329                                      vlib_cli_command_t * cmd)
1330 {
1331   ip6_address_t addr;
1332   u32 mask_width = ~0;
1333   int is_add = 0;
1334   int is_pop = 0;
1335   int is_none = 0;
1336   u32 vrf_id = 0;
1337   int rv;
1338
1339   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1340     {
1341       if (unformat (input, "%U/%d", 
1342                     unformat_ip6_address, &addr, &mask_width))
1343         ;
1344       else if (unformat (input, "vrf-id %d", &vrf_id))
1345         ;
1346       else if (unformat (input, "add"))
1347         is_add = 1;
1348       else if (unformat (input, "pop"))
1349         is_pop = 1;
1350       else if (unformat (input, "none"))
1351         is_none = 1;
1352       else
1353         break;
1354     }
1355
1356   if ((is_add + is_pop + is_none) != 1)
1357     return clib_error_return (0, "One of (add, pop, none) required");
1358   if (mask_width == ~0)
1359     return clib_error_return (0, "<address>/<mask-width> required");
1360
1361   rv = ip6_ioam_set_destination (&addr, mask_width, vrf_id, 
1362                                  is_add, is_pop, is_none);
1363
1364   switch (rv)
1365     {
1366     case 0:
1367       break;
1368     default:
1369       return clib_error_return (0, "ip6_ioam_set_destination returned %d", rv);
1370     }
1371   
1372   return 0;
1373 }
1374
1375 VLIB_CLI_COMMAND (ip6_set_ioam_destination_cmd, static) = {
1376   .path = "set ioam destination",
1377   .short_help = "set ioam destination <ip6-address>/<width> add | pop | none",
1378   .function = ip6_set_ioam_destination_command_fn,
1379 };
1380
1381 void vnet_register_ioam_end_of_path_callback (void *cb)
1382 {
1383   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1384
1385   hm->ioam_end_of_path_cb = cb;
1386 }
1387                                              
1388