A Protocol Independent Hierarchical FIB (VPP-352)
[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/fib/ip6_fib.h>
28
29 char *ppc_state[] = { "None", "Encap", "Decap" };
30
31 ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main;
32
33 #define foreach_ip6_hbyh_ioam_input_next        \
34   _(IP6_REWRITE, "ip6-rewrite")                 \
35   _(IP6_LOOKUP, "ip6-lookup")                   \
36   _(DROP, "error-drop")
37
38 typedef enum
39 {
40 #define _(s,n) IP6_HBYH_IOAM_INPUT_NEXT_##s,
41   foreach_ip6_hbyh_ioam_input_next
42 #undef _
43     IP6_HBYH_IOAM_INPUT_N_NEXT,
44 } ip6_hbyh_ioam_input_next_t;
45
46
47
48 /* The main h-b-h tracer will be invoked, no need to do much here */
49 int
50 ip6_hbh_add_register_option (u8 option,
51                              u8 size,
52                              int rewrite_options (u8 * rewrite_string,
53                                                   u8 * rewrite_size))
54 {
55   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
56
57   ASSERT (option < ARRAY_LEN (hm->add_options));
58
59   /* Already registered */
60   if (hm->add_options[option])
61     return (-1);
62
63   hm->add_options[option] = rewrite_options;
64   hm->options_size[option] = size;
65
66   return (0);
67 }
68
69 int
70 ip6_hbh_add_unregister_option (u8 option)
71 {
72   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
73
74   ASSERT (option < ARRAY_LEN (hm->add_options));
75
76   /* Not registered */
77   if (!hm->add_options[option])
78     return (-1);
79
80   hm->add_options[option] = NULL;
81   hm->options_size[option] = 0;
82   return (0);
83 }
84
85 typedef struct
86 {
87   u32 next_index;
88 } ip6_add_hop_by_hop_trace_t;
89
90 /* packet trace format function */
91 static u8 *
92 format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
93 {
94   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
95   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
96   ip6_add_hop_by_hop_trace_t *t = va_arg (*args,
97                                           ip6_add_hop_by_hop_trace_t *);
98
99   s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d", t->next_index);
100   return s;
101 }
102
103 vlib_node_registration_t ip6_add_hop_by_hop_node;
104
105 #define foreach_ip6_add_hop_by_hop_error \
106 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
107
108 typedef enum
109 {
110 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
111   foreach_ip6_add_hop_by_hop_error
112 #undef _
113     IP6_ADD_HOP_BY_HOP_N_ERROR,
114 } ip6_add_hop_by_hop_error_t;
115
116 static char *ip6_add_hop_by_hop_error_strings[] = {
117 #define _(sym,string) string,
118   foreach_ip6_add_hop_by_hop_error
119 #undef _
120 };
121
122 static uword
123 ip6_add_hop_by_hop_node_fn (vlib_main_t * vm,
124                             vlib_node_runtime_t * node, vlib_frame_t * frame)
125 {
126   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
127   u32 n_left_from, *from, *to_next;
128   ip_lookup_next_t next_index;
129   u32 processed = 0;
130   u8 *rewrite = hm->rewrite;
131   u32 rewrite_length = vec_len (rewrite);
132
133   from = vlib_frame_vector_args (frame);
134   n_left_from = frame->n_vectors;
135   next_index = node->cached_next_index;
136
137   while (n_left_from > 0)
138     {
139       u32 n_left_to_next;
140
141       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
142       while (n_left_from >= 4 && n_left_to_next >= 2)
143         {
144           u32 bi0, bi1;
145           vlib_buffer_t *b0, *b1;
146           u32 next0, next1;
147           ip6_header_t *ip0, *ip1;
148           ip6_hop_by_hop_header_t *hbh0, *hbh1;
149           u64 *copy_src0, *copy_dst0, *copy_src1, *copy_dst1;
150           u16 new_l0, new_l1;
151
152           /* Prefetch next iteration. */
153           {
154             vlib_buffer_t *p2, *p3;
155
156             p2 = vlib_get_buffer (vm, from[2]);
157             p3 = vlib_get_buffer (vm, from[3]);
158
159             vlib_prefetch_buffer_header (p2, LOAD);
160             vlib_prefetch_buffer_header (p3, LOAD);
161
162             CLIB_PREFETCH (p2->data - rewrite_length,
163                            2 * CLIB_CACHE_LINE_BYTES, STORE);
164             CLIB_PREFETCH (p3->data - rewrite_length,
165                            2 * CLIB_CACHE_LINE_BYTES, STORE);
166           }
167
168           /* speculatively enqueue b0 and b1 to the current next frame */
169           to_next[0] = bi0 = from[0];
170           to_next[1] = bi1 = from[1];
171           from += 2;
172           to_next += 2;
173           n_left_from -= 2;
174           n_left_to_next -= 2;
175
176           b0 = vlib_get_buffer (vm, bi0);
177           b1 = vlib_get_buffer (vm, bi1);
178
179           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
180           ASSERT (b0->current_data == 0);
181           ASSERT (b1->current_data == 0);
182
183           ip0 = vlib_buffer_get_current (b0);
184           ip1 = vlib_buffer_get_current (b1);
185
186           /* Copy the ip header left by the required amount */
187           copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
188           copy_dst1 = (u64 *) (((u8 *) ip1) - rewrite_length);
189           copy_src0 = (u64 *) ip0;
190           copy_src1 = (u64 *) ip1;
191
192           copy_dst0[0] = copy_src0[0];
193           copy_dst0[1] = copy_src0[1];
194           copy_dst0[2] = copy_src0[2];
195           copy_dst0[3] = copy_src0[3];
196           copy_dst0[4] = copy_src0[4];
197
198           copy_dst1[0] = copy_src1[0];
199           copy_dst1[1] = copy_src1[1];
200           copy_dst1[2] = copy_src1[2];
201           copy_dst1[3] = copy_src1[3];
202           copy_dst1[4] = copy_src1[4];
203
204           vlib_buffer_advance (b0, -(word) rewrite_length);
205           vlib_buffer_advance (b1, -(word) rewrite_length);
206           ip0 = vlib_buffer_get_current (b0);
207           ip1 = vlib_buffer_get_current (b1);
208
209           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
210           hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
211           /* $$$ tune, rewrite_length is a multiple of 8 */
212           clib_memcpy (hbh0, rewrite, rewrite_length);
213           clib_memcpy (hbh1, rewrite, rewrite_length);
214           /* Patch the protocol chain, insert the h-b-h (type 0) header */
215           hbh0->protocol = ip0->protocol;
216           hbh1->protocol = ip1->protocol;
217           ip0->protocol = 0;
218           ip1->protocol = 0;
219           new_l0 =
220             clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
221           new_l1 =
222             clib_net_to_host_u16 (ip1->payload_length) + rewrite_length;
223           ip0->payload_length = clib_host_to_net_u16 (new_l0);
224           ip1->payload_length = clib_host_to_net_u16 (new_l1);
225
226           /* Populate the (first) h-b-h list elt */
227           next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
228           next1 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
229
230
231           /* $$$$$ End of processing 2 x packets $$$$$ */
232
233           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
234             {
235               if (b0->flags & VLIB_BUFFER_IS_TRACED)
236                 {
237                   ip6_add_hop_by_hop_trace_t *t =
238                     vlib_add_trace (vm, node, b0, sizeof (*t));
239                   t->next_index = next0;
240                 }
241               if (b1->flags & VLIB_BUFFER_IS_TRACED)
242                 {
243                   ip6_add_hop_by_hop_trace_t *t =
244                     vlib_add_trace (vm, node, b1, sizeof (*t));
245                   t->next_index = next1;
246                 }
247             }
248           processed += 2;
249           /* verify speculative enqueues, maybe switch current next frame */
250           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
251                                            to_next, n_left_to_next,
252                                            bi0, bi1, next0, next1);
253         }
254       while (n_left_from > 0 && n_left_to_next > 0)
255         {
256           u32 bi0;
257           vlib_buffer_t *b0;
258           u32 next0;
259           ip6_header_t *ip0;
260           ip6_hop_by_hop_header_t *hbh0;
261           u64 *copy_src0, *copy_dst0;
262           u16 new_l0;
263
264           /* speculatively enqueue b0 to the current next frame */
265           bi0 = from[0];
266           to_next[0] = bi0;
267           from += 1;
268           to_next += 1;
269           n_left_from -= 1;
270           n_left_to_next -= 1;
271
272           b0 = vlib_get_buffer (vm, bi0);
273
274           ip0 = vlib_buffer_get_current (b0);
275
276           /* Copy the ip header left by the required amount */
277           copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
278           copy_src0 = (u64 *) ip0;
279
280           copy_dst0[0] = copy_src0[0];
281           copy_dst0[1] = copy_src0[1];
282           copy_dst0[2] = copy_src0[2];
283           copy_dst0[3] = copy_src0[3];
284           copy_dst0[4] = copy_src0[4];
285           vlib_buffer_advance (b0, -(word) rewrite_length);
286           ip0 = vlib_buffer_get_current (b0);
287
288           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
289           /* $$$ tune, rewrite_length is a multiple of 8 */
290           clib_memcpy (hbh0, rewrite, rewrite_length);
291           /* Patch the protocol chain, insert the h-b-h (type 0) header */
292           hbh0->protocol = ip0->protocol;
293           ip0->protocol = 0;
294           new_l0 =
295             clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
296           ip0->payload_length = clib_host_to_net_u16 (new_l0);
297
298           /* Populate the (first) h-b-h list elt */
299           next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
300
301           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
302                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
303             {
304               ip6_add_hop_by_hop_trace_t *t =
305                 vlib_add_trace (vm, node, b0, sizeof (*t));
306               t->next_index = next0;
307             }
308
309           processed++;
310
311           /* verify speculative enqueue, maybe switch current next frame */
312           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
313                                            to_next, n_left_to_next,
314                                            bi0, next0);
315         }
316
317       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
318     }
319
320   vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
321                                IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
322   return frame->n_vectors;
323 }
324
325 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) =  /* *INDENT-OFF* */
326 {
327   .function = ip6_add_hop_by_hop_node_fn,.name =
328     "ip6-add-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
329     format_ip6_add_hop_by_hop_trace,.type =
330     VLIB_NODE_TYPE_INTERNAL,.n_errors =
331     ARRAY_LEN (ip6_add_hop_by_hop_error_strings),.error_strings =
332     ip6_add_hop_by_hop_error_strings,
333     /* See ip/lookup.h */
334     .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,.next_nodes =
335   {
336 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
337     foreach_ip6_hbyh_ioam_input_next
338 #undef _
339   }
340 ,};
341
342 /* *INDENT-ON* */
343
344 VLIB_NODE_FUNCTION_MULTIARCH (ip6_add_hop_by_hop_node,
345                               ip6_add_hop_by_hop_node_fn)
346 /* The main h-b-h tracer was already invoked, no need to do much here */
347      typedef struct
348      {
349        u32 next_index;
350      } ip6_pop_hop_by_hop_trace_t;
351
352 /* packet trace format function */
353      static u8 *format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
354 {
355   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
356   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
357   ip6_pop_hop_by_hop_trace_t *t =
358     va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
359
360   s = format (s, "IP6_POP_HOP_BY_HOP: next index %d", t->next_index);
361   return s;
362 }
363
364 int
365 ip6_hbh_pop_register_option (u8 option,
366                              int options (ip6_header_t * ip,
367                                           ip6_hop_by_hop_option_t * opt))
368 {
369   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
370
371   ASSERT (option < ARRAY_LEN (hm->pop_options));
372
373   /* Already registered */
374   if (hm->pop_options[option])
375     return (-1);
376
377   hm->pop_options[option] = options;
378
379   return (0);
380 }
381
382 int
383 ip6_hbh_pop_unregister_option (u8 option)
384 {
385   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
386
387   ASSERT (option < ARRAY_LEN (hm->pop_options));
388
389   /* Not registered */
390   if (!hm->pop_options[option])
391     return (-1);
392
393   hm->pop_options[option] = NULL;
394   return (0);
395 }
396
397 vlib_node_registration_t ip6_pop_hop_by_hop_node;
398
399 #define foreach_ip6_pop_hop_by_hop_error                \
400 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options")  \
401 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options")         \
402 _(OPTION_FAILED, "ip6 pop hop-by-hop failed to process")
403
404 typedef enum
405 {
406 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
407   foreach_ip6_pop_hop_by_hop_error
408 #undef _
409     IP6_POP_HOP_BY_HOP_N_ERROR,
410 } ip6_pop_hop_by_hop_error_t;
411
412 static char *ip6_pop_hop_by_hop_error_strings[] = {
413 #define _(sym,string) string,
414   foreach_ip6_pop_hop_by_hop_error
415 #undef _
416 };
417
418 static inline void
419 ioam_pop_hop_by_hop_processing (vlib_main_t * vm,
420                                 ip6_header_t * ip0,
421                                 ip6_hop_by_hop_header_t * hbh0)
422 {
423   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
424   ip6_hop_by_hop_option_t *opt0, *limit0;
425   u8 type0;
426
427   if (!hbh0 || !ip0)
428     return;
429
430   opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
431   limit0 = (ip6_hop_by_hop_option_t *)
432     ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
433
434   /* Scan the set of h-b-h options, process ones that we understand */
435   while (opt0 < limit0)
436     {
437       type0 = opt0->type;
438       switch (type0)
439         {
440         case 0:         /* Pad1 */
441           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
442           continue;
443         case 1:         /* PadN */
444           break;
445         default:
446           if (hm->pop_options[type0])
447             {
448               if ((*hm->pop_options[type0]) (ip0, opt0) < 0)
449                 {
450                   vlib_node_increment_counter (vm,
451                                                ip6_pop_hop_by_hop_node.index,
452                                                IP6_POP_HOP_BY_HOP_ERROR_OPTION_FAILED,
453                                                1);
454                 }
455             }
456         }
457       opt0 =
458         (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
459                                      sizeof (ip6_hop_by_hop_option_t));
460     }
461 }
462
463 static uword
464 ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm,
465                             vlib_node_runtime_t * node, vlib_frame_t * frame)
466 {
467   ip6_main_t *im = &ip6_main;
468   ip_lookup_main_t *lm = &im->lookup_main;
469   u32 n_left_from, *from, *to_next;
470   ip_lookup_next_t next_index;
471   u32 processed = 0;
472   u32 no_header = 0;
473
474   from = vlib_frame_vector_args (frame);
475   n_left_from = frame->n_vectors;
476   next_index = node->cached_next_index;
477
478   while (n_left_from > 0)
479     {
480       u32 n_left_to_next;
481
482       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
483
484       while (n_left_from >= 4 && n_left_to_next >= 2)
485         {
486           u32 bi0, bi1;
487           vlib_buffer_t *b0, *b1;
488           u32 next0, next1;
489           u32 adj_index0, adj_index1;
490           ip6_header_t *ip0, *ip1;
491           ip_adjacency_t *adj0, *adj1;
492           ip6_hop_by_hop_header_t *hbh0, *hbh1;
493           u64 *copy_dst0, *copy_src0, *copy_dst1, *copy_src1;
494           u16 new_l0, new_l1;
495
496           /* Prefetch next iteration. */
497           {
498             vlib_buffer_t *p2, *p3;
499
500             p2 = vlib_get_buffer (vm, from[2]);
501             p3 = vlib_get_buffer (vm, from[3]);
502
503             vlib_prefetch_buffer_header (p2, LOAD);
504             vlib_prefetch_buffer_header (p3, LOAD);
505
506             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
507             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
508           }
509
510           /* speculatively enqueue b0 and b1 to the current next frame */
511           to_next[0] = bi0 = from[0];
512           to_next[1] = bi1 = from[1];
513           from += 2;
514           to_next += 2;
515           n_left_from -= 2;
516           n_left_to_next -= 2;
517
518           b0 = vlib_get_buffer (vm, bi0);
519           b1 = vlib_get_buffer (vm, bi1);
520
521           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
522           ASSERT (b0->current_data == 0);
523           ASSERT (b1->current_data == 0);
524
525           ip0 = vlib_buffer_get_current (b0);
526           ip1 = vlib_buffer_get_current (b1);
527           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
528           adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
529           adj0 = ip_get_adjacency (lm, adj_index0);
530           adj1 = ip_get_adjacency (lm, adj_index1);
531
532           next0 = adj0->lookup_next_index;
533           next1 = adj1->lookup_next_index;
534
535           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
536           hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
537
538           ioam_pop_hop_by_hop_processing (vm, ip0, hbh0);
539           ioam_pop_hop_by_hop_processing (vm, ip1, hbh1);
540
541           vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
542           vlib_buffer_advance (b1, (hbh1->length + 1) << 3);
543
544           new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
545             ((hbh0->length + 1) << 3);
546           new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
547             ((hbh1->length + 1) << 3);
548
549           ip0->payload_length = clib_host_to_net_u16 (new_l0);
550           ip1->payload_length = clib_host_to_net_u16 (new_l1);
551
552           ip0->protocol = hbh0->protocol;
553           ip1->protocol = hbh1->protocol;
554
555           copy_src0 = (u64 *) ip0;
556           copy_src1 = (u64 *) ip1;
557           copy_dst0 = copy_src0 + (hbh0->length + 1);
558           copy_dst0[4] = copy_src0[4];
559           copy_dst0[3] = copy_src0[3];
560           copy_dst0[2] = copy_src0[2];
561           copy_dst0[1] = copy_src0[1];
562           copy_dst0[0] = copy_src0[0];
563           copy_dst1 = copy_src1 + (hbh1->length + 1);
564           copy_dst1[4] = copy_src1[4];
565           copy_dst1[3] = copy_src1[3];
566           copy_dst1[2] = copy_src1[2];
567           copy_dst1[1] = copy_src1[1];
568           copy_dst1[0] = copy_src1[0];
569           processed += 2;
570           /* $$$$$ End of processing 2 x packets $$$$$ */
571
572           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
573             {
574               if (b0->flags & VLIB_BUFFER_IS_TRACED)
575                 {
576                   ip6_pop_hop_by_hop_trace_t *t =
577                     vlib_add_trace (vm, node, b0, sizeof (*t));
578                   t->next_index = next0;
579                 }
580               if (b1->flags & VLIB_BUFFER_IS_TRACED)
581                 {
582                   ip6_pop_hop_by_hop_trace_t *t =
583                     vlib_add_trace (vm, node, b1, sizeof (*t));
584                   t->next_index = next1;
585                 }
586             }
587
588           /* verify speculative enqueues, maybe switch current next frame */
589           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
590                                            to_next, n_left_to_next,
591                                            bi0, bi1, next0, next1);
592         }
593
594       while (n_left_from > 0 && n_left_to_next > 0)
595         {
596           u32 bi0;
597           vlib_buffer_t *b0;
598           u32 next0;
599           u32 adj_index0;
600           ip6_header_t *ip0;
601           ip_adjacency_t *adj0;
602           ip6_hop_by_hop_header_t *hbh0;
603           u64 *copy_dst0, *copy_src0;
604           u16 new_l0;
605
606           /* speculatively enqueue b0 to the current next frame */
607           bi0 = from[0];
608           to_next[0] = bi0;
609           from += 1;
610           to_next += 1;
611           n_left_from -= 1;
612           n_left_to_next -= 1;
613
614           b0 = vlib_get_buffer (vm, bi0);
615
616           ip0 = vlib_buffer_get_current (b0);
617           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
618           adj0 = ip_get_adjacency (lm, adj_index0);
619
620           /* Default use the next_index from the adjacency. */
621           next0 = adj0->lookup_next_index;
622
623           /* Perfectly normal to end up here w/ out h-b-h header */
624           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
625
626           /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
627           ioam_pop_hop_by_hop_processing (vm, ip0, hbh0);
628           /* Pop the trace data */
629           vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
630           new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
631             ((hbh0->length + 1) << 3);
632           ip0->payload_length = clib_host_to_net_u16 (new_l0);
633           ip0->protocol = hbh0->protocol;
634           copy_src0 = (u64 *) ip0;
635           copy_dst0 = copy_src0 + (hbh0->length + 1);
636           copy_dst0[4] = copy_src0[4];
637           copy_dst0[3] = copy_src0[3];
638           copy_dst0[2] = copy_src0[2];
639           copy_dst0[1] = copy_src0[1];
640           copy_dst0[0] = copy_src0[0];
641           processed++;
642
643           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
644                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
645             {
646               ip6_pop_hop_by_hop_trace_t *t =
647                 vlib_add_trace (vm, node, b0, sizeof (*t));
648               t->next_index = next0;
649             }
650
651           /* verify speculative enqueue, maybe switch current next frame */
652           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
653                                            to_next, n_left_to_next,
654                                            bi0, next0);
655         }
656
657       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
658     }
659
660   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
661                                IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
662   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
663                                IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
664   return frame->n_vectors;
665 }
666
667 /* *INDENT-OFF* */
668 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) =
669 {
670   .function = ip6_pop_hop_by_hop_node_fn,.name =
671     "ip6-pop-hop-by-hop",.vector_size = sizeof (u32),.format_trace =
672     format_ip6_pop_hop_by_hop_trace,.type =
673     VLIB_NODE_TYPE_INTERNAL,.sibling_of = "ip6-lookup",.n_errors =
674     ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),.error_strings =
675     ip6_pop_hop_by_hop_error_strings,
676     /* See ip/lookup.h */
677 .n_next_nodes = 0,};
678
679 /* *INDENT-ON* */
680
681 VLIB_NODE_FUNCTION_MULTIARCH (ip6_pop_hop_by_hop_node,
682                               ip6_pop_hop_by_hop_node_fn)
683      static clib_error_t *ip6_hop_by_hop_ioam_init (vlib_main_t * vm)
684 {
685   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
686
687   hm->vlib_main = vm;
688   hm->vnet_main = vnet_get_main ();
689   hm->unix_time_0 = (u32) time (0);     /* Store starting time */
690   hm->vlib_time_0 = vlib_time_now (vm);
691   hm->ioam_flag = IOAM_HBYH_MOD;
692   memset (hm->add_options, 0, sizeof (hm->add_options));
693   memset (hm->pop_options, 0, sizeof (hm->pop_options));
694   memset (hm->options_size, 0, sizeof (hm->options_size));
695
696   return (0);
697 }
698
699 VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init);
700
701 int
702 ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option,
703                       int has_pot_option, int has_ppc_option)
704 {
705   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
706   u8 *rewrite = 0;
707   u32 size, rnd_size;
708   ip6_hop_by_hop_header_t *hbh;
709   u8 *current;
710   u8 trace_data_size = 0;
711   u8 pot_data_size = 0;
712
713   vec_free (*rwp);
714
715   if (has_trace_option == 0 && has_pot_option == 0)
716     return -1;
717
718   /* Work out how much space we need */
719   size = sizeof (ip6_hop_by_hop_header_t);
720
721   //if (has_trace_option && hm->get_sizeof_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
722   if (has_trace_option
723       && hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
724     {
725       size += sizeof (ip6_hop_by_hop_option_t);
726       size += hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
727     }
728   if (has_pot_option
729       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
730     {
731       size += sizeof (ip6_hop_by_hop_option_t);
732       size += hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
733     }
734
735   /* Round to a multiple of 8 octets */
736   rnd_size = (size + 7) & ~7;
737
738   /* allocate it, zero-fill / pad by construction */
739   vec_validate (rewrite, rnd_size - 1);
740
741   hbh = (ip6_hop_by_hop_header_t *) rewrite;
742   /* Length of header in 8 octet units, not incl first 8 octets */
743   hbh->length = (rnd_size >> 3) - 1;
744   current = (u8 *) (hbh + 1);
745
746   if (has_trace_option
747       && hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
748     {
749       if (0 != (hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST]))
750         {
751           trace_data_size =
752             hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
753           if (0 ==
754               hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (current,
755                                                                      &trace_data_size))
756             current += hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
757         }
758     }
759   if (has_pot_option
760       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
761     {
762       pot_data_size = hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
763       if (0 ==
764           hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (current,
765                                                                   &pot_data_size))
766         current +=
767           sizeof (hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT]);
768     }
769
770   *rwp = rewrite;
771   return 0;
772 }
773
774 clib_error_t *
775 clear_ioam_rewrite_fn (void)
776 {
777   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
778
779   vec_free (hm->rewrite);
780   hm->rewrite = 0;
781   hm->has_trace_option = 0;
782   hm->has_pot_option = 0;
783   hm->has_ppc_option = 0;
784
785   return 0;
786 }
787
788 clib_error_t *
789 clear_ioam_rewrite_command_fn (vlib_main_t * vm,
790                                unformat_input_t * input,
791                                vlib_cli_command_t * cmd)
792 {
793   return (clear_ioam_rewrite_fn ());
794 }
795
796 /* *INDENT-OFF* */
797 VLIB_CLI_COMMAND (ip6_clear_ioam_rewrite_cmd, static) =
798 {
799 .path = "clear ioam rewrite",.short_help = "clear ioam rewrite",.function =
800     clear_ioam_rewrite_command_fn,};
801 /* *INDENT-ON* */
802
803 clib_error_t *
804 ip6_ioam_enable (int has_trace_option, int has_pot_option, int has_ppc_option)
805 {
806   int rv;
807   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
808   rv = ip6_ioam_set_rewrite (&hm->rewrite, has_trace_option,
809                              has_pot_option, has_ppc_option);
810
811   switch (rv)
812     {
813     case 0:
814       hm->has_trace_option = has_trace_option;
815       hm->has_pot_option = has_pot_option;
816       hm->has_ppc_option = has_ppc_option;
817       break;
818
819     default:
820       return clib_error_return_code (0, rv, 0,
821                                      "ip6_ioam_set_rewrite returned %d", rv);
822     }
823
824   return 0;
825 }
826
827
828 static clib_error_t *
829 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
830                                  unformat_input_t * input,
831                                  vlib_cli_command_t * cmd)
832 {
833   int has_trace_option = 0;
834   int has_pot_option = 0;
835   int has_ppc_option = 0;
836   clib_error_t *rv = 0;
837
838   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
839     {
840       if (unformat (input, "trace"))
841         has_trace_option = 1;
842       else if (unformat (input, "pot"))
843         has_pot_option = 1;
844       else if (unformat (input, "ppc encap"))
845         has_ppc_option = PPC_ENCAP;
846       else if (unformat (input, "ppc decap"))
847         has_ppc_option = PPC_DECAP;
848       else if (unformat (input, "ppc none"))
849         has_ppc_option = PPC_NONE;
850       else
851         break;
852     }
853
854
855   rv = ip6_ioam_enable (has_trace_option, has_pot_option, has_ppc_option);
856
857   return rv;
858 }
859
860 /* *INDENT-OFF* */
861 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) =
862 {
863 .path = "set ioam rewrite",.short_help =
864     "set ioam [trace] [pot] [ppc <encap|decap>]",.function =
865     ip6_set_ioam_rewrite_command_fn,};
866 /* *INDENT-ON* */
867
868 static clib_error_t *
869 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
870                               unformat_input_t * input,
871                               vlib_cli_command_t * cmd)
872 {
873   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
874   u8 *s = 0;
875
876
877   if (!is_zero_ip6_address (&hm->adj))
878     {
879       s = format (s, "              REWRITE FLOW CONFIGS - \n");
880       s = format (s, "               Destination Address : %U\n",
881                   format_ip6_address, &hm->adj, sizeof (ip6_address_t));
882       s =
883         format (s, "                    Flow operation : %d (%s)\n",
884                 hm->ioam_flag,
885                 (hm->ioam_flag ==
886                  IOAM_HBYH_ADD) ? "Add" : ((hm->ioam_flag ==
887                                             IOAM_HBYH_MOD) ? "Mod" : "Pop"));
888     }
889   else
890     {
891       s = format (s, "              REWRITE FLOW CONFIGS - Not configured\n");
892     }
893
894
895   s = format (s, "                        TRACE OPTION - %d (%s)\n",
896               hm->has_trace_option,
897               (hm->has_trace_option ? "Enabled" : "Disabled"));
898   if (hm->has_trace_option)
899     s =
900       format (s,
901               "Try 'show ioam trace and show ioam-trace profile' for more information\n");
902
903
904   s = format (s, "                        POT OPTION - %d (%s)\n",
905               hm->has_pot_option,
906               (hm->has_pot_option ? "Enabled" : "Disabled"));
907   if (hm->has_pot_option)
908     s =
909       format (s,
910               "Try 'show ioam pot and show pot profile' for more information\n");
911
912   s = format (s, "         EDGE TO EDGE - PPC OPTION - %d (%s)\n",
913               hm->has_ppc_option, ppc_state[hm->has_ppc_option]);
914   if (hm->has_ppc_option)
915     s = format (s, "Try 'show ioam ppc' for more information\n");
916
917   vlib_cli_output (vm, "%v", s);
918   vec_free (s);
919   return 0;
920 }
921
922 /* *INDENT-OFF* */
923 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) =
924 {
925 .path = "show ioam summary",.short_help =
926     "Summary of IOAM configuration",.function =
927     ip6_show_ioam_summary_cmd_fn,};
928 /* *INDENT-ON* */
929
930 int
931 ip6_ioam_set_destination (ip6_address_t * addr, u32 mask_width, u32 vrf_id,
932                           int is_add, int is_pop, int is_none)
933 {
934   ip6_main_t *im = &ip6_main;
935   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
936   ip_lookup_main_t *lm = &im->lookup_main;
937   ip_adjacency_t *adj;
938   u32 fib_index;
939   u32 adj_index;
940
941   if ((is_add + is_pop + is_none) != 1)
942     return VNET_API_ERROR_INVALID_VALUE_2;
943
944   /* Go find the adjacency we're supposed to tickle */
945   fib_index = ip6_fib_index_from_table_id (vrf_id);
946
947   if (~0 == fib_index)
948     return VNET_API_ERROR_NO_SUCH_FIB;
949
950   adj_index = ip6_fib_table_fwding_lookup (im, fib_index, addr);
951
952   ASSERT (!"Not an ADJ");
953
954   /* Got it, modify as directed... */
955   adj = ip_get_adjacency (lm, adj_index);
956
957   /* Restore original lookup-next action */
958   if (adj->saved_lookup_next_index)
959     {
960       adj->lookup_next_index = adj->saved_lookup_next_index;
961       adj->saved_lookup_next_index = 0;
962     }
963
964   /* Save current action */
965   if (is_add || is_pop)
966     adj->saved_lookup_next_index = adj->lookup_next_index;
967
968   if (is_add)
969     adj->lookup_next_index =
970       (ip_lookup_next_t) IP6_LOOKUP_NEXT_ADD_HOP_BY_HOP;
971
972   if (is_pop)
973     adj->lookup_next_index =
974       (ip_lookup_next_t) IP6_LOOKUP_NEXT_POP_HOP_BY_HOP;
975
976   hm->adj = *addr;
977   hm->ioam_flag = (is_add ? IOAM_HBYH_ADD :
978                    (is_pop ? IOAM_HBYH_POP : IOAM_HBYH_MOD));
979   return 0;
980 }
981
982 static clib_error_t *
983 ip6_set_ioam_destination_command_fn (vlib_main_t * vm,
984                                      unformat_input_t * input,
985                                      vlib_cli_command_t * cmd)
986 {
987   ip6_address_t addr;
988   u32 mask_width = ~0;
989   int is_add = 0;
990   int is_pop = 0;
991   int is_none = 0;
992   u32 vrf_id = 0;
993   // int rv;
994
995   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
996     {
997       if (unformat (input, "%U/%d", unformat_ip6_address, &addr, &mask_width))
998         ;
999       else if (unformat (input, "vrf-id %d", &vrf_id))
1000         ;
1001       else if (unformat (input, "add"))
1002         is_add = 1;
1003       else if (unformat (input, "pop"))
1004         is_pop = 1;
1005       else if (unformat (input, "none"))
1006         is_none = 1;
1007       else
1008         break;
1009     }
1010
1011   if ((is_add + is_pop + is_none) != 1)
1012     return clib_error_return (0, "One of (add, pop, none) required");
1013   if (mask_width == ~0)
1014     return clib_error_return (0, "<address>/<mask-width> required");
1015
1016   /* rv = ip6_ioam_set_destination (&addr, mask_width, vrf_id, */
1017   /*                             is_add, is_pop, is_none); */
1018
1019   /* switch (rv) */
1020   /*   { */
1021   /*   case 0: */
1022   /*     break; */
1023   /*   default: */
1024   /*     return clib_error_return (0, "ip6_ioam_set_destination returned %d", */
1025   /*                            rv); */
1026   /*   } */
1027
1028   /* return 0; */
1029
1030   return clib_error_return (0,
1031                             "ip6_ioam_set_destination Currnetly Disabled due to FIB2.0",
1032                             1);
1033 }
1034
1035 /* *INDENT-OFF* */
1036 VLIB_CLI_COMMAND (ip6_set_ioam_destination_cmd, static) =
1037 {
1038 .path = "set ioam destination",.short_help =
1039     "set ioam destination <ip6-address>/<width> add | pop | none",.function
1040     = ip6_set_ioam_destination_command_fn,};
1041 /* *INDENT-ON* */
1042
1043
1044 void
1045 vnet_register_ioam_end_of_path_callback (void *cb)
1046 {
1047   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1048
1049   hm->ioam_end_of_path_cb = cb;
1050 }
1051
1052 /*
1053  * fd.io coding-style-patch-verification: ON
1054  *
1055  * Local Variables:
1056  * eval: (c-set-style "gnu")
1057  * End:
1058  */