Add support for multiple microarchitectures in single binary
[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 VLIB_NODE_FUNCTION_MULTIARCH (ip6_hop_by_hop_node, ip6_hop_by_hop_node_fn)
632
633 /* The main h-b-h tracer will be invoked, no need to do much here */
634 typedef struct {
635   u32 next_index;
636 } ip6_add_hop_by_hop_trace_t;
637
638 /* packet trace format function */
639 static u8 * format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
640 {
641   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
642   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
643   ip6_add_hop_by_hop_trace_t * t = va_arg (*args, 
644                                             ip6_add_hop_by_hop_trace_t *);
645   
646   s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d",
647               t->next_index);
648   return s;
649 }
650
651 vlib_node_registration_t ip6_add_hop_by_hop_node;
652
653 #define foreach_ip6_add_hop_by_hop_error \
654 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
655
656 typedef enum {
657 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
658   foreach_ip6_add_hop_by_hop_error
659 #undef _
660   IP6_ADD_HOP_BY_HOP_N_ERROR,
661 } ip6_add_hop_by_hop_error_t;
662
663 static char * ip6_add_hop_by_hop_error_strings[] = {
664 #define _(sym,string) string,
665   foreach_ip6_add_hop_by_hop_error
666 #undef _
667 };
668
669 static uword
670 ip6_add_hop_by_hop_node_fn (vlib_main_t * vm,
671                   vlib_node_runtime_t * node,
672                   vlib_frame_t * frame)
673 {
674   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
675   u32 n_left_from, * from, * to_next;
676   ip_lookup_next_t next_index;
677   u32 processed = 0;
678   u8 * rewrite = hm->rewrite;
679   u32 rewrite_length = vec_len (rewrite);
680
681   from = vlib_frame_vector_args (frame);
682   n_left_from = frame->n_vectors;
683   next_index = node->cached_next_index;
684
685   while (n_left_from > 0)
686     {
687       u32 n_left_to_next;
688
689       vlib_get_next_frame (vm, node, next_index,
690                            to_next, n_left_to_next);
691
692 #if 0
693       while (n_left_from >= 4 && n_left_to_next >= 2)
694         {
695           u32 next0 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
696           u32 next1 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
697           u32 sw_if_index0, sw_if_index1;
698           u8 tmp0[6], tmp1[6];
699           ethernet_header_t *en0, *en1;
700           u32 bi0, bi1;
701           vlib_buffer_t * b0, * b1;
702           
703           /* Prefetch next iteration. */
704           {
705             vlib_buffer_t * p2, * p3;
706             
707             p2 = vlib_get_buffer (vm, from[2]);
708             p3 = vlib_get_buffer (vm, from[3]);
709             
710             vlib_prefetch_buffer_header (p2, LOAD);
711             vlib_prefetch_buffer_header (p3, LOAD);
712
713             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
714             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
715           }
716
717           /* speculatively enqueue b0 and b1 to the current next frame */
718           to_next[0] = bi0 = from[0];
719           to_next[1] = bi1 = from[1];
720           from += 2;
721           to_next += 2;
722           n_left_from -= 2;
723           n_left_to_next -= 2;
724
725           b0 = vlib_get_buffer (vm, bi0);
726           b1 = vlib_get_buffer (vm, bi1);
727
728           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
729           ASSERT (b0->current_data == 0);
730           ASSERT (b1->current_data == 0);
731           
732           ip0 = vlib_buffer_get_current (b0);
733           ip1 = vlib_buffer_get_current (b0);
734
735           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
736           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
737
738           /* $$$$$ End of processing 2 x packets $$$$$ */
739
740           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
741             {
742               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
743                 {
744                     ip6_add_hop_by_hop_trace_t *t = 
745                       vlib_add_trace (vm, node, b0, sizeof (*t));
746                     t->sw_if_index = sw_if_index0;
747                     t->next_index = next0;
748                   }
749                 if (b1->flags & VLIB_BUFFER_IS_TRACED) 
750                   {
751                     ip6_add_hop_by_hop_trace_t *t = 
752                       vlib_add_trace (vm, node, b1, sizeof (*t));
753                     t->sw_if_index = sw_if_index1;
754                     t->next_index = next1;
755                   }
756               }
757             
758             /* verify speculative enqueues, maybe switch current next frame */
759             vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
760                                              to_next, n_left_to_next,
761                                              bi0, bi1, next0, next1);
762         }
763 #endif
764
765       while (n_left_from > 0 && n_left_to_next > 0)
766         {
767           u32 bi0;
768           vlib_buffer_t * b0;
769           u32 next0;
770           ip6_header_t * ip0;
771           ip6_hop_by_hop_header_t * hbh0;
772           u64 * copy_src0, * copy_dst0;
773           u16 new_l0;
774           
775           /* speculatively enqueue b0 to the current next frame */
776           bi0 = from[0];
777           to_next[0] = bi0;
778           from += 1;
779           to_next += 1;
780           n_left_from -= 1;
781           n_left_to_next -= 1;
782
783           b0 = vlib_get_buffer (vm, bi0);
784
785           ip0 = vlib_buffer_get_current (b0);
786
787           /* Copy the ip header left by the required amount */
788           copy_dst0 = (u64 *)(((u8 *)ip0) - rewrite_length);
789           copy_src0 = (u64 *) ip0;
790
791           copy_dst0 [0] = copy_src0 [0];
792           copy_dst0 [1] = copy_src0 [1];
793           copy_dst0 [2] = copy_src0 [2];
794           copy_dst0 [3] = copy_src0 [3];
795           copy_dst0 [4] = copy_src0 [4];
796           vlib_buffer_advance (b0, - (word)rewrite_length);
797           ip0 = vlib_buffer_get_current (b0);
798
799           hbh0 = (ip6_hop_by_hop_header_t *)(ip0 + 1);
800           /* $$$ tune, rewrite_length is a multiple of 8 */
801           clib_memcpy (hbh0, rewrite, rewrite_length);
802           /* Patch the protocol chain, insert the h-b-h (type 0) header */
803           hbh0->protocol = ip0->protocol;
804           ip0->protocol = 0;
805           new_l0 = clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
806           ip0->payload_length = clib_host_to_net_u16 (new_l0);
807           
808           /* Populate the (first) h-b-h list elt */
809           next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
810
811           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
812                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
813             {
814               ip6_add_hop_by_hop_trace_t *t = 
815                  vlib_add_trace (vm, node, b0, sizeof (*t));
816               t->next_index = next0;
817             }
818             
819           processed++;
820
821           /* verify speculative enqueue, maybe switch current next frame */
822           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
823                                            to_next, n_left_to_next,
824                                            bi0, next0);
825         }
826
827       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
828     }
829
830   vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index, 
831                                IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
832   return frame->n_vectors;
833 }
834
835 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = {
836   .function = ip6_add_hop_by_hop_node_fn,
837   .name = "ip6-add-hop-by-hop",
838   .vector_size = sizeof (u32),
839   .format_trace = format_ip6_add_hop_by_hop_trace,
840   .type = VLIB_NODE_TYPE_INTERNAL,
841   
842   .n_errors = ARRAY_LEN(ip6_add_hop_by_hop_error_strings),
843   .error_strings = ip6_add_hop_by_hop_error_strings,
844
845   /* See ip/lookup.h */
846   .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
847   .next_nodes = {
848 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
849     foreach_ip6_hbyh_input_next
850 #undef _
851   },
852 };
853
854 VLIB_NODE_FUNCTION_MULTIARCH (ip6_add_hop_by_hop_node, ip6_add_hop_by_hop_node_fn)
855
856 /* The main h-b-h tracer was already invoked, no need to do much here */
857 typedef struct {
858   u32 next_index;
859 } ip6_pop_hop_by_hop_trace_t;
860
861 /* packet trace format function */
862 static u8 * format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
863 {
864   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
865   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
866   ip6_pop_hop_by_hop_trace_t * t = va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
867   
868   s = format (s, "IP6_POP_HOP_BY_HOP: next index %d",
869               t->next_index);
870   return s;
871 }
872
873 vlib_node_registration_t ip6_pop_hop_by_hop_node;
874
875 #define foreach_ip6_pop_hop_by_hop_error                \
876 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options")  \
877 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
878 _(SCV_PASSED, "Pkts with SCV in Policy") \
879 _(SCV_FAILED, "Pkts with SCV out of Policy") 
880
881 typedef enum {
882 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
883   foreach_ip6_pop_hop_by_hop_error
884 #undef _
885   IP6_POP_HOP_BY_HOP_N_ERROR,
886 } ip6_pop_hop_by_hop_error_t;
887
888 static char * ip6_pop_hop_by_hop_error_strings[] = {
889 #define _(sym,string) string,
890   foreach_ip6_pop_hop_by_hop_error
891 #undef _
892 };
893
894 static inline void ioam_end_of_path_validation (vlib_main_t * vm,
895                                                 ip6_header_t *ip0,
896                                                 ip6_hop_by_hop_header_t *hbh0)
897 {
898   ip6_hop_by_hop_option_t *opt0, *limit0;
899   ioam_pow_option_t * pow0;
900   u8 type0;
901   u64 final_cumulative = 0;
902   u64 random = 0;
903   u8 result = 0;
904
905   if (!hbh0 || !ip0) return;
906
907   opt0 = (ip6_hop_by_hop_option_t *)(hbh0+1);
908   limit0 = (ip6_hop_by_hop_option_t *)
909     ((u8 *)hbh0 + ((hbh0->length+1)<<3));
910
911   /* Scan the set of h-b-h options, process ones that we understand */
912   while (opt0 < limit0)
913     {
914       type0 = opt0->type & HBH_OPTION_TYPE_MASK;
915       switch (type0)
916         {
917         case HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE:
918         case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
919           opt0 = (ip6_hop_by_hop_option_t *)
920             (((u8 *)opt0) + opt0->length
921              + sizeof (ip6_hop_by_hop_option_t));
922           break;
923         case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK:
924           pow0 = (ioam_pow_option_t *) opt0;
925           random = clib_net_to_host_u64(pow0->random);
926           final_cumulative = clib_net_to_host_u64(pow0->cumulative);
927           result =  scv_validate (pow_profile,
928                                        final_cumulative, random);
929           
930           if (result == 1) 
931             {
932               vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
933                                          IP6_POP_HOP_BY_HOP_ERROR_SCV_PASSED, result);
934             }
935           else 
936             {
937               vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
938                                          IP6_POP_HOP_BY_HOP_ERROR_SCV_FAILED, 1);
939             }
940           /* TODO: notify the scv failure*/
941           opt0 = (ip6_hop_by_hop_option_t *)
942             (((u8 *)opt0) + sizeof (ioam_pow_option_t));
943           break;
944
945         case 0: /* Pad */
946           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1;
947           break;
948
949         default:
950            format(0, "Something is wrong\n"); 
951            break;
952         }
953     }
954 }
955
956
957 static uword
958 ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm,
959                   vlib_node_runtime_t * node,
960                   vlib_frame_t * frame)
961 {
962   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
963   ip6_main_t * im = &ip6_main;
964   ip_lookup_main_t * lm = &im->lookup_main;
965   u32 n_left_from, * from, * to_next;
966   ip_lookup_next_t next_index;
967   u32 processed = 0;
968   u32 no_header = 0;
969   u32 (*ioam_end_of_path_cb) (vlib_main_t *, vlib_node_runtime_t *,
970                               vlib_buffer_t *, ip6_header_t *, 
971                               ip_adjacency_t *);
972   
973   ioam_end_of_path_cb = hm->ioam_end_of_path_cb;
974   
975   from = vlib_frame_vector_args (frame);
976   n_left_from = frame->n_vectors;
977   next_index = node->cached_next_index;
978   
979   while (n_left_from > 0)
980     {
981       u32 n_left_to_next;
982
983       vlib_get_next_frame (vm, node, next_index,
984                            to_next, n_left_to_next);
985
986 #if 0
987       while (n_left_from >= 4 && n_left_to_next >= 2)
988         {
989           u32 next0 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
990           u32 next1 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
991           u32 sw_if_index0, sw_if_index1;
992           u8 tmp0[6], tmp1[6];
993           ethernet_header_t *en0, *en1;
994           u32 bi0, bi1;
995           vlib_buffer_t * b0, * b1;
996           
997           /* Prefetch next iteration. */
998           {
999             vlib_buffer_t * p2, * p3;
1000             
1001             p2 = vlib_get_buffer (vm, from[2]);
1002             p3 = vlib_get_buffer (vm, from[3]);
1003             
1004             vlib_prefetch_buffer_header (p2, LOAD);
1005             vlib_prefetch_buffer_header (p3, LOAD);
1006
1007             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
1008             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
1009           }
1010
1011           /* speculatively enqueue b0 and b1 to the current next frame */
1012           to_next[0] = bi0 = from[0];
1013           to_next[1] = bi1 = from[1];
1014           from += 2;
1015           to_next += 2;
1016           n_left_from -= 2;
1017           n_left_to_next -= 2;
1018
1019           b0 = vlib_get_buffer (vm, bi0);
1020           b1 = vlib_get_buffer (vm, bi1);
1021
1022           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
1023           ASSERT (b0->current_data == 0);
1024           ASSERT (b1->current_data == 0);
1025           
1026           ip0 = vlib_buffer_get_current (b0);
1027           ip1 = vlib_buffer_get_current (b0);
1028
1029           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1030           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
1031
1032           /* $$$$$ End of processing 2 x packets $$$$$ */
1033
1034           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
1035             {
1036               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
1037                 {
1038                     ip6_pop_hop_by_hop_trace_t *t = 
1039                       vlib_add_trace (vm, node, b0, sizeof (*t));
1040                     t->sw_if_index = sw_if_index0;
1041                     t->next_index = next0;
1042                   }
1043                 if (b1->flags & VLIB_BUFFER_IS_TRACED) 
1044                   {
1045                     ip6_pop_hop_by_hop_trace_t *t = 
1046                       vlib_add_trace (vm, node, b1, sizeof (*t));
1047                     t->sw_if_index = sw_if_index1;
1048                     t->next_index = next1;
1049                   }
1050               }
1051             
1052             /* verify speculative enqueues, maybe switch current next frame */
1053             vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1054                                              to_next, n_left_to_next,
1055                                              bi0, bi1, next0, next1);
1056         }
1057 #endif
1058
1059       while (n_left_from > 0 && n_left_to_next > 0)
1060         {
1061           u32 bi0;
1062           vlib_buffer_t * b0;
1063           u32 next0;
1064           u32 adj_index0;
1065           ip6_header_t * ip0;
1066           ip_adjacency_t * adj0;
1067           ip6_hop_by_hop_header_t *hbh0;
1068           u64 * copy_dst0, * copy_src0;
1069           u16 new_l0;
1070           
1071           /* speculatively enqueue b0 to the current next frame */
1072           bi0 = from[0];
1073           to_next[0] = bi0;
1074           from += 1;
1075           to_next += 1;
1076           n_left_from -= 1;
1077           n_left_to_next -= 1;
1078
1079           b0 = vlib_get_buffer (vm, bi0);
1080
1081           ip0 = vlib_buffer_get_current (b0);
1082           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
1083           adj0 = ip_get_adjacency (lm, adj_index0);
1084
1085           /* Perfectly normal to end up here w/ out h-b-h header */
1086           if (PREDICT_TRUE (ip0->protocol == 0))
1087             {
1088               hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1);
1089           
1090               if (vnet_buffer(b0)->l2_classify.opaque_index == OI_DECAP)
1091                 { /* First pass. Send to hbyh node. */
1092                   next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1093                   goto out1;
1094                 }
1095               
1096               /* Second pass */
1097               /* Collect data from trace via callback */
1098               next0 = ioam_end_of_path_cb ? 
1099                           ioam_end_of_path_cb (vm, node, b0, ip0, adj0) :
1100                  IP6_HBYH_INPUT_NEXT_IP6_REWRITE;
1101               /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
1102               ioam_end_of_path_validation(vm, ip0, hbh0);
1103               /* Pop the trace data */
1104               vlib_buffer_advance (b0, (hbh0->length+1)<<3);
1105               new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
1106                 ((hbh0->length+1)<<3);
1107               ip0->payload_length = clib_host_to_net_u16 (new_l0);
1108               ip0->protocol = hbh0->protocol;
1109               copy_src0 = (u64 *)ip0;
1110               copy_dst0 = copy_src0 + (hbh0->length+1);
1111               copy_dst0 [4] = copy_src0[4];
1112               copy_dst0 [3] = copy_src0[3];
1113               copy_dst0 [2] = copy_src0[2];
1114               copy_dst0 [1] = copy_src0[1];
1115               copy_dst0 [0] = copy_src0[0];
1116               processed++;
1117             }
1118           else
1119             {
1120               next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1121               no_header++;
1122             }
1123               
1124           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
1125                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
1126             {
1127               ip6_pop_hop_by_hop_trace_t *t = 
1128                  vlib_add_trace (vm, node, b0, sizeof (*t));
1129               t->next_index = next0;
1130             }
1131
1132 out1:
1133           /* verify speculative enqueue, maybe switch current next frame */
1134           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1135                                            to_next, n_left_to_next,
1136                                            bi0, next0);
1137         }
1138
1139       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1140     }
1141
1142   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, 
1143                                IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
1144   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, 
1145                                IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
1146   return frame->n_vectors;
1147 }
1148
1149 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) = {
1150   .function = ip6_pop_hop_by_hop_node_fn,
1151   .name = "ip6-pop-hop-by-hop",
1152   .vector_size = sizeof (u32),
1153   .format_trace = format_ip6_pop_hop_by_hop_trace,
1154   .type = VLIB_NODE_TYPE_INTERNAL,
1155   
1156   .n_errors = ARRAY_LEN(ip6_pop_hop_by_hop_error_strings),
1157   .error_strings = ip6_pop_hop_by_hop_error_strings,
1158
1159   /* See ip/lookup.h */
1160   .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
1161   .next_nodes = {
1162 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
1163     foreach_ip6_hbyh_input_next
1164 #undef _
1165   },
1166 };
1167
1168 VLIB_NODE_FUNCTION_MULTIARCH (ip6_pop_hop_by_hop_node,
1169                               ip6_pop_hop_by_hop_node_fn)
1170
1171 static clib_error_t *
1172 ip6_hop_by_hop_init (vlib_main_t * vm)
1173 {
1174   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1175
1176   hm->vlib_main = vm;
1177   hm->vnet_main = vnet_get_main();
1178   hm->unix_time_0 = (u32) time (0); /* Store starting time */
1179   hm->vlib_time_0 = vlib_time_now (vm);
1180   hm->ioam_flag = IOAM_HBYH_MOD;
1181   hm->trace_tsp = TSP_MICROSECONDS; /* Micro seconds */
1182
1183   return 0;
1184 }
1185
1186 VLIB_INIT_FUNCTION (ip6_hop_by_hop_init);
1187
1188 int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts, 
1189                           int has_pow_option, int has_ppc_option)
1190 {
1191   u8 *rewrite = 0;
1192   u32 size, rnd_size;
1193   ip6_hop_by_hop_header_t *hbh;
1194   ioam_trace_option_t * trace_option;
1195   ioam_pow_option_t * pow_option;
1196   u8 *current;
1197   u8 trace_data_size = 0;  
1198
1199   vec_free (*rwp);
1200
1201   if (trace_option_elts == 0 && has_pow_option == 0)
1202     return -1;
1203
1204   /* Work out how much space we need */
1205   size = sizeof (ip6_hop_by_hop_header_t);
1206
1207   if (trace_option_elts)
1208     {
1209       size += sizeof (ip6_hop_by_hop_option_t);
1210
1211       trace_data_size = fetch_trace_data_size(trace_type);
1212       if (trace_data_size == 0)
1213           return VNET_API_ERROR_INVALID_VALUE;
1214
1215       if (trace_option_elts * trace_data_size > 254)
1216           return VNET_API_ERROR_INVALID_VALUE;
1217   
1218       size += trace_option_elts * trace_data_size;
1219     }
1220   if (has_pow_option)
1221     {
1222       size += sizeof (ip6_hop_by_hop_option_t);
1223       size += sizeof (ioam_pow_option_t);
1224     }
1225
1226   /* Round to a multiple of 8 octets */
1227   rnd_size = (size + 7) & ~7;
1228
1229   /* allocate it, zero-fill / pad by construction */
1230   vec_validate (rewrite, rnd_size-1);
1231
1232   hbh = (ip6_hop_by_hop_header_t *) rewrite;
1233   /* Length of header in 8 octet units, not incl first 8 octets */
1234   hbh->length = (rnd_size>>3) - 1;
1235   current = (u8 *)(hbh+1);
1236   
1237   if (trace_option_elts)
1238     {
1239       trace_option = (ioam_trace_option_t *)current;
1240       trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
1241         | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1242       trace_option->hdr.length = 
1243                2 /*ioam_trace_type,data_list_elts_left */ + 
1244               trace_option_elts * trace_data_size;
1245       trace_option->ioam_trace_type = trace_type & TRACE_TYPE_MASK;
1246       trace_option->data_list_elts_left = trace_option_elts;
1247       current += sizeof (ioam_trace_option_t) + 
1248         trace_option_elts * trace_data_size;
1249     }
1250   if (has_pow_option)
1251     {
1252       pow_option = (ioam_pow_option_t *)current;
1253       pow_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK
1254         | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1255       pow_option->hdr.length = sizeof (ioam_pow_option_t) - 
1256         sizeof (ip6_hop_by_hop_option_t);
1257       current += sizeof (ioam_pow_option_t);
1258     }
1259   
1260   *rwp = rewrite;
1261   return 0;
1262 }
1263
1264 clib_error_t *
1265 clear_ioam_rewrite_fn(void)
1266 {
1267   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1268
1269   vec_free(hm->rewrite);
1270   hm->rewrite = 0;
1271   hm->node_id = 0;
1272   hm->app_data = 0;
1273   hm->trace_type = 0;
1274   hm->trace_option_elts = 0;
1275   hm->has_pow_option = 0;
1276   hm->has_ppc_option = 0;
1277   hm->trace_tsp = TSP_MICROSECONDS; 
1278
1279   return 0;
1280 }
1281
1282 clib_error_t * clear_ioam_rewrite_command_fn (vlib_main_t * vm,
1283                                  unformat_input_t * input,
1284                                  vlib_cli_command_t * cmd)
1285 {
1286   return(clear_ioam_rewrite_fn());
1287 }
1288   
1289 VLIB_CLI_COMMAND (ip6_clear_ioam_trace_cmd, static) = {
1290   .path = "clear ioam rewrite",
1291   .short_help = "clear ioam rewrite",
1292   .function = clear_ioam_rewrite_command_fn,
1293 };
1294
1295 clib_error_t *
1296 ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id,
1297                            u32 app_data, int has_pow_option, u32 trace_tsp, 
1298                            int has_ppc_option)
1299 {
1300   int rv;
1301   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1302   rv = ip6_ioam_set_rewrite (&hm->rewrite, trace_type, trace_option_elts,
1303                              has_pow_option, has_ppc_option);
1304
1305   switch (rv)
1306     {
1307     case 0:
1308       hm->node_id = node_id;
1309       hm->app_data = app_data;
1310       hm->trace_type = trace_type;
1311       hm->trace_option_elts = trace_option_elts;
1312       hm->has_pow_option = has_pow_option;
1313       hm->has_ppc_option = has_ppc_option;
1314       hm->trace_tsp = trace_tsp;
1315       break;
1316
1317     default:
1318       return clib_error_return_code(0, rv, 0, "ip6_ioam_set_rewrite returned %d", rv);
1319     }
1320
1321   return 0;
1322 }
1323
1324
1325 static clib_error_t *
1326 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
1327                                  unformat_input_t * input,
1328                                  vlib_cli_command_t * cmd)
1329 {
1330   u32 trace_option_elts = 0;
1331   u32 trace_type = 0, node_id = 0; 
1332   u32 app_data = 0, trace_tsp = TSP_MICROSECONDS;
1333   int has_pow_option = 0;
1334   int has_ppc_option = 0;
1335   clib_error_t * rv = 0;
1336   
1337   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1338     {
1339       if (unformat (input, "trace-type 0x%x trace-elts %d "
1340                            "trace-tsp %d node-id 0x%x app-data 0x%x", 
1341                       &trace_type, &trace_option_elts, &trace_tsp,
1342                       &node_id, &app_data))
1343             ;
1344       else if (unformat (input, "pow"))
1345         has_pow_option = 1;
1346       else if (unformat (input, "ppc encap"))
1347         has_ppc_option = PPC_ENCAP;
1348       else if (unformat (input, "ppc decap"))
1349         has_ppc_option = PPC_DECAP;
1350       else if (unformat (input, "ppc none"))
1351         has_ppc_option = PPC_NONE;
1352       else
1353         break;
1354     }
1355   
1356     
1357     rv = ip6_ioam_trace_profile_set(trace_option_elts, trace_type, node_id,
1358                            app_data, has_pow_option, trace_tsp, has_ppc_option);
1359
1360     return rv;
1361 }
1362
1363
1364 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1365   .path = "set ioam rewrite",
1366   .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>]",
1367   .function = ip6_set_ioam_rewrite_command_fn,
1368 };
1369   
1370 static clib_error_t *
1371 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1372                       unformat_input_t * input,
1373                       vlib_cli_command_t * cmd)
1374 {
1375   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1376   u8 *s = 0;
1377
1378
1379   if (!is_zero_ip6_address(&hm->adj))
1380   {
1381   s = format(s, "              REWRITE FLOW CONFIGS - \n");
1382   s = format(s, "               Destination Address : %U\n",
1383             format_ip6_address, &hm->adj, sizeof(ip6_address_t));
1384   s = format(s, "                    Flow operation : %d (%s)\n", hm->ioam_flag,
1385            (hm->ioam_flag == IOAM_HBYH_ADD) ? "Add" : 
1386           ((hm->ioam_flag == IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1387   } 
1388   else 
1389   {
1390   s = format(s, "              REWRITE FLOW CONFIGS - Not configured\n");
1391   }
1392
1393   if (hm->trace_option_elts)
1394   {
1395   s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - \n");
1396   s = format(s, "                        Trace Type : 0x%x (%d)\n", 
1397           hm->trace_type, hm->trace_type);
1398   s = format(s, "         Trace timestamp precision : %d (%s)\n", hm->trace_tsp,
1399        (hm->trace_tsp == TSP_SECONDS) ? "Seconds" : 
1400       ((hm->trace_tsp == TSP_MILLISECONDS) ? "Milliseconds" : 
1401      (((hm->trace_tsp == TSP_MICROSECONDS) ? "Microseconds" : "Nanoseconds"))));
1402   s = format(s, "                Num of trace nodes : %d\n", 
1403           hm->trace_option_elts);
1404   s = format(s, "                           Node-id : 0x%x (%d)\n", 
1405           hm->node_id, hm->node_id);
1406   s = format(s, "                          App Data : 0x%x (%d)\n", 
1407           hm->app_data, hm->app_data);
1408   }
1409   else
1410   {
1411   s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n");
1412   }
1413
1414   s = format(s, "                        POW OPTION - %d (%s)\n", 
1415           hm->has_pow_option, (hm->has_pow_option?"Enabled":"Disabled"));
1416   if (hm->has_pow_option)
1417     s = format(s, "Try 'show ioam sc-profile' for more information\n");
1418
1419   s = format(s, "         EDGE TO EDGE - PPC OPTION - %d (%s)\n", 
1420          hm->has_ppc_option, ppc_state[hm->has_ppc_option]);
1421   if (hm->has_ppc_option)
1422     s = format(s, "Try 'show ioam ppc' for more information\n");
1423
1424   vlib_cli_output(vm, "%v", s);
1425   vec_free(s);
1426   return 0;
1427 }
1428
1429 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1430   .path = "show ioam summary",
1431   .short_help = "Summary of IOAM configuration",
1432   .function = ip6_show_ioam_summary_cmd_fn,
1433 };
1434
1435 int ip6_ioam_set_destination (ip6_address_t *addr, u32 mask_width, u32 vrf_id,
1436                               int is_add, int is_pop, int is_none)
1437 {
1438   ip6_main_t * im = &ip6_main;
1439   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1440   ip_lookup_main_t * lm = &im->lookup_main;
1441   ip_adjacency_t * adj;
1442   u32 fib_index;
1443   u32 len, adj_index;
1444   int i, rv;
1445   uword * p;
1446   BVT(clib_bihash_kv) kv, value;
1447
1448   if ((is_add + is_pop + is_none) != 1)
1449     return VNET_API_ERROR_INVALID_VALUE_2;
1450
1451   /* Go find the adjacency we're supposed to tickle */
1452   p = hash_get (im->fib_index_by_table_id, vrf_id);
1453
1454   if (p == 0)
1455     return VNET_API_ERROR_NO_SUCH_FIB;
1456
1457   fib_index = p[0];
1458
1459   len = vec_len (im->prefix_lengths_in_search_order);
1460   
1461   for (i = 0; i < len; i++)
1462     {
1463       int dst_address_length = im->prefix_lengths_in_search_order[i];
1464       ip6_address_t * mask = &im->fib_masks[dst_address_length];
1465       
1466       if (dst_address_length != mask_width)
1467         continue;
1468
1469       kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
1470       kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
1471       kv.key[2] = ((u64)((fib_index))<<32) | dst_address_length;
1472       
1473       rv = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value);
1474       if (rv == 0)
1475         goto found;
1476
1477     }
1478   return VNET_API_ERROR_NO_SUCH_ENTRY;
1479   
1480  found:
1481
1482   /* Got it, modify as directed... */
1483   adj_index = value.value;
1484   adj = ip_get_adjacency (lm, adj_index);
1485
1486   /* Restore original lookup-next action */
1487   if (adj->saved_lookup_next_index)
1488     {
1489       adj->lookup_next_index = adj->saved_lookup_next_index;
1490       adj->saved_lookup_next_index = 0;
1491     }
1492
1493   /* Save current action */
1494   if (is_add || is_pop)
1495     adj->saved_lookup_next_index = adj->lookup_next_index;
1496
1497   if (is_add)
1498     adj->lookup_next_index = IP_LOOKUP_NEXT_ADD_HOP_BY_HOP;
1499
1500   if (is_pop)
1501     adj->lookup_next_index = IP_LOOKUP_NEXT_POP_HOP_BY_HOP;
1502
1503   hm->adj = *addr;
1504   hm->ioam_flag = (is_add ? IOAM_HBYH_ADD :
1505                   (is_pop ? IOAM_HBYH_POP : IOAM_HBYH_MOD));
1506   return 0;
1507 }
1508                               
1509 static clib_error_t *
1510 ip6_set_ioam_destination_command_fn (vlib_main_t * vm,
1511                                      unformat_input_t * input,
1512                                      vlib_cli_command_t * cmd)
1513 {
1514   ip6_address_t addr;
1515   u32 mask_width = ~0;
1516   int is_add = 0;
1517   int is_pop = 0;
1518   int is_none = 0;
1519   u32 vrf_id = 0;
1520   int rv;
1521
1522   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1523     {
1524       if (unformat (input, "%U/%d", 
1525                     unformat_ip6_address, &addr, &mask_width))
1526         ;
1527       else if (unformat (input, "vrf-id %d", &vrf_id))
1528         ;
1529       else if (unformat (input, "add"))
1530         is_add = 1;
1531       else if (unformat (input, "pop"))
1532         is_pop = 1;
1533       else if (unformat (input, "none"))
1534         is_none = 1;
1535       else
1536         break;
1537     }
1538
1539   if ((is_add + is_pop + is_none) != 1)
1540     return clib_error_return (0, "One of (add, pop, none) required");
1541   if (mask_width == ~0)
1542     return clib_error_return (0, "<address>/<mask-width> required");
1543
1544   rv = ip6_ioam_set_destination (&addr, mask_width, vrf_id, 
1545                                  is_add, is_pop, is_none);
1546
1547   switch (rv)
1548     {
1549     case 0:
1550       break;
1551     default:
1552       return clib_error_return (0, "ip6_ioam_set_destination returned %d", rv);
1553     }
1554   
1555   return 0;
1556 }
1557
1558 VLIB_CLI_COMMAND (ip6_set_ioam_destination_cmd, static) = {
1559   .path = "set ioam destination",
1560   .short_help = "set ioam destination <ip6-address>/<width> add | pop | none",
1561   .function = ip6_set_ioam_destination_command_fn,
1562 };
1563
1564
1565 void vnet_register_ioam_end_of_path_callback (void *cb)
1566 {
1567   ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1568
1569   hm->ioam_end_of_path_cb = cb;
1570 }
1571
1572                                              
1573