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