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