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