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