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 VLIB_NODE_FUNCTION_MULTIARCH (ip6_hop_by_hop_node, ip6_hop_by_hop_node_fn)
633 /* The main h-b-h tracer will be invoked, no need to do much here */
636 } ip6_add_hop_by_hop_trace_t;
638 /* packet trace format function */
639 static u8 * format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
641 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
642 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
643 ip6_add_hop_by_hop_trace_t * t = va_arg (*args,
644 ip6_add_hop_by_hop_trace_t *);
646 s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d",
651 vlib_node_registration_t ip6_add_hop_by_hop_node;
653 #define foreach_ip6_add_hop_by_hop_error \
654 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
657 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
658 foreach_ip6_add_hop_by_hop_error
660 IP6_ADD_HOP_BY_HOP_N_ERROR,
661 } ip6_add_hop_by_hop_error_t;
663 static char * ip6_add_hop_by_hop_error_strings[] = {
664 #define _(sym,string) string,
665 foreach_ip6_add_hop_by_hop_error
670 ip6_add_hop_by_hop_node_fn (vlib_main_t * vm,
671 vlib_node_runtime_t * node,
672 vlib_frame_t * frame)
674 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
675 u32 n_left_from, * from, * to_next;
676 ip_lookup_next_t next_index;
678 u8 * rewrite = hm->rewrite;
679 u32 rewrite_length = vec_len (rewrite);
681 from = vlib_frame_vector_args (frame);
682 n_left_from = frame->n_vectors;
683 next_index = node->cached_next_index;
685 while (n_left_from > 0)
689 vlib_get_next_frame (vm, node, next_index,
690 to_next, n_left_to_next);
693 while (n_left_from >= 4 && n_left_to_next >= 2)
695 u32 next0 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
696 u32 next1 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
697 u32 sw_if_index0, sw_if_index1;
699 ethernet_header_t *en0, *en1;
701 vlib_buffer_t * b0, * b1;
703 /* Prefetch next iteration. */
705 vlib_buffer_t * p2, * p3;
707 p2 = vlib_get_buffer (vm, from[2]);
708 p3 = vlib_get_buffer (vm, from[3]);
710 vlib_prefetch_buffer_header (p2, LOAD);
711 vlib_prefetch_buffer_header (p3, LOAD);
713 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
714 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
717 /* speculatively enqueue b0 and b1 to the current next frame */
718 to_next[0] = bi0 = from[0];
719 to_next[1] = bi1 = from[1];
725 b0 = vlib_get_buffer (vm, bi0);
726 b1 = vlib_get_buffer (vm, bi1);
728 /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
729 ASSERT (b0->current_data == 0);
730 ASSERT (b1->current_data == 0);
732 ip0 = vlib_buffer_get_current (b0);
733 ip1 = vlib_buffer_get_current (b0);
735 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
736 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
738 /* $$$$$ End of processing 2 x packets $$$$$ */
740 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
742 if (b0->flags & VLIB_BUFFER_IS_TRACED)
744 ip6_add_hop_by_hop_trace_t *t =
745 vlib_add_trace (vm, node, b0, sizeof (*t));
746 t->sw_if_index = sw_if_index0;
747 t->next_index = next0;
749 if (b1->flags & VLIB_BUFFER_IS_TRACED)
751 ip6_add_hop_by_hop_trace_t *t =
752 vlib_add_trace (vm, node, b1, sizeof (*t));
753 t->sw_if_index = sw_if_index1;
754 t->next_index = next1;
758 /* verify speculative enqueues, maybe switch current next frame */
759 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
760 to_next, n_left_to_next,
761 bi0, bi1, next0, next1);
765 while (n_left_from > 0 && n_left_to_next > 0)
771 ip6_hop_by_hop_header_t * hbh0;
772 u64 * copy_src0, * copy_dst0;
775 /* speculatively enqueue b0 to the current next frame */
783 b0 = vlib_get_buffer (vm, bi0);
785 ip0 = vlib_buffer_get_current (b0);
787 /* Copy the ip header left by the required amount */
788 copy_dst0 = (u64 *)(((u8 *)ip0) - rewrite_length);
789 copy_src0 = (u64 *) ip0;
791 copy_dst0 [0] = copy_src0 [0];
792 copy_dst0 [1] = copy_src0 [1];
793 copy_dst0 [2] = copy_src0 [2];
794 copy_dst0 [3] = copy_src0 [3];
795 copy_dst0 [4] = copy_src0 [4];
796 vlib_buffer_advance (b0, - (word)rewrite_length);
797 ip0 = vlib_buffer_get_current (b0);
799 hbh0 = (ip6_hop_by_hop_header_t *)(ip0 + 1);
800 /* $$$ tune, rewrite_length is a multiple of 8 */
801 clib_memcpy (hbh0, rewrite, rewrite_length);
802 /* Patch the protocol chain, insert the h-b-h (type 0) header */
803 hbh0->protocol = ip0->protocol;
805 new_l0 = clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
806 ip0->payload_length = clib_host_to_net_u16 (new_l0);
808 /* Populate the (first) h-b-h list elt */
809 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
811 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
812 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
814 ip6_add_hop_by_hop_trace_t *t =
815 vlib_add_trace (vm, node, b0, sizeof (*t));
816 t->next_index = next0;
821 /* verify speculative enqueue, maybe switch current next frame */
822 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
823 to_next, n_left_to_next,
827 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
830 vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
831 IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
832 return frame->n_vectors;
835 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = {
836 .function = ip6_add_hop_by_hop_node_fn,
837 .name = "ip6-add-hop-by-hop",
838 .vector_size = sizeof (u32),
839 .format_trace = format_ip6_add_hop_by_hop_trace,
840 .type = VLIB_NODE_TYPE_INTERNAL,
842 .n_errors = ARRAY_LEN(ip6_add_hop_by_hop_error_strings),
843 .error_strings = ip6_add_hop_by_hop_error_strings,
845 /* See ip/lookup.h */
846 .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
848 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
849 foreach_ip6_hbyh_input_next
854 VLIB_NODE_FUNCTION_MULTIARCH (ip6_add_hop_by_hop_node, ip6_add_hop_by_hop_node_fn)
856 /* The main h-b-h tracer was already invoked, no need to do much here */
859 } ip6_pop_hop_by_hop_trace_t;
861 /* packet trace format function */
862 static u8 * format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
864 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
865 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
866 ip6_pop_hop_by_hop_trace_t * t = va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
868 s = format (s, "IP6_POP_HOP_BY_HOP: next index %d",
873 vlib_node_registration_t ip6_pop_hop_by_hop_node;
875 #define foreach_ip6_pop_hop_by_hop_error \
876 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \
877 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
878 _(SCV_PASSED, "Pkts with SCV in Policy") \
879 _(SCV_FAILED, "Pkts with SCV out of Policy")
882 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
883 foreach_ip6_pop_hop_by_hop_error
885 IP6_POP_HOP_BY_HOP_N_ERROR,
886 } ip6_pop_hop_by_hop_error_t;
888 static char * ip6_pop_hop_by_hop_error_strings[] = {
889 #define _(sym,string) string,
890 foreach_ip6_pop_hop_by_hop_error
894 static inline void ioam_end_of_path_validation (vlib_main_t * vm,
896 ip6_hop_by_hop_header_t *hbh0)
898 ip6_hop_by_hop_option_t *opt0, *limit0;
899 ioam_pow_option_t * pow0;
901 u64 final_cumulative = 0;
905 if (!hbh0 || !ip0) return;
907 opt0 = (ip6_hop_by_hop_option_t *)(hbh0+1);
908 limit0 = (ip6_hop_by_hop_option_t *)
909 ((u8 *)hbh0 + ((hbh0->length+1)<<3));
911 /* Scan the set of h-b-h options, process ones that we understand */
912 while (opt0 < limit0)
914 type0 = opt0->type & HBH_OPTION_TYPE_MASK;
917 case HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE:
918 case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
919 opt0 = (ip6_hop_by_hop_option_t *)
920 (((u8 *)opt0) + opt0->length
921 + sizeof (ip6_hop_by_hop_option_t));
923 case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK:
924 pow0 = (ioam_pow_option_t *) opt0;
925 random = clib_net_to_host_u64(pow0->random);
926 final_cumulative = clib_net_to_host_u64(pow0->cumulative);
927 result = scv_validate (pow_profile,
928 final_cumulative, random);
932 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
933 IP6_POP_HOP_BY_HOP_ERROR_SCV_PASSED, result);
937 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
938 IP6_POP_HOP_BY_HOP_ERROR_SCV_FAILED, 1);
940 /* TODO: notify the scv failure*/
941 opt0 = (ip6_hop_by_hop_option_t *)
942 (((u8 *)opt0) + sizeof (ioam_pow_option_t));
946 opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1;
950 format(0, "Something is wrong\n");
958 ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm,
959 vlib_node_runtime_t * node,
960 vlib_frame_t * frame)
962 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
963 ip6_main_t * im = &ip6_main;
964 ip_lookup_main_t * lm = &im->lookup_main;
965 u32 n_left_from, * from, * to_next;
966 ip_lookup_next_t next_index;
969 u32 (*ioam_end_of_path_cb) (vlib_main_t *, vlib_node_runtime_t *,
970 vlib_buffer_t *, ip6_header_t *,
973 ioam_end_of_path_cb = hm->ioam_end_of_path_cb;
975 from = vlib_frame_vector_args (frame);
976 n_left_from = frame->n_vectors;
977 next_index = node->cached_next_index;
979 while (n_left_from > 0)
983 vlib_get_next_frame (vm, node, next_index,
984 to_next, n_left_to_next);
987 while (n_left_from >= 4 && n_left_to_next >= 2)
989 u32 next0 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
990 u32 next1 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
991 u32 sw_if_index0, sw_if_index1;
993 ethernet_header_t *en0, *en1;
995 vlib_buffer_t * b0, * b1;
997 /* Prefetch next iteration. */
999 vlib_buffer_t * p2, * p3;
1001 p2 = vlib_get_buffer (vm, from[2]);
1002 p3 = vlib_get_buffer (vm, from[3]);
1004 vlib_prefetch_buffer_header (p2, LOAD);
1005 vlib_prefetch_buffer_header (p3, LOAD);
1007 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
1008 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
1011 /* speculatively enqueue b0 and b1 to the current next frame */
1012 to_next[0] = bi0 = from[0];
1013 to_next[1] = bi1 = from[1];
1017 n_left_to_next -= 2;
1019 b0 = vlib_get_buffer (vm, bi0);
1020 b1 = vlib_get_buffer (vm, bi1);
1022 /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
1023 ASSERT (b0->current_data == 0);
1024 ASSERT (b1->current_data == 0);
1026 ip0 = vlib_buffer_get_current (b0);
1027 ip1 = vlib_buffer_get_current (b0);
1029 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1030 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
1032 /* $$$$$ End of processing 2 x packets $$$$$ */
1034 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
1036 if (b0->flags & VLIB_BUFFER_IS_TRACED)
1038 ip6_pop_hop_by_hop_trace_t *t =
1039 vlib_add_trace (vm, node, b0, sizeof (*t));
1040 t->sw_if_index = sw_if_index0;
1041 t->next_index = next0;
1043 if (b1->flags & VLIB_BUFFER_IS_TRACED)
1045 ip6_pop_hop_by_hop_trace_t *t =
1046 vlib_add_trace (vm, node, b1, sizeof (*t));
1047 t->sw_if_index = sw_if_index1;
1048 t->next_index = next1;
1052 /* verify speculative enqueues, maybe switch current next frame */
1053 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1054 to_next, n_left_to_next,
1055 bi0, bi1, next0, next1);
1059 while (n_left_from > 0 && n_left_to_next > 0)
1066 ip_adjacency_t * adj0;
1067 ip6_hop_by_hop_header_t *hbh0;
1068 u64 * copy_dst0, * copy_src0;
1071 /* speculatively enqueue b0 to the current next frame */
1077 n_left_to_next -= 1;
1079 b0 = vlib_get_buffer (vm, bi0);
1081 ip0 = vlib_buffer_get_current (b0);
1082 adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
1083 adj0 = ip_get_adjacency (lm, adj_index0);
1085 /* Perfectly normal to end up here w/ out h-b-h header */
1086 if (PREDICT_TRUE (ip0->protocol == 0))
1088 hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1);
1090 if (vnet_buffer(b0)->l2_classify.opaque_index == OI_DECAP)
1091 { /* First pass. Send to hbyh node. */
1092 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1097 /* Collect data from trace via callback */
1098 next0 = ioam_end_of_path_cb ?
1099 ioam_end_of_path_cb (vm, node, b0, ip0, adj0) :
1100 IP6_HBYH_INPUT_NEXT_IP6_REWRITE;
1101 /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
1102 ioam_end_of_path_validation(vm, ip0, hbh0);
1103 /* Pop the trace data */
1104 vlib_buffer_advance (b0, (hbh0->length+1)<<3);
1105 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
1106 ((hbh0->length+1)<<3);
1107 ip0->payload_length = clib_host_to_net_u16 (new_l0);
1108 ip0->protocol = hbh0->protocol;
1109 copy_src0 = (u64 *)ip0;
1110 copy_dst0 = copy_src0 + (hbh0->length+1);
1111 copy_dst0 [4] = copy_src0[4];
1112 copy_dst0 [3] = copy_src0[3];
1113 copy_dst0 [2] = copy_src0[2];
1114 copy_dst0 [1] = copy_src0[1];
1115 copy_dst0 [0] = copy_src0[0];
1120 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1124 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1125 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1127 ip6_pop_hop_by_hop_trace_t *t =
1128 vlib_add_trace (vm, node, b0, sizeof (*t));
1129 t->next_index = next0;
1133 /* verify speculative enqueue, maybe switch current next frame */
1134 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1135 to_next, n_left_to_next,
1139 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1142 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
1143 IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
1144 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
1145 IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
1146 return frame->n_vectors;
1149 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) = {
1150 .function = ip6_pop_hop_by_hop_node_fn,
1151 .name = "ip6-pop-hop-by-hop",
1152 .vector_size = sizeof (u32),
1153 .format_trace = format_ip6_pop_hop_by_hop_trace,
1154 .type = VLIB_NODE_TYPE_INTERNAL,
1156 .n_errors = ARRAY_LEN(ip6_pop_hop_by_hop_error_strings),
1157 .error_strings = ip6_pop_hop_by_hop_error_strings,
1159 /* See ip/lookup.h */
1160 .n_next_nodes = IP6_HBYH_INPUT_N_NEXT,
1162 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n,
1163 foreach_ip6_hbyh_input_next
1168 VLIB_NODE_FUNCTION_MULTIARCH (ip6_pop_hop_by_hop_node,
1169 ip6_pop_hop_by_hop_node_fn)
1171 static clib_error_t *
1172 ip6_hop_by_hop_init (vlib_main_t * vm)
1174 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1177 hm->vnet_main = vnet_get_main();
1178 hm->unix_time_0 = (u32) time (0); /* Store starting time */
1179 hm->vlib_time_0 = vlib_time_now (vm);
1180 hm->ioam_flag = IOAM_HBYH_MOD;
1181 hm->trace_tsp = TSP_MICROSECONDS; /* Micro seconds */
1186 VLIB_INIT_FUNCTION (ip6_hop_by_hop_init);
1188 int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts,
1189 int has_pow_option, int has_ppc_option)
1193 ip6_hop_by_hop_header_t *hbh;
1194 ioam_trace_option_t * trace_option;
1195 ioam_pow_option_t * pow_option;
1197 u8 trace_data_size = 0;
1201 if (trace_option_elts == 0 && has_pow_option == 0)
1204 /* Work out how much space we need */
1205 size = sizeof (ip6_hop_by_hop_header_t);
1207 if (trace_option_elts)
1209 size += sizeof (ip6_hop_by_hop_option_t);
1211 trace_data_size = fetch_trace_data_size(trace_type);
1212 if (trace_data_size == 0)
1213 return VNET_API_ERROR_INVALID_VALUE;
1215 if (trace_option_elts * trace_data_size > 254)
1216 return VNET_API_ERROR_INVALID_VALUE;
1218 size += trace_option_elts * trace_data_size;
1222 size += sizeof (ip6_hop_by_hop_option_t);
1223 size += sizeof (ioam_pow_option_t);
1226 /* Round to a multiple of 8 octets */
1227 rnd_size = (size + 7) & ~7;
1229 /* allocate it, zero-fill / pad by construction */
1230 vec_validate (rewrite, rnd_size-1);
1232 hbh = (ip6_hop_by_hop_header_t *) rewrite;
1233 /* Length of header in 8 octet units, not incl first 8 octets */
1234 hbh->length = (rnd_size>>3) - 1;
1235 current = (u8 *)(hbh+1);
1237 if (trace_option_elts)
1239 trace_option = (ioam_trace_option_t *)current;
1240 trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
1241 | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1242 trace_option->hdr.length =
1243 2 /*ioam_trace_type,data_list_elts_left */ +
1244 trace_option_elts * trace_data_size;
1245 trace_option->ioam_trace_type = trace_type & TRACE_TYPE_MASK;
1246 trace_option->data_list_elts_left = trace_option_elts;
1247 current += sizeof (ioam_trace_option_t) +
1248 trace_option_elts * trace_data_size;
1252 pow_option = (ioam_pow_option_t *)current;
1253 pow_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK
1254 | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
1255 pow_option->hdr.length = sizeof (ioam_pow_option_t) -
1256 sizeof (ip6_hop_by_hop_option_t);
1257 current += sizeof (ioam_pow_option_t);
1265 clear_ioam_rewrite_fn(void)
1267 ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1269 vec_free(hm->rewrite);
1274 hm->trace_option_elts = 0;
1275 hm->has_pow_option = 0;
1276 hm->has_ppc_option = 0;
1277 hm->trace_tsp = TSP_MICROSECONDS;
1282 clib_error_t * clear_ioam_rewrite_command_fn (vlib_main_t * vm,
1283 unformat_input_t * input,
1284 vlib_cli_command_t * cmd)
1286 return(clear_ioam_rewrite_fn());
1289 VLIB_CLI_COMMAND (ip6_clear_ioam_trace_cmd, static) = {
1290 .path = "clear ioam rewrite",
1291 .short_help = "clear ioam rewrite",
1292 .function = clear_ioam_rewrite_command_fn,
1296 ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id,
1297 u32 app_data, int has_pow_option, u32 trace_tsp,
1301 ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1302 rv = ip6_ioam_set_rewrite (&hm->rewrite, trace_type, trace_option_elts,
1303 has_pow_option, has_ppc_option);
1308 hm->node_id = node_id;
1309 hm->app_data = app_data;
1310 hm->trace_type = trace_type;
1311 hm->trace_option_elts = trace_option_elts;
1312 hm->has_pow_option = has_pow_option;
1313 hm->has_ppc_option = has_ppc_option;
1314 hm->trace_tsp = trace_tsp;
1318 return clib_error_return_code(0, rv, 0, "ip6_ioam_set_rewrite returned %d", rv);
1325 static clib_error_t *
1326 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
1327 unformat_input_t * input,
1328 vlib_cli_command_t * cmd)
1330 u32 trace_option_elts = 0;
1331 u32 trace_type = 0, node_id = 0;
1332 u32 app_data = 0, trace_tsp = TSP_MICROSECONDS;
1333 int has_pow_option = 0;
1334 int has_ppc_option = 0;
1335 clib_error_t * rv = 0;
1337 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1339 if (unformat (input, "trace-type 0x%x trace-elts %d "
1340 "trace-tsp %d node-id 0x%x app-data 0x%x",
1341 &trace_type, &trace_option_elts, &trace_tsp,
1342 &node_id, &app_data))
1344 else if (unformat (input, "pow"))
1346 else if (unformat (input, "ppc encap"))
1347 has_ppc_option = PPC_ENCAP;
1348 else if (unformat (input, "ppc decap"))
1349 has_ppc_option = PPC_DECAP;
1350 else if (unformat (input, "ppc none"))
1351 has_ppc_option = PPC_NONE;
1357 rv = ip6_ioam_trace_profile_set(trace_option_elts, trace_type, node_id,
1358 app_data, has_pow_option, trace_tsp, has_ppc_option);
1364 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1365 .path = "set ioam rewrite",
1366 .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>]",
1367 .function = ip6_set_ioam_rewrite_command_fn,
1370 static clib_error_t *
1371 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1372 unformat_input_t * input,
1373 vlib_cli_command_t * cmd)
1375 ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
1379 if (!is_zero_ip6_address(&hm->adj))
1381 s = format(s, " REWRITE FLOW CONFIGS - \n");
1382 s = format(s, " Destination Address : %U\n",
1383 format_ip6_address, &hm->adj, sizeof(ip6_address_t));
1384 s = format(s, " Flow operation : %d (%s)\n", hm->ioam_flag,
1385 (hm->ioam_flag == IOAM_HBYH_ADD) ? "Add" :
1386 ((hm->ioam_flag == IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1390 s = format(s, " REWRITE FLOW CONFIGS - Not configured\n");
1393 if (hm->trace_option_elts)
1395 s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - \n");
1396 s = format(s, " Trace Type : 0x%x (%d)\n",
1397 hm->trace_type, hm->trace_type);
1398 s = format(s, " Trace timestamp precision : %d (%s)\n", hm->trace_tsp,
1399 (hm->trace_tsp == TSP_SECONDS) ? "Seconds" :
1400 ((hm->trace_tsp == TSP_MILLISECONDS) ? "Milliseconds" :
1401 (((hm->trace_tsp == TSP_MICROSECONDS) ? "Microseconds" : "Nanoseconds"))));
1402 s = format(s, " Num of trace nodes : %d\n",
1403 hm->trace_option_elts);
1404 s = format(s, " Node-id : 0x%x (%d)\n",
1405 hm->node_id, hm->node_id);
1406 s = format(s, " App Data : 0x%x (%d)\n",
1407 hm->app_data, hm->app_data);
1411 s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n");
1414 s = format(s, " POW OPTION - %d (%s)\n",
1415 hm->has_pow_option, (hm->has_pow_option?"Enabled":"Disabled"));
1416 if (hm->has_pow_option)
1417 s = format(s, "Try 'show ioam sc-profile' for more information\n");
1419 s = format(s, " EDGE TO EDGE - PPC OPTION - %d (%s)\n",
1420 hm->has_ppc_option, ppc_state[hm->has_ppc_option]);
1421 if (hm->has_ppc_option)
1422 s = format(s, "Try 'show ioam ppc' for more information\n");
1424 vlib_cli_output(vm, "%v", s);
1429 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1430 .path = "show ioam summary",
1431 .short_help = "Summary of IOAM configuration",
1432 .function = ip6_show_ioam_summary_cmd_fn,
1435 int ip6_ioam_set_destination (ip6_address_t *addr, u32 mask_width, u32 vrf_id,
1436 int is_add, int is_pop, int is_none)
1438 ip6_main_t * im = &ip6_main;
1439 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1440 ip_lookup_main_t * lm = &im->lookup_main;
1441 ip_adjacency_t * adj;
1446 BVT(clib_bihash_kv) kv, value;
1448 if ((is_add + is_pop + is_none) != 1)
1449 return VNET_API_ERROR_INVALID_VALUE_2;
1451 /* Go find the adjacency we're supposed to tickle */
1452 p = hash_get (im->fib_index_by_table_id, vrf_id);
1455 return VNET_API_ERROR_NO_SUCH_FIB;
1459 len = vec_len (im->prefix_lengths_in_search_order);
1461 for (i = 0; i < len; i++)
1463 int dst_address_length = im->prefix_lengths_in_search_order[i];
1464 ip6_address_t * mask = &im->fib_masks[dst_address_length];
1466 if (dst_address_length != mask_width)
1469 kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
1470 kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
1471 kv.key[2] = ((u64)((fib_index))<<32) | dst_address_length;
1473 rv = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value);
1478 return VNET_API_ERROR_NO_SUCH_ENTRY;
1482 /* Got it, modify as directed... */
1483 adj_index = value.value;
1484 adj = ip_get_adjacency (lm, adj_index);
1486 /* Restore original lookup-next action */
1487 if (adj->saved_lookup_next_index)
1489 adj->lookup_next_index = adj->saved_lookup_next_index;
1490 adj->saved_lookup_next_index = 0;
1493 /* Save current action */
1494 if (is_add || is_pop)
1495 adj->saved_lookup_next_index = adj->lookup_next_index;
1498 adj->lookup_next_index = IP_LOOKUP_NEXT_ADD_HOP_BY_HOP;
1501 adj->lookup_next_index = IP_LOOKUP_NEXT_POP_HOP_BY_HOP;
1504 hm->ioam_flag = (is_add ? IOAM_HBYH_ADD :
1505 (is_pop ? IOAM_HBYH_POP : IOAM_HBYH_MOD));
1509 static clib_error_t *
1510 ip6_set_ioam_destination_command_fn (vlib_main_t * vm,
1511 unformat_input_t * input,
1512 vlib_cli_command_t * cmd)
1515 u32 mask_width = ~0;
1522 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1524 if (unformat (input, "%U/%d",
1525 unformat_ip6_address, &addr, &mask_width))
1527 else if (unformat (input, "vrf-id %d", &vrf_id))
1529 else if (unformat (input, "add"))
1531 else if (unformat (input, "pop"))
1533 else if (unformat (input, "none"))
1539 if ((is_add + is_pop + is_none) != 1)
1540 return clib_error_return (0, "One of (add, pop, none) required");
1541 if (mask_width == ~0)
1542 return clib_error_return (0, "<address>/<mask-width> required");
1544 rv = ip6_ioam_set_destination (&addr, mask_width, vrf_id,
1545 is_add, is_pop, is_none);
1552 return clib_error_return (0, "ip6_ioam_set_destination returned %d", rv);
1558 VLIB_CLI_COMMAND (ip6_set_ioam_destination_cmd, static) = {
1559 .path = "set ioam destination",
1560 .short_help = "set ioam destination <ip6-address>/<width> add | pop | none",
1561 .function = ip6_set_ioam_destination_command_fn,
1565 void vnet_register_ioam_end_of_path_callback (void *cb)
1567 ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main;
1569 hm->ioam_end_of_path_cb = cb;