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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <vlib/vlib.h>
16 #include <vnet/vnet.h>
17 #include <vnet/pg/pg.h>
18 #include <vppinfra/error.h>
20 #include <vnet/ip/ip.h>
22 #include <vppinfra/hash.h>
23 #include <vppinfra/error.h>
24 #include <vppinfra/elog.h>
26 #include <vnet/ip/ip6_hop_by_hop.h>
27 #include <vnet/lib-scv/scv_util.h>
29 /* Timestamp precision multipliers for seconds, milliseconds, microseconds
30 * and nanoseconds respectively.
32 static f64 trace_tsp_mul[4] = {1, 1e3, 1e6, 1e9};
34 char *ppc_state[] = {"None", "Encap", "Decap"};
36 ip6_hop_by_hop_main_t ip6_hop_by_hop_main;
40 #define foreach_ip6_hbyh_input_next \
41 _(IP6_REWRITE, "ip6-rewrite") \
42 _(IP6_LOOKUP, "ip6-lookup") \
43 _(IP6_HBYH, "ip6-hop-by-hop")\
44 _(IP6_POP_HBYH, "ip6-pop-hop-by-hop")\
48 #define _(s,n) IP6_HBYH_INPUT_NEXT_##s,
49 foreach_ip6_hbyh_input_next
51 IP6_HBYH_INPUT_N_NEXT,
52 } ip6_hbyh_input_next_t;
55 * ip6 hop-by-hop option handling. We push pkts with h-b-h options to
56 * ip6_hop_by_hop_node_fn from ip6-lookup at a cost of ~2 clocks/pkt in
59 * We parse through the h-b-h option TLVs, specifically looking for
60 * HBH_OPTION_TYPE_IOAM_DATA_LIST. [Someone needs to get bananas from
61 * IANA, aka to actually allocate the option TLV codes.]
63 * If we find the indicated option type, and we have remaining list
64 * elements in the trace list, allocate and populate the trace list
67 * At the ingress edge: punch in the h-b-h rewrite, then visit the
68 * standard h-b-h option handler. We have to be careful in the standard
69 * h-b-h handler, to avoid looping until we run out of rewrite space.
70 * Ask me how I know that.
73 * decide on egress point "pop and count" scheme
74 * time stamp handling: usec since the top of the hour?
75 * configure the node id
76 * trace list application data support
77 * cons up analysis / steering plug-in(s)
78 * add configuration binary APIs, vpp_api_test_support, yang models and
80 * perf tune: dual loop, replace memcpy w/ N x 8-byte load/stores
85 * primary h-b-h handler trace support
86 * We work pretty hard on the problem for obvious reasons
91 u32 timestamp_msbs; /* Store the top set of bits of timestamp */
93 } ip6_hop_by_hop_trace_t;
101 fetch_trace_data_size(u8 trace_type)
103 u8 trace_data_size = 0;
105 if (trace_type == TRACE_TYPE_IF_TS_APP)
106 trace_data_size = sizeof(ioam_trace_if_ts_app_t);
107 else if(trace_type == TRACE_TYPE_IF)
108 trace_data_size = sizeof(ioam_trace_if_t);
109 else if(trace_type == TRACE_TYPE_TS)
110 trace_data_size = sizeof(ioam_trace_ts_t);
111 else if(trace_type == TRACE_TYPE_APP)
112 trace_data_size = sizeof(ioam_trace_app_t);
113 else if(trace_type == TRACE_TYPE_TS_APP)
114 trace_data_size = sizeof(ioam_trace_ts_app_t);
116 return trace_data_size;
119 static u8 * format_ioam_data_list_element (u8 * s, va_list * args)
121 u32 *elt = va_arg (*args, u32 *);
122 u8 *trace_type_p = va_arg (*args, u8 *);
123 u8 trace_type = *trace_type_p;
126 if (trace_type & BIT_TTL_NODEID)
128 u32 ttl_node_id_host_byte_order = clib_net_to_host_u32 (*elt);
129 s = format (s, "ttl 0x%x node id 0x%x ",
130 ttl_node_id_host_byte_order>>24,
131 ttl_node_id_host_byte_order & 0x00FFFFFF);
136 if (trace_type & BIT_ING_INTERFACE && trace_type & BIT_ING_INTERFACE)
138 u32 ingress_host_byte_order = clib_net_to_host_u32(*elt);
139 s = format (s, "ingress 0x%x egress 0x%x ",
140 ingress_host_byte_order >> 16,
141 ingress_host_byte_order &0xFFFF);
145 if (trace_type & BIT_TIMESTAMP)
147 u32 ts_in_host_byte_order = clib_net_to_host_u32 (*elt);
148 s = format (s, "ts 0x%x \n", ts_in_host_byte_order);
152 if (trace_type & BIT_APPDATA)
154 u32 appdata_in_host_byte_order = clib_net_to_host_u32 (*elt);
155 s = format (s, "app 0x%x ", appdata_in_host_byte_order);
161 static u8 * format_ioam_pow (u8 * s, va_list * args)
163 ioam_pow_option_t * pow0 = va_arg (*args, ioam_pow_option_t *);
164 u64 random, cumulative;
165 random = cumulative = 0;
168 random = clib_net_to_host_u64 (pow0->random);
169 cumulative = clib_net_to_host_u64 (pow0->cumulative);
172 s = format (s, "random = 0x%Lx, Cumulative = 0x%Lx, Index = 0x%x",
173 random, cumulative, pow0->reserved_profile_id);
177 static u8 * format_ip6_hop_by_hop_trace (u8 * s, va_list * args)
179 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
180 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
181 ip6_hop_by_hop_trace_t * t = va_arg (*args, ip6_hop_by_hop_trace_t *);
182 ip6_hop_by_hop_header_t *hbh0;
183 ip6_hop_by_hop_option_t *opt0, *limit0;
184 ioam_trace_option_t * trace0;
185 u8 trace_data_size_in_words = 0;
189 ioam_pow_option_t * pow0;
191 hbh0 = (ip6_hop_by_hop_header_t *)t->option_data;
193 s = format (s, "IP6_HOP_BY_HOP: next index %d len %d traced %d\n",
194 t->next_index, (hbh0->length+1)<<3, t->trace_len);
196 opt0 = (ip6_hop_by_hop_option_t *) (hbh0+1);
197 limit0 = (ip6_hop_by_hop_option_t *) ((u8 *)hbh0) + t->trace_len;
199 while (opt0 < limit0)
201 type0 = opt0->type & HBH_OPTION_TYPE_MASK;
205 case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
206 trace0 = (ioam_trace_option_t *)opt0;
207 s = format (s, " Trace Type 0x%x , %d elts left ts msb(s) 0x%x\n",
208 trace0->ioam_trace_type, trace0->data_list_elts_left,
210 trace_data_size_in_words =
211 fetch_trace_data_size(trace0->ioam_trace_type)/4;
212 elt0 = &trace0->elts[0];
214 ((u8 *)(&trace0->elts[0]) + trace0->hdr.length - 2
215 /* -2 accounts for ioam_trace_type,elts_left */))
217 s = format (s, " [%d] %U\n",elt_index,
218 format_ioam_data_list_element,
219 elt0, &trace0->ioam_trace_type);
221 elt0 += trace_data_size_in_words;
224 opt0 = (ip6_hop_by_hop_option_t *)
225 (((u8 *)opt0) + opt0->length
226 + sizeof (ip6_hop_by_hop_option_t));
229 case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK:
230 s = format (s, " POW opt present\n");
231 pow0 = (ioam_pow_option_t *) opt0;
232 s = format (s, " %U\n", format_ioam_pow,pow0);
234 opt0 = (ip6_hop_by_hop_option_t *)
235 (((u8 *)opt0) + sizeof (ioam_pow_option_t));
238 case 0: /* Pad, just stop */
239 opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1;
243 s = format (s, "Unknown %d", type0);
244 opt0 = (ip6_hop_by_hop_option_t *)
245 (((u8 *)opt0) + opt0->length
246 + sizeof (ip6_hop_by_hop_option_t));
253 vlib_node_registration_t ip6_hop_by_hop_node;
255 #define foreach_ip6_hop_by_hop_error \
256 _(PROCESSED, "Pkts with ip6 hop-by-hop options") \
257 _(PROFILE_MISS, "Pkts with ip6 hop-by-hop options but no profile set") \
258 _(UNKNOWN_OPTION, "Unknown ip6 hop-by-hop options")
261 #define _(sym,str) IP6_HOP_BY_HOP_ERROR_##sym,
262 foreach_ip6_hop_by_hop_error
264 IP6_HOP_BY_HOP_N_ERROR,
265 } ip6_hop_by_hop_error_t;
267 static char * ip6_hop_by_hop_error_strings[] = {
268 #define _(sym,string) string,
269 foreach_ip6_hop_by_hop_error
274 ip6_hop_by_hop_node_fn (vlib_main_t * vm,
275 vlib_node_runtime_t * node,
276 vlib_frame_t * frame)
278 ip6_main_t * im = &ip6_main;
279 ip_lookup_main_t * lm = &im->lookup_main;
280 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
281 u32 n_left_from, * from, * to_next;
282 ip_lookup_next_t next_index;
283 u32 processed = 0, unknown_opts = 0;
288 from = vlib_frame_vector_args (frame);
289 n_left_from = frame->n_vectors;
290 next_index = node->cached_next_index;
292 while (n_left_from > 0)
296 vlib_get_next_frame (vm, node, next_index,
297 to_next, n_left_to_next);
299 #if 0 /* $$$ DUAL-LOOP ME */
300 while (n_left_from >= 4 && n_left_to_next >= 2)
302 u32 next0 = IP6_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
303 u32 next1 = IP6_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
304 u32 sw_if_index0, sw_if_index1;
306 ethernet_header_t *en0, *en1;
308 vlib_buffer_t * b0, * b1;
310 /* Prefetch next iteration. */
312 vlib_buffer_t * p2, * p3;
314 p2 = vlib_get_buffer (vm, from[2]);
315 p3 = vlib_get_buffer (vm, from[3]);
317 vlib_prefetch_buffer_header (p2, LOAD);
318 vlib_prefetch_buffer_header (p3, LOAD);
320 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
321 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
324 /* speculatively enqueue b0 and b1 to the current next frame */
325 to_next[0] = bi0 = from[0];
326 to_next[1] = bi1 = from[1];
332 b0 = vlib_get_buffer (vm, bi0);
333 b1 = vlib_get_buffer (vm, bi1);
335 /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
336 ASSERT (b0->current_data == 0);
337 ASSERT (b1->current_data == 0);
339 ip0 = vlib_buffer_get_current (b0);
340 ip1 = vlib_buffer_get_current (b0);
342 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
343 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
345 /* $$$$$ End of processing 2 x packets $$$$$ */
347 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
349 if (b0->flags & VLIB_BUFFER_IS_TRACED)
351 ip6_hop_by_hop_trace_t *t =
352 vlib_add_trace (vm, node, b0, sizeof (*t));
353 t->sw_if_index = sw_if_index0;
354 t->next_index = next0;
356 if (b1->flags & VLIB_BUFFER_IS_TRACED)
358 ip6_hop_by_hop_trace_t *t =
359 vlib_add_trace (vm, node, b1, sizeof (*t));
360 t->sw_if_index = sw_if_index1;
361 t->next_index = next1;
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);
372 while (n_left_from > 0 && n_left_to_next > 0)
379 ip_adjacency_t * adj0;
380 ip6_hop_by_hop_header_t *hbh0;
381 ip6_hop_by_hop_option_t *opt0, *limit0;
382 ioam_trace_option_t * trace0;
383 ioam_pow_option_t * pow0;
386 u64 random = 0, cumulative = 0;
389 /* speculatively enqueue b0 to the current next frame */
397 b0 = vlib_get_buffer (vm, bi0);
399 ip0 = vlib_buffer_get_current (b0);
400 adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
401 adj0 = ip_get_adjacency (lm, adj_index0);
402 hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1);
403 opt0 = (ip6_hop_by_hop_option_t *)(hbh0+1);
404 limit0 = (ip6_hop_by_hop_option_t *)
405 ((u8 *)hbh0 + ((hbh0->length+1)<<3));
407 /* Scan the set of h-b-h options, process ones that we understand */
408 while (opt0 < limit0)
410 type0 = opt0->type & HBH_OPTION_TYPE_MASK;
413 case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
414 trace0 = (ioam_trace_option_t *)opt0;
415 if (PREDICT_TRUE (trace0->data_list_elts_left))
417 trace0->data_list_elts_left--;
418 /* fetch_trace_data_size returns in bytes. Convert it to 4-bytes
419 * to skip to this node's location.
421 elt_index = trace0->data_list_elts_left *
422 fetch_trace_data_size(trace0->ioam_trace_type)/4;
423 elt0 = &trace0->elts[elt_index];
424 if (trace0->ioam_trace_type & BIT_TTL_NODEID)
427 clib_host_to_net_u32 ((ip0->hop_limit<<24)
432 if (trace0->ioam_trace_type & BIT_ING_INTERFACE)
435 (vnet_buffer(b0)->sw_if_index[VLIB_RX]&0xFFFF) << 16 | (adj0->rewrite_header.sw_if_index & 0xFFFF);
436 *elt0 = clib_host_to_net_u32(*elt0);
440 if (trace0->ioam_trace_type & BIT_TIMESTAMP)
442 /* Send least significant 32 bits */
443 f64 time_f64 = (f64)(((f64)hm->unix_time_0) +
444 (vlib_time_now(hm->vlib_main) - hm->vlib_time_0));
447 time_f64 * trace_tsp_mul[hm->trace_tsp];
448 *elt0 = clib_host_to_net_u32(time_u64.as_u32[0]);
452 if (trace0->ioam_trace_type & BIT_APPDATA)
454 /* $$$ set elt0->app_data */
455 *elt0 = clib_host_to_net_u32(hm->app_data);
460 opt0 = (ip6_hop_by_hop_option_t *)
461 (((u8 *)opt0) + opt0->length
462 + sizeof (ip6_hop_by_hop_option_t));
465 case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK:
466 pow_profile = scv_profile_find(pow_profile_index);
467 if (PREDICT_FALSE(!pow_profile))
469 vlib_node_increment_counter (vm,
470 ip6_hop_by_hop_node.index,
471 IP6_HOP_BY_HOP_ERROR_PROFILE_MISS, 1);
473 opt0 = (ip6_hop_by_hop_option_t *)
474 (((u8 *)opt0) + sizeof (ioam_pow_option_t));
477 pow0 = (ioam_pow_option_t *) opt0;
478 pow_encap = (pow0->random == 0);
481 if (PREDICT_FALSE(total_pkts_using_this_profile >=
482 pow_profile->validity))
484 /* Choose a new profile */
485 u16 new_profile_index;
487 scv_get_next_profile_id(vm,
489 if (new_profile_index != pow_profile_index)
491 /* Got a new profile */
492 scv_profile_invalidate(vm, hm,
495 pow_profile_index = new_profile_index;
497 scv_profile_find(pow_profile_index);
498 total_pkts_using_this_profile = 0;
502 scv_profile_invalidate(vm, hm,
507 pow0->reserved_profile_id =
508 pow_profile_index & PROFILE_ID_MASK;
509 total_pkts_using_this_profile++;
512 { /* Non encap node */
513 if (PREDICT_FALSE(pow0->reserved_profile_id !=
516 /* New profile announced by encap node. */
517 scv_profile *new_profile = 0;
519 scv_profile_find(pow0->reserved_profile_id);
520 if (PREDICT_FALSE(new_profile == 0 ||
521 new_profile->validity == 0))
523 /* Profile is invalid. Use old profile*/
524 vlib_node_increment_counter (vm,
525 ip6_hop_by_hop_node.index,
526 IP6_HOP_BY_HOP_ERROR_PROFILE_MISS, 1);
527 scv_profile_invalidate(vm, hm,
528 pow0->reserved_profile_id,
533 scv_profile_invalidate(vm, hm,
536 pow_profile_index = pow0->reserved_profile_id;
537 pow_profile = new_profile;
538 total_pkts_using_this_profile = 0;
541 total_pkts_using_this_profile++;
544 if (pow0->random == 0)
546 pow0->random = clib_host_to_net_u64(
547 scv_generate_random(pow_profile));
548 pow0->cumulative = 0;
550 random = clib_net_to_host_u64(pow0->random);
551 cumulative = clib_net_to_host_u64(pow0->cumulative);
552 pow0->cumulative = clib_host_to_net_u64(
553 scv_update_cumulative(pow_profile,
556 opt0 = (ip6_hop_by_hop_option_t *)
557 (((u8 *)opt0) + sizeof (ioam_pow_option_t));
561 opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1;
565 opt0 = (ip6_hop_by_hop_option_t *)
566 (((u8 *)opt0) + opt0->length
567 + sizeof (ip6_hop_by_hop_option_t));
574 next0 = (vnet_buffer(b0)->l2_classify.opaque_index == OI_DECAP) ?
575 IP6_HBYH_INPUT_NEXT_IP6_POP_HBYH : IP6_HBYH_INPUT_NEXT_IP6_REWRITE;
576 vnet_buffer(b0)->l2_classify.opaque_index = ~0;
578 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
579 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
581 ip6_hop_by_hop_trace_t *t =
582 vlib_add_trace (vm, node, b0, sizeof (*t));
583 u32 trace_len = (hbh0->length+1)<<3;
584 t->next_index = next0;
585 /* Capture the h-b-h option verbatim */
586 trace_len = trace_len < ARRAY_LEN(t->option_data) ?
587 trace_len : ARRAY_LEN(t->option_data);
588 t->trace_len = trace_len;
589 clib_memcpy (t->option_data, hbh0, trace_len);
594 /* verify speculative enqueue, maybe switch current next frame */
595 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
596 to_next, n_left_to_next,
600 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
603 if (PREDICT_FALSE(unknown_opts > 0)) {
604 vlib_node_increment_counter (vm, ip6_hop_by_hop_node.index,
605 IP6_HOP_BY_HOP_ERROR_UNKNOWN_OPTION, unknown_opts);
608 vlib_node_increment_counter (vm, ip6_hop_by_hop_node.index,
609 IP6_HOP_BY_HOP_ERROR_PROCESSED, processed);
610 return frame->n_vectors;
613 VLIB_REGISTER_NODE (ip6_hop_by_hop_node) = {
614 .function = ip6_hop_by_hop_node_fn,
615 .name = "ip6-hop-by-hop",
616 .vector_size = sizeof (u32),
617 .format_trace = format_ip6_hop_by_hop_trace,
618 .type = VLIB_NODE_TYPE_INTERNAL,
620 .n_errors = ARRAY_LEN(ip6_hop_by_hop_error_strings),
621 .error_strings = ip6_hop_by_hop_error_strings,
623 .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
625 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
626 foreach_ip6_hbyh_input_next
631 /* The main h-b-h tracer will be invoked, no need to do much here */
634 } ip6_add_hop_by_hop_trace_t;
636 /* packet trace format function */
637 static u8 * format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
639 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
640 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
641 ip6_add_hop_by_hop_trace_t * t = va_arg (*args,
642 ip6_add_hop_by_hop_trace_t *);
644 s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d",
649 vlib_node_registration_t ip6_add_hop_by_hop_node;
651 #define foreach_ip6_add_hop_by_hop_error \
652 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
655 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
656 foreach_ip6_add_hop_by_hop_error
658 IP6_ADD_HOP_BY_HOP_N_ERROR,
659 } ip6_add_hop_by_hop_error_t;
661 static char * ip6_add_hop_by_hop_error_strings[] = {
662 #define _(sym,string) string,
663 foreach_ip6_add_hop_by_hop_error
668 ip6_add_hop_by_hop_node_fn (vlib_main_t * vm,
669 vlib_node_runtime_t * node,
670 vlib_frame_t * frame)
672 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
673 u32 n_left_from, * from, * to_next;
674 ip_lookup_next_t next_index;
676 u8 * rewrite = hm->rewrite;
677 u32 rewrite_length = vec_len (rewrite);
679 from = vlib_frame_vector_args (frame);
680 n_left_from = frame->n_vectors;
681 next_index = node->cached_next_index;
683 while (n_left_from > 0)
687 vlib_get_next_frame (vm, node, next_index,
688 to_next, n_left_to_next);
691 while (n_left_from >= 4 && n_left_to_next >= 2)
693 u32 next0 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
694 u32 next1 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
695 u32 sw_if_index0, sw_if_index1;
697 ethernet_header_t *en0, *en1;
699 vlib_buffer_t * b0, * b1;
701 /* Prefetch next iteration. */
703 vlib_buffer_t * p2, * p3;
705 p2 = vlib_get_buffer (vm, from[2]);
706 p3 = vlib_get_buffer (vm, from[3]);
708 vlib_prefetch_buffer_header (p2, LOAD);
709 vlib_prefetch_buffer_header (p3, LOAD);
711 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
712 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
715 /* speculatively enqueue b0 and b1 to the current next frame */
716 to_next[0] = bi0 = from[0];
717 to_next[1] = bi1 = from[1];
723 b0 = vlib_get_buffer (vm, bi0);
724 b1 = vlib_get_buffer (vm, bi1);
726 /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
727 ASSERT (b0->current_data == 0);
728 ASSERT (b1->current_data == 0);
730 ip0 = vlib_buffer_get_current (b0);
731 ip1 = vlib_buffer_get_current (b0);
733 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
734 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
736 /* $$$$$ End of processing 2 x packets $$$$$ */
738 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
740 if (b0->flags & VLIB_BUFFER_IS_TRACED)
742 ip6_add_hop_by_hop_trace_t *t =
743 vlib_add_trace (vm, node, b0, sizeof (*t));
744 t->sw_if_index = sw_if_index0;
745 t->next_index = next0;
747 if (b1->flags & VLIB_BUFFER_IS_TRACED)
749 ip6_add_hop_by_hop_trace_t *t =
750 vlib_add_trace (vm, node, b1, sizeof (*t));
751 t->sw_if_index = sw_if_index1;
752 t->next_index = next1;
756 /* verify speculative enqueues, maybe switch current next frame */
757 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
758 to_next, n_left_to_next,
759 bi0, bi1, next0, next1);
763 while (n_left_from > 0 && n_left_to_next > 0)
769 ip6_hop_by_hop_header_t * hbh0;
770 u64 * copy_src0, * copy_dst0;
773 /* speculatively enqueue b0 to the current next frame */
781 b0 = vlib_get_buffer (vm, bi0);
783 ip0 = vlib_buffer_get_current (b0);
785 /* Copy the ip header left by the required amount */
786 copy_dst0 = (u64 *)(((u8 *)ip0) - rewrite_length);
787 copy_src0 = (u64 *) ip0;
789 copy_dst0 [0] = copy_src0 [0];
790 copy_dst0 [1] = copy_src0 [1];
791 copy_dst0 [2] = copy_src0 [2];
792 copy_dst0 [3] = copy_src0 [3];
793 copy_dst0 [4] = copy_src0 [4];
794 vlib_buffer_advance (b0, - (word)rewrite_length);
795 ip0 = vlib_buffer_get_current (b0);
797 hbh0 = (ip6_hop_by_hop_header_t *)(ip0 + 1);
798 /* $$$ tune, rewrite_length is a multiple of 8 */
799 clib_memcpy (hbh0, rewrite, rewrite_length);
800 /* Patch the protocol chain, insert the h-b-h (type 0) header */
801 hbh0->protocol = ip0->protocol;
803 new_l0 = clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
804 ip0->payload_length = clib_host_to_net_u16 (new_l0);
806 /* Populate the (first) h-b-h list elt */
807 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
809 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
810 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
812 ip6_add_hop_by_hop_trace_t *t =
813 vlib_add_trace (vm, node, b0, sizeof (*t));
814 t->next_index = next0;
819 /* verify speculative enqueue, maybe switch current next frame */
820 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
821 to_next, n_left_to_next,
825 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
828 vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
829 IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
830 return frame->n_vectors;
833 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = {
834 .function = ip6_add_hop_by_hop_node_fn,
835 .name = "ip6-add-hop-by-hop",
836 .vector_size = sizeof (u32),
837 .format_trace = format_ip6_add_hop_by_hop_trace,
838 .type = VLIB_NODE_TYPE_INTERNAL,
840 .n_errors = ARRAY_LEN(ip6_add_hop_by_hop_error_strings),
841 .error_strings = ip6_add_hop_by_hop_error_strings,
843 /* See ip/lookup.h */
844 .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
846 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
847 foreach_ip6_hbyh_input_next
853 /* The main h-b-h tracer was already invoked, no need to do much here */
856 } ip6_pop_hop_by_hop_trace_t;
858 /* packet trace format function */
859 static u8 * format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
861 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
862 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
863 ip6_pop_hop_by_hop_trace_t * t = va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
865 s = format (s, "IP6_POP_HOP_BY_HOP: next index %d",
870 vlib_node_registration_t ip6_pop_hop_by_hop_node;
872 #define foreach_ip6_pop_hop_by_hop_error \
873 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \
874 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
875 _(SCV_PASSED, "Pkts with SCV in Policy") \
876 _(SCV_FAILED, "Pkts with SCV out of Policy")
879 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
880 foreach_ip6_pop_hop_by_hop_error
882 IP6_POP_HOP_BY_HOP_N_ERROR,
883 } ip6_pop_hop_by_hop_error_t;
885 static char * ip6_pop_hop_by_hop_error_strings[] = {
886 #define _(sym,string) string,
887 foreach_ip6_pop_hop_by_hop_error
891 static inline void ioam_end_of_path_validation (vlib_main_t * vm,
893 ip6_hop_by_hop_header_t *hbh0)
895 ip6_hop_by_hop_option_t *opt0, *limit0;
896 ioam_pow_option_t * pow0;
898 u64 final_cumulative = 0;
902 if (!hbh0 || !ip0) return;
904 opt0 = (ip6_hop_by_hop_option_t *)(hbh0+1);
905 limit0 = (ip6_hop_by_hop_option_t *)
906 ((u8 *)hbh0 + ((hbh0->length+1)<<3));
908 /* Scan the set of h-b-h options, process ones that we understand */
909 while (opt0 < limit0)
911 type0 = opt0->type & HBH_OPTION_TYPE_MASK;
914 case HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE:
915 case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
916 opt0 = (ip6_hop_by_hop_option_t *)
917 (((u8 *)opt0) + opt0->length
918 + sizeof (ip6_hop_by_hop_option_t));
920 case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK:
921 pow0 = (ioam_pow_option_t *) opt0;
922 random = clib_net_to_host_u64(pow0->random);
923 final_cumulative = clib_net_to_host_u64(pow0->cumulative);
924 result = scv_validate (pow_profile,
925 final_cumulative, random);
929 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
930 IP6_POP_HOP_BY_HOP_ERROR_SCV_PASSED, result);
934 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
935 IP6_POP_HOP_BY_HOP_ERROR_SCV_FAILED, 1);
937 /* TODO: notify the scv failure*/
938 opt0 = (ip6_hop_by_hop_option_t *)
939 (((u8 *)opt0) + sizeof (ioam_pow_option_t));
943 opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1;
947 format(0, "Something is wrong\n");
955 ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm,
956 vlib_node_runtime_t * node,
957 vlib_frame_t * frame)
959 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
960 ip6_main_t * im = &ip6_main;
961 ip_lookup_main_t * lm = &im->lookup_main;
962 u32 n_left_from, * from, * to_next;
963 ip_lookup_next_t next_index;
966 u32 (*ioam_end_of_path_cb) (vlib_main_t *, vlib_node_runtime_t *,
967 vlib_buffer_t *, ip6_header_t *,
970 ioam_end_of_path_cb = hm->ioam_end_of_path_cb;
972 from = vlib_frame_vector_args (frame);
973 n_left_from = frame->n_vectors;
974 next_index = node->cached_next_index;
976 while (n_left_from > 0)
980 vlib_get_next_frame (vm, node, next_index,
981 to_next, n_left_to_next);
984 while (n_left_from >= 4 && n_left_to_next >= 2)
986 u32 next0 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
987 u32 next1 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
988 u32 sw_if_index0, sw_if_index1;
990 ethernet_header_t *en0, *en1;
992 vlib_buffer_t * b0, * b1;
994 /* Prefetch next iteration. */
996 vlib_buffer_t * p2, * p3;
998 p2 = vlib_get_buffer (vm, from[2]);
999 p3 = vlib_get_buffer (vm, from[3]);
1001 vlib_prefetch_buffer_header (p2, LOAD);
1002 vlib_prefetch_buffer_header (p3, LOAD);
1004 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
1005 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
1008 /* speculatively enqueue b0 and b1 to the current next frame */
1009 to_next[0] = bi0 = from[0];
1010 to_next[1] = bi1 = from[1];
1014 n_left_to_next -= 2;
1016 b0 = vlib_get_buffer (vm, bi0);
1017 b1 = vlib_get_buffer (vm, bi1);
1019 /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
1020 ASSERT (b0->current_data == 0);
1021 ASSERT (b1->current_data == 0);
1023 ip0 = vlib_buffer_get_current (b0);
1024 ip1 = vlib_buffer_get_current (b0);
1026 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1027 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
1029 /* $$$$$ End of processing 2 x packets $$$$$ */
1031 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
1033 if (b0->flags & VLIB_BUFFER_IS_TRACED)
1035 ip6_pop_hop_by_hop_trace_t *t =
1036 vlib_add_trace (vm, node, b0, sizeof (*t));
1037 t->sw_if_index = sw_if_index0;
1038 t->next_index = next0;
1040 if (b1->flags & VLIB_BUFFER_IS_TRACED)
1042 ip6_pop_hop_by_hop_trace_t *t =
1043 vlib_add_trace (vm, node, b1, sizeof (*t));
1044 t->sw_if_index = sw_if_index1;
1045 t->next_index = next1;
1049 /* verify speculative enqueues, maybe switch current next frame */
1050 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1051 to_next, n_left_to_next,
1052 bi0, bi1, next0, next1);
1056 while (n_left_from > 0 && n_left_to_next > 0)
1063 ip_adjacency_t * adj0;
1064 ip6_hop_by_hop_header_t *hbh0;
1065 u64 * copy_dst0, * copy_src0;
1068 /* speculatively enqueue b0 to the current next frame */
1074 n_left_to_next -= 1;
1076 b0 = vlib_get_buffer (vm, bi0);
1078 ip0 = vlib_buffer_get_current (b0);
1079 adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
1080 adj0 = ip_get_adjacency (lm, adj_index0);
1082 /* Perfectly normal to end up here w/ out h-b-h header */
1083 if (PREDICT_TRUE (ip0->protocol == 0))
1085 hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1);
1087 if (vnet_buffer(b0)->l2_classify.opaque_index == OI_DECAP)
1088 { /* First pass. Send to hbyh node. */
1089 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1094 /* Collect data from trace via callback */
1095 next0 = ioam_end_of_path_cb ?
1096 ioam_end_of_path_cb (vm, node, b0, ip0, adj0) :
1097 IP6_HBYH_INPUT_NEXT_IP6_REWRITE;
1098 /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
1099 ioam_end_of_path_validation(vm, ip0, hbh0);
1100 /* Pop the trace data */
1101 vlib_buffer_advance (b0, (hbh0->length+1)<<3);
1102 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
1103 ((hbh0->length+1)<<3);
1104 ip0->payload_length = clib_host_to_net_u16 (new_l0);
1105 ip0->protocol = hbh0->protocol;
1106 copy_src0 = (u64 *)ip0;
1107 copy_dst0 = copy_src0 + (hbh0->length+1);
1108 copy_dst0 [4] = copy_src0[4];
1109 copy_dst0 [3] = copy_src0[3];
1110 copy_dst0 [2] = copy_src0[2];
1111 copy_dst0 [1] = copy_src0[1];
1112 copy_dst0 [0] = copy_src0[0];
1117 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1121 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1122 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1124 ip6_pop_hop_by_hop_trace_t *t =
1125 vlib_add_trace (vm, node, b0, sizeof (*t));
1126 t->next_index = next0;
1130 /* verify speculative enqueue, maybe switch current next frame */
1131 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1132 to_next, n_left_to_next,
1136 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1139 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
1140 IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
1141 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
1142 IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
1143 return frame->n_vectors;
1146 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) = {
1147 .function = ip6_pop_hop_by_hop_node_fn,
1148 .name = "ip6-pop-hop-by-hop",
1149 .vector_size = sizeof (u32),
1150 .format_trace = format_ip6_pop_hop_by_hop_trace,
1151 .type = VLIB_NODE_TYPE_INTERNAL,
1153 .n_errors = ARRAY_LEN(ip6_pop_hop_by_hop_error_strings),
1154 .error_strings = ip6_pop_hop_by_hop_error_strings,
1156 /* See ip/lookup.h */
1157 .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
1159 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
1160 foreach_ip6_hbyh_input_next
1166 static clib_error_t *
1167 ip6_hop_by_hop_init (vlib_main_t * vm)
1169 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1172 hm->vnet_main = vnet_get_main();
1173 hm->unix_time_0 = (u32) time (0); /* Store starting time */
1174 hm->vlib_time_0 = vlib_time_now (vm);
1175 hm->ioam_flag = IOAM_HBYH_MOD;
1176 hm->trace_tsp = TSP_MICROSECONDS; /* Micro seconds */
1181 VLIB_INIT_FUNCTION (ip6_hop_by_hop_init);
1183 int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts,
1184 int has_pow_option, int has_ppc_option)
1188 ip6_hop_by_hop_header_t *hbh;
1189 ioam_trace_option_t * trace_option;
1190 ioam_pow_option_t * pow_option;
1192 u8 trace_data_size = 0;
1196 if (trace_option_elts == 0 && has_pow_option == 0)
1199 /* Work out how much space we need */
1200 size = sizeof (ip6_hop_by_hop_header_t);
1202 if (trace_option_elts)
1204 size += sizeof (ip6_hop_by_hop_option_t);
1206 trace_data_size = fetch_trace_data_size(trace_type);
1207 if (trace_data_size == 0)
1208 return VNET_API_ERROR_INVALID_VALUE;
1210 if (trace_option_elts * trace_data_size > 254)
1211 return VNET_API_ERROR_INVALID_VALUE;
1213 size += trace_option_elts * trace_data_size;
1217 size += sizeof (ip6_hop_by_hop_option_t);
1218 size += sizeof (ioam_pow_option_t);
1221 /* Round to a multiple of 8 octets */
1222 rnd_size = (size + 7) & ~7;
1224 /* allocate it, zero-fill / pad by construction */
1225 vec_validate (rewrite, rnd_size-1);
1227 hbh = (ip6_hop_by_hop_header_t *) rewrite;
1228 /* Length of header in 8 octet units, not incl first 8 octets */
1229 hbh->length = (rnd_size>>3) - 1;
1230 current = (u8 *)(hbh+1);
1232 if (trace_option_elts)
1234 trace_option = (ioam_trace_option_t *)current;
1235 trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
1236 | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1237 trace_option->hdr.length =
1238 2 /*ioam_trace_type,data_list_elts_left */ +
1239 trace_option_elts * trace_data_size;
1240 trace_option->ioam_trace_type = trace_type & TRACE_TYPE_MASK;
1241 trace_option->data_list_elts_left = trace_option_elts;
1242 current += sizeof (ioam_trace_option_t) +
1243 trace_option_elts * trace_data_size;
1247 pow_option = (ioam_pow_option_t *)current;
1248 pow_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK
1249 | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1250 pow_option->hdr.length = sizeof (ioam_pow_option_t) -
1251 sizeof (ip6_hop_by_hop_option_t);
1252 current += sizeof (ioam_pow_option_t);
1260 clear_ioam_rewrite_fn(void)
1262 ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1264 vec_free(hm->rewrite);
1269 hm->trace_option_elts = 0;
1270 hm->has_pow_option = 0;
1271 hm->has_ppc_option = 0;
1272 hm->trace_tsp = TSP_MICROSECONDS;
1277 clib_error_t * clear_ioam_rewrite_command_fn (vlib_main_t * vm,
1278 unformat_input_t * input,
1279 vlib_cli_command_t * cmd)
1281 return(clear_ioam_rewrite_fn());
1284 VLIB_CLI_COMMAND (ip6_clear_ioam_trace_cmd, static) = {
1285 .path = "clear ioam rewrite",
1286 .short_help = "clear ioam rewrite",
1287 .function = clear_ioam_rewrite_command_fn,
1291 ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id,
1292 u32 app_data, int has_pow_option, u32 trace_tsp,
1296 ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1297 rv = ip6_ioam_set_rewrite (&hm->rewrite, trace_type, trace_option_elts,
1298 has_pow_option, has_ppc_option);
1303 hm->node_id = node_id;
1304 hm->app_data = app_data;
1305 hm->trace_type = trace_type;
1306 hm->trace_option_elts = trace_option_elts;
1307 hm->has_pow_option = has_pow_option;
1308 hm->has_ppc_option = has_ppc_option;
1309 hm->trace_tsp = trace_tsp;
1313 return clib_error_return_code(0, rv, 0, "ip6_ioam_set_rewrite returned %d", rv);
1320 static clib_error_t *
1321 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
1322 unformat_input_t * input,
1323 vlib_cli_command_t * cmd)
1325 u32 trace_option_elts = 0;
1326 u32 trace_type = 0, node_id = 0;
1327 u32 app_data = 0, trace_tsp = TSP_MICROSECONDS;
1328 int has_pow_option = 0;
1329 int has_ppc_option = 0;
1330 clib_error_t * rv = 0;
1332 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1334 if (unformat (input, "trace-type 0x%x trace-elts %d "
1335 "trace-tsp %d node-id 0x%x app-data 0x%x",
1336 &trace_type, &trace_option_elts, &trace_tsp,
1337 &node_id, &app_data))
1339 else if (unformat (input, "pow"))
1341 else if (unformat (input, "ppc encap"))
1342 has_ppc_option = PPC_ENCAP;
1343 else if (unformat (input, "ppc decap"))
1344 has_ppc_option = PPC_DECAP;
1345 else if (unformat (input, "ppc none"))
1346 has_ppc_option = PPC_NONE;
1352 rv = ip6_ioam_trace_profile_set(trace_option_elts, trace_type, node_id,
1353 app_data, has_pow_option, trace_tsp, has_ppc_option);
1359 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1360 .path = "set ioam rewrite",
1361 .short_help = "set ioam rewrite trace-type <0x1f|0x3|0x9|0x11|0x19> trace-elts <nn> trace-tsp <0|1|2|3> node-id <node id in hex> app-data <app_data in hex> [pow] [ppc <encap|decap>]",
1362 .function = ip6_set_ioam_rewrite_command_fn,
1365 static clib_error_t *
1366 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1367 unformat_input_t * input,
1368 vlib_cli_command_t * cmd)
1370 ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1374 if (!is_zero_ip6_address(&hm->adj))
1376 s = format(s, " REWRITE FLOW CONFIGS - \n");
1377 s = format(s, " Destination Address : %U\n",
1378 format_ip6_address, &hm->adj, sizeof(ip6_address_t));
1379 s = format(s, " Flow operation : %d (%s)\n", hm->ioam_flag,
1380 (hm->ioam_flag == IOAM_HBYH_ADD) ? "Add" :
1381 ((hm->ioam_flag == IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1385 s = format(s, " REWRITE FLOW CONFIGS - Not configured\n");
1388 if (hm->trace_option_elts)
1390 s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - \n");
1391 s = format(s, " Trace Type : 0x%x (%d)\n",
1392 hm->trace_type, hm->trace_type);
1393 s = format(s, " Trace timestamp precision : %d (%s)\n", hm->trace_tsp,
1394 (hm->trace_tsp == TSP_SECONDS) ? "Seconds" :
1395 ((hm->trace_tsp == TSP_MILLISECONDS) ? "Milliseconds" :
1396 (((hm->trace_tsp == TSP_MICROSECONDS) ? "Microseconds" : "Nanoseconds"))));
1397 s = format(s, " Num of trace nodes : %d\n",
1398 hm->trace_option_elts);
1399 s = format(s, " Node-id : 0x%x (%d)\n",
1400 hm->node_id, hm->node_id);
1401 s = format(s, " App Data : 0x%x (%d)\n",
1402 hm->app_data, hm->app_data);
1406 s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n");
1409 s = format(s, " POW OPTION - %d (%s)\n",
1410 hm->has_pow_option, (hm->has_pow_option?"Enabled":"Disabled"));
1411 if (hm->has_pow_option)
1412 s = format(s, "Try 'show ioam sc-profile' for more information\n");
1414 s = format(s, " EDGE TO EDGE - PPC OPTION - %d (%s)\n",
1415 hm->has_ppc_option, ppc_state[hm->has_ppc_option]);
1416 if (hm->has_ppc_option)
1417 s = format(s, "Try 'show ioam ppc' for more information\n");
1419 vlib_cli_output(vm, "%v", s);
1424 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1425 .path = "show ioam summary",
1426 .short_help = "Summary of IOAM configuration",
1427 .function = ip6_show_ioam_summary_cmd_fn,
1430 int ip6_ioam_set_destination (ip6_address_t *addr, u32 mask_width, u32 vrf_id,
1431 int is_add, int is_pop, int is_none)
1433 ip6_main_t * im = &ip6_main;
1434 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1435 ip_lookup_main_t * lm = &im->lookup_main;
1436 ip_adjacency_t * adj;
1441 BVT(clib_bihash_kv) kv, value;
1443 if ((is_add + is_pop + is_none) != 1)
1444 return VNET_API_ERROR_INVALID_VALUE_2;
1446 /* Go find the adjacency we're supposed to tickle */
1447 p = hash_get (im->fib_index_by_table_id, vrf_id);
1450 return VNET_API_ERROR_NO_SUCH_FIB;
1454 len = vec_len (im->prefix_lengths_in_search_order);
1456 for (i = 0; i < len; i++)
1458 int dst_address_length = im->prefix_lengths_in_search_order[i];
1459 ip6_address_t * mask = &im->fib_masks[dst_address_length];
1461 if (dst_address_length != mask_width)
1464 kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
1465 kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
1466 kv.key[2] = ((u64)((fib_index))<<32) | dst_address_length;
1468 rv = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value);
1473 return VNET_API_ERROR_NO_SUCH_ENTRY;
1477 /* Got it, modify as directed... */
1478 adj_index = value.value;
1479 adj = ip_get_adjacency (lm, adj_index);
1481 /* Restore original lookup-next action */
1482 if (adj->saved_lookup_next_index)
1484 adj->lookup_next_index = adj->saved_lookup_next_index;
1485 adj->saved_lookup_next_index = 0;
1488 /* Save current action */
1489 if (is_add || is_pop)
1490 adj->saved_lookup_next_index = adj->lookup_next_index;
1493 adj->lookup_next_index = IP_LOOKUP_NEXT_ADD_HOP_BY_HOP;
1496 adj->lookup_next_index = IP_LOOKUP_NEXT_POP_HOP_BY_HOP;
1499 hm->ioam_flag = (is_add ? IOAM_HBYH_ADD :
1500 (is_pop ? IOAM_HBYH_POP : IOAM_HBYH_MOD));
1504 static clib_error_t *
1505 ip6_set_ioam_destination_command_fn (vlib_main_t * vm,
1506 unformat_input_t * input,
1507 vlib_cli_command_t * cmd)
1510 u32 mask_width = ~0;
1517 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1519 if (unformat (input, "%U/%d",
1520 unformat_ip6_address, &addr, &mask_width))
1522 else if (unformat (input, "vrf-id %d", &vrf_id))
1524 else if (unformat (input, "add"))
1526 else if (unformat (input, "pop"))
1528 else if (unformat (input, "none"))
1534 if ((is_add + is_pop + is_none) != 1)
1535 return clib_error_return (0, "One of (add, pop, none) required");
1536 if (mask_width == ~0)
1537 return clib_error_return (0, "<address>/<mask-width> required");
1539 rv = ip6_ioam_set_destination (&addr, mask_width, vrf_id,
1540 is_add, is_pop, is_none);
1547 return clib_error_return (0, "ip6_ioam_set_destination returned %d", rv);
1553 VLIB_CLI_COMMAND (ip6_set_ioam_destination_cmd, static) = {
1554 .path = "set ioam destination",
1555 .short_help = "set ioam destination <ip6-address>/<width> add | pop | none",
1556 .function = ip6_set_ioam_destination_command_fn,
1560 void vnet_register_ioam_end_of_path_callback (void *cb)
1562 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1564 hm->ioam_end_of_path_cb = cb;