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>
28 #include <vnet/lib-scv/scv_util.h>
30 /* Timestamp precision multipliers for seconds, milliseconds, microseconds
31 * and nanoseconds respectively.
33 static f64 trace_tsp_mul[4] = {1, 1e3, 1e6, 1e9};
35 char *ppc_state[] = {"None", "Encap", "Decap"};
37 ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main;
39 #define foreach_ip6_hbyh_ioam_input_next \
40 _(IP6_REWRITE, "ip6-rewrite") \
41 _(IP6_LOOKUP, "ip6-lookup") \
45 #define _(s,n) IP6_HBYH_IOAM_INPUT_NEXT_##s,
46 foreach_ip6_hbyh_ioam_input_next
48 IP6_HBYH_IOAM_INPUT_N_NEXT,
49 } ip6_hbyh_ioam_input_next_t;
57 fetch_trace_data_size(u8 trace_type)
59 u8 trace_data_size = 0;
61 if (trace_type == TRACE_TYPE_IF_TS_APP)
62 trace_data_size = sizeof(ioam_trace_if_ts_app_t);
63 else if(trace_type == TRACE_TYPE_IF)
64 trace_data_size = sizeof(ioam_trace_if_t);
65 else if(trace_type == TRACE_TYPE_TS)
66 trace_data_size = sizeof(ioam_trace_ts_t);
67 else if(trace_type == TRACE_TYPE_APP)
68 trace_data_size = sizeof(ioam_trace_app_t);
69 else if(trace_type == TRACE_TYPE_TS_APP)
70 trace_data_size = sizeof(ioam_trace_ts_app_t);
72 return trace_data_size;
75 static u8 * format_ioam_data_list_element (u8 * s, va_list * args)
77 u32 *elt = va_arg (*args, u32 *);
78 u8 *trace_type_p = va_arg (*args, u8 *);
79 u8 trace_type = *trace_type_p;
82 if (trace_type & BIT_TTL_NODEID)
84 u32 ttl_node_id_host_byte_order = clib_net_to_host_u32 (*elt);
85 s = format (s, "ttl 0x%x node id 0x%x ",
86 ttl_node_id_host_byte_order>>24,
87 ttl_node_id_host_byte_order & 0x00FFFFFF);
92 if (trace_type & BIT_ING_INTERFACE && trace_type & BIT_ING_INTERFACE)
94 u32 ingress_host_byte_order = clib_net_to_host_u32(*elt);
95 s = format (s, "ingress 0x%x egress 0x%x ",
96 ingress_host_byte_order >> 16,
97 ingress_host_byte_order &0xFFFF);
101 if (trace_type & BIT_TIMESTAMP)
103 u32 ts_in_host_byte_order = clib_net_to_host_u32 (*elt);
104 s = format (s, "ts 0x%x \n", ts_in_host_byte_order);
108 if (trace_type & BIT_APPDATA)
110 u32 appdata_in_host_byte_order = clib_net_to_host_u32 (*elt);
111 s = format (s, "app 0x%x ", appdata_in_host_byte_order);
118 static u8 * format_ioam_pow (u8 * s, va_list * args)
120 ioam_pow_option_t * pow0 = va_arg (*args, ioam_pow_option_t *);
121 u64 random, cumulative;
122 random = cumulative = 0;
125 random = clib_net_to_host_u64 (pow0->random);
126 cumulative = clib_net_to_host_u64 (pow0->cumulative);
129 s = format (s, "random = 0x%Lx, Cumulative = 0x%Lx, Index = 0x%x",
130 random, cumulative, pow0->reserved_profile_id);
135 ip6_hbh_ioam_trace_data_list_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt)
137 ioam_trace_option_t *trace;
138 u8 trace_data_size_in_words = 0;
142 trace = (ioam_trace_option_t *)opt;
144 s = format (s, " Trace Type 0x%x , %d elts left ts msb(s) 0x%x\n", trace->ioam_trace_type, trace->data_list_elts_left,
147 s = format (s, " Trace Type 0x%x , %d elts left\n", trace->ioam_trace_type, trace->data_list_elts_left);
148 trace_data_size_in_words = fetch_trace_data_size(trace->ioam_trace_type)/4;
149 elt = &trace->elts[0];
150 while ((u8 *) elt < ((u8 *)(&trace->elts[0]) + trace->hdr.length - 2
151 /* -2 accounts for ioam_trace_type,elts_left */)) {
152 s = format (s, " [%d] %U\n",elt_index,
153 format_ioam_data_list_element,
154 elt, &trace->ioam_trace_type);
156 elt += trace_data_size_in_words;
162 ip6_hbh_ioam_proof_of_work_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt)
164 ioam_pow_option_t *pow;
166 s = format (s, " POW opt present\n");
167 pow = (ioam_pow_option_t *) opt;
168 s = format (s, " %U\n", format_ioam_pow, pow);
173 ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt)
175 ip6_main_t * im = &ip6_main;
176 ip_lookup_main_t * lm = &im->lookup_main;
177 ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
179 ioam_trace_option_t *trace = (ioam_trace_option_t *)opt;
180 u32 adj_index = vnet_buffer (b)->ip.adj_index[VLIB_TX];
181 ip_adjacency_t *adj = ip_get_adjacency (lm, adj_index);
188 if (PREDICT_TRUE (trace->data_list_elts_left)) {
189 trace->data_list_elts_left--;
190 /* fetch_trace_data_size returns in bytes. Convert it to 4-bytes
191 * to skip to this node's location.
193 elt_index = trace->data_list_elts_left * fetch_trace_data_size(trace->ioam_trace_type) / 4;
194 elt = &trace->elts[elt_index];
195 if (trace->ioam_trace_type & BIT_TTL_NODEID) {
196 *elt = clib_host_to_net_u32 ((ip->hop_limit<<24) | hm->node_id);
200 if (trace->ioam_trace_type & BIT_ING_INTERFACE) {
201 *elt = (vnet_buffer(b)->sw_if_index[VLIB_RX]&0xFFFF) << 16 | (adj->rewrite_header.sw_if_index & 0xFFFF);
202 *elt = clib_host_to_net_u32(*elt);
206 if (trace->ioam_trace_type & BIT_TIMESTAMP) {
207 /* Send least significant 32 bits */
208 f64 time_f64 = (f64)(((f64)hm->unix_time_0) + (vlib_time_now(hm->vlib_main) - hm->vlib_time_0));
210 time_u64.as_u64 = time_f64 * trace_tsp_mul[hm->trace_tsp];
211 *elt = clib_host_to_net_u32(time_u64.as_u32[0]);
215 if (trace->ioam_trace_type & BIT_APPDATA) {
216 /* $$$ set elt0->app_data */
217 *elt = clib_host_to_net_u32(hm->app_data);
225 ip6_hbh_ioam_proof_of_work_handler (vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt)
227 ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
228 ioam_pow_option_t * pow;
229 u64 random = 0, cumulative = 0;
232 pow_profile = scv_profile_find(pow_profile_index);
233 if (PREDICT_FALSE(!pow_profile)) {
237 pow = (ioam_pow_option_t *) opt;
239 u8 pow_encap = (pow->random == 0);
241 if (PREDICT_FALSE(total_pkts_using_this_profile >= pow_profile->validity)) {
242 /* Choose a new profile */
243 u16 new_profile_index;
244 new_profile_index = scv_get_next_profile_id(hm->vlib_main, pow_profile_index);
245 if (new_profile_index != pow_profile_index) {
246 /* Got a new profile */
247 scv_profile_invalidate(hm->vlib_main, hm,
250 pow_profile_index = new_profile_index;
251 pow_profile = scv_profile_find(pow_profile_index);
252 total_pkts_using_this_profile = 0;
254 scv_profile_invalidate(hm->vlib_main, hm, pow_profile_index, pow_encap);
257 pow->reserved_profile_id = pow_profile_index & PROFILE_ID_MASK;
258 total_pkts_using_this_profile++;
259 } else { /* Non encap node */
260 if (PREDICT_FALSE(pow->reserved_profile_id != pow_profile_index)) {
261 /* New profile announced by encap node. */
262 scv_profile *new_profile = 0;
263 new_profile = scv_profile_find(pow->reserved_profile_id);
264 if (PREDICT_FALSE(new_profile == 0 || new_profile->validity == 0)) {
265 /* Profile is invalid. Use old profile*/
267 scv_profile_invalidate(hm->vlib_main, hm,
268 pow->reserved_profile_id,
271 scv_profile_invalidate(hm->vlib_main, hm,
274 pow_profile_index = pow->reserved_profile_id;
275 pow_profile = new_profile;
276 total_pkts_using_this_profile = 0;
279 total_pkts_using_this_profile++;
282 if (pow->random == 0) {
283 pow->random = clib_host_to_net_u64(scv_generate_random(pow_profile));
286 random = clib_net_to_host_u64(pow->random);
287 cumulative = clib_net_to_host_u64(pow->cumulative);
288 pow->cumulative = clib_host_to_net_u64(scv_update_cumulative(pow_profile, cumulative, random));
293 /* The main h-b-h tracer will be invoked, no need to do much here */
296 } ip6_add_hop_by_hop_trace_t;
298 /* packet trace format function */
299 static u8 * format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
301 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
302 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
303 ip6_add_hop_by_hop_trace_t * t = va_arg (*args,
304 ip6_add_hop_by_hop_trace_t *);
306 s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d",
311 vlib_node_registration_t ip6_add_hop_by_hop_node;
313 #define foreach_ip6_add_hop_by_hop_error \
314 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
317 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
318 foreach_ip6_add_hop_by_hop_error
320 IP6_ADD_HOP_BY_HOP_N_ERROR,
321 } ip6_add_hop_by_hop_error_t;
323 static char * ip6_add_hop_by_hop_error_strings[] = {
324 #define _(sym,string) string,
325 foreach_ip6_add_hop_by_hop_error
330 ip6_add_hop_by_hop_node_fn (vlib_main_t * vm,
331 vlib_node_runtime_t * node,
332 vlib_frame_t * frame)
334 ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
335 u32 n_left_from, * from, * to_next;
336 ip_lookup_next_t next_index;
338 u8 * rewrite = hm->rewrite;
339 u32 rewrite_length = vec_len (rewrite);
341 from = vlib_frame_vector_args (frame);
342 n_left_from = frame->n_vectors;
343 next_index = node->cached_next_index;
345 while (n_left_from > 0)
349 vlib_get_next_frame (vm, node, next_index,
350 to_next, n_left_to_next);
353 while (n_left_from >= 4 && n_left_to_next >= 2)
355 u32 next0 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
356 u32 next1 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
357 u32 sw_if_index0, sw_if_index1;
359 ethernet_header_t *en0, *en1;
361 vlib_buffer_t * b0, * b1;
363 /* Prefetch next iteration. */
365 vlib_buffer_t * p2, * p3;
367 p2 = vlib_get_buffer (vm, from[2]);
368 p3 = vlib_get_buffer (vm, from[3]);
370 vlib_prefetch_buffer_header (p2, LOAD);
371 vlib_prefetch_buffer_header (p3, LOAD);
373 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
374 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
377 /* speculatively enqueue b0 and b1 to the current next frame */
378 to_next[0] = bi0 = from[0];
379 to_next[1] = bi1 = from[1];
385 b0 = vlib_get_buffer (vm, bi0);
386 b1 = vlib_get_buffer (vm, bi1);
388 /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
389 ASSERT (b0->current_data == 0);
390 ASSERT (b1->current_data == 0);
392 ip0 = vlib_buffer_get_current (b0);
393 ip1 = vlib_buffer_get_current (b0);
395 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
396 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
398 /* $$$$$ End of processing 2 x packets $$$$$ */
400 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
402 if (b0->flags & VLIB_BUFFER_IS_TRACED)
404 ip6_add_hop_by_hop_trace_t *t =
405 vlib_add_trace (vm, node, b0, sizeof (*t));
406 t->sw_if_index = sw_if_index0;
407 t->next_index = next0;
409 if (b1->flags & VLIB_BUFFER_IS_TRACED)
411 ip6_add_hop_by_hop_trace_t *t =
412 vlib_add_trace (vm, node, b1, sizeof (*t));
413 t->sw_if_index = sw_if_index1;
414 t->next_index = next1;
418 /* verify speculative enqueues, maybe switch current next frame */
419 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
420 to_next, n_left_to_next,
421 bi0, bi1, next0, next1);
425 while (n_left_from > 0 && n_left_to_next > 0)
431 ip6_hop_by_hop_header_t * hbh0;
432 u64 * copy_src0, * copy_dst0;
435 /* speculatively enqueue b0 to the current next frame */
443 b0 = vlib_get_buffer (vm, bi0);
445 ip0 = vlib_buffer_get_current (b0);
447 /* Copy the ip header left by the required amount */
448 copy_dst0 = (u64 *)(((u8 *)ip0) - rewrite_length);
449 copy_src0 = (u64 *) ip0;
451 copy_dst0 [0] = copy_src0 [0];
452 copy_dst0 [1] = copy_src0 [1];
453 copy_dst0 [2] = copy_src0 [2];
454 copy_dst0 [3] = copy_src0 [3];
455 copy_dst0 [4] = copy_src0 [4];
456 vlib_buffer_advance (b0, - (word)rewrite_length);
457 ip0 = vlib_buffer_get_current (b0);
459 hbh0 = (ip6_hop_by_hop_header_t *)(ip0 + 1);
460 /* $$$ tune, rewrite_length is a multiple of 8 */
461 clib_memcpy (hbh0, rewrite, rewrite_length);
462 /* Patch the protocol chain, insert the h-b-h (type 0) header */
463 hbh0->protocol = ip0->protocol;
465 new_l0 = clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
466 ip0->payload_length = clib_host_to_net_u16 (new_l0);
468 /* Populate the (first) h-b-h list elt */
469 next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
471 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
472 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
474 ip6_add_hop_by_hop_trace_t *t =
475 vlib_add_trace (vm, node, b0, sizeof (*t));
476 t->next_index = next0;
481 /* verify speculative enqueue, maybe switch current next frame */
482 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
483 to_next, n_left_to_next,
487 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
490 vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
491 IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
492 return frame->n_vectors;
495 VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = {
496 .function = ip6_add_hop_by_hop_node_fn,
497 .name = "ip6-add-hop-by-hop",
498 .vector_size = sizeof (u32),
499 .format_trace = format_ip6_add_hop_by_hop_trace,
500 .type = VLIB_NODE_TYPE_INTERNAL,
502 .n_errors = ARRAY_LEN(ip6_add_hop_by_hop_error_strings),
503 .error_strings = ip6_add_hop_by_hop_error_strings,
505 /* See ip/lookup.h */
506 .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,
508 #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
509 foreach_ip6_hbyh_ioam_input_next
514 VLIB_NODE_FUNCTION_MULTIARCH (ip6_add_hop_by_hop_node, ip6_add_hop_by_hop_node_fn)
516 /* The main h-b-h tracer was already invoked, no need to do much here */
519 } ip6_pop_hop_by_hop_trace_t;
521 /* packet trace format function */
522 static u8 * format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
524 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
525 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
526 ip6_pop_hop_by_hop_trace_t * t = va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
528 s = format (s, "IP6_POP_HOP_BY_HOP: next index %d",
533 vlib_node_registration_t ip6_pop_hop_by_hop_node;
535 #define foreach_ip6_pop_hop_by_hop_error \
536 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \
537 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \
538 _(SCV_PASSED, "Pkts with SCV in Policy") \
539 _(SCV_FAILED, "Pkts with SCV out of Policy")
542 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
543 foreach_ip6_pop_hop_by_hop_error
545 IP6_POP_HOP_BY_HOP_N_ERROR,
546 } ip6_pop_hop_by_hop_error_t;
548 static char * ip6_pop_hop_by_hop_error_strings[] = {
549 #define _(sym,string) string,
550 foreach_ip6_pop_hop_by_hop_error
554 static inline void ioam_end_of_path_validation (vlib_main_t * vm,
556 ip6_hop_by_hop_header_t *hbh0)
558 ip6_hop_by_hop_option_t *opt0, *limit0;
559 ioam_pow_option_t * pow0;
561 u64 final_cumulative = 0;
565 if (!hbh0 || !ip0) return;
567 opt0 = (ip6_hop_by_hop_option_t *)(hbh0+1);
568 limit0 = (ip6_hop_by_hop_option_t *)
569 ((u8 *)hbh0 + ((hbh0->length+1)<<3));
571 /* Scan the set of h-b-h options, process ones that we understand */
572 while (opt0 < limit0)
577 case HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE:
578 case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
579 opt0 = (ip6_hop_by_hop_option_t *)
580 (((u8 *)opt0) + opt0->length
581 + sizeof (ip6_hop_by_hop_option_t));
583 case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK:
584 pow0 = (ioam_pow_option_t *) opt0;
585 random = clib_net_to_host_u64(pow0->random);
586 final_cumulative = clib_net_to_host_u64(pow0->cumulative);
587 result = scv_validate (pow_profile,
588 final_cumulative, random);
592 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
593 IP6_POP_HOP_BY_HOP_ERROR_SCV_PASSED, result);
597 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
598 IP6_POP_HOP_BY_HOP_ERROR_SCV_FAILED, 1);
600 /* TODO: notify the scv failure*/
601 opt0 = (ip6_hop_by_hop_option_t *)
602 (((u8 *)opt0) + sizeof (ioam_pow_option_t));
606 opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1;
610 format(0, "Something is wrong\n");
618 ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm,
619 vlib_node_runtime_t * node,
620 vlib_frame_t * frame)
622 ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
623 ip6_main_t * im = &ip6_main;
624 ip_lookup_main_t * lm = &im->lookup_main;
625 u32 n_left_from, * from, * to_next;
626 ip_lookup_next_t next_index;
629 u32 (*ioam_end_of_path_cb) (vlib_main_t *, vlib_node_runtime_t *,
630 vlib_buffer_t *, ip6_header_t *,
633 ioam_end_of_path_cb = hm->ioam_end_of_path_cb;
635 from = vlib_frame_vector_args (frame);
636 n_left_from = frame->n_vectors;
637 next_index = node->cached_next_index;
639 while (n_left_from > 0)
643 vlib_get_next_frame (vm, node, next_index,
644 to_next, n_left_to_next);
647 while (n_left_from >= 4 && n_left_to_next >= 2)
649 u32 next0 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
650 u32 next1 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
651 u32 sw_if_index0, sw_if_index1;
653 ethernet_header_t *en0, *en1;
655 vlib_buffer_t * b0, * b1;
657 /* Prefetch next iteration. */
659 vlib_buffer_t * p2, * p3;
661 p2 = vlib_get_buffer (vm, from[2]);
662 p3 = vlib_get_buffer (vm, from[3]);
664 vlib_prefetch_buffer_header (p2, LOAD);
665 vlib_prefetch_buffer_header (p3, LOAD);
667 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
668 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
671 /* speculatively enqueue b0 and b1 to the current next frame */
672 to_next[0] = bi0 = from[0];
673 to_next[1] = bi1 = from[1];
679 b0 = vlib_get_buffer (vm, bi0);
680 b1 = vlib_get_buffer (vm, bi1);
682 /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
683 ASSERT (b0->current_data == 0);
684 ASSERT (b1->current_data == 0);
686 ip0 = vlib_buffer_get_current (b0);
687 ip1 = vlib_buffer_get_current (b0);
689 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
690 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
692 /* $$$$$ End of processing 2 x packets $$$$$ */
694 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
696 if (b0->flags & VLIB_BUFFER_IS_TRACED)
698 ip6_pop_hop_by_hop_trace_t *t =
699 vlib_add_trace (vm, node, b0, sizeof (*t));
700 t->sw_if_index = sw_if_index0;
701 t->next_index = next0;
703 if (b1->flags & VLIB_BUFFER_IS_TRACED)
705 ip6_pop_hop_by_hop_trace_t *t =
706 vlib_add_trace (vm, node, b1, sizeof (*t));
707 t->sw_if_index = sw_if_index1;
708 t->next_index = next1;
712 /* verify speculative enqueues, maybe switch current next frame */
713 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
714 to_next, n_left_to_next,
715 bi0, bi1, next0, next1);
719 while (n_left_from > 0 && n_left_to_next > 0)
726 ip_adjacency_t * adj0;
727 ip6_hop_by_hop_header_t *hbh0;
728 u64 * copy_dst0, * copy_src0;
731 /* speculatively enqueue b0 to the current next frame */
739 b0 = vlib_get_buffer (vm, bi0);
741 ip0 = vlib_buffer_get_current (b0);
742 adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
743 adj0 = ip_get_adjacency (lm, adj_index0);
745 /* Default use the next_index from the adjacency. */
746 next0 = adj0->lookup_next_index;
748 /* Perfectly normal to end up here w/ out h-b-h header */
749 hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1);
751 /* Collect data from trace via callback */
752 next0 = ioam_end_of_path_cb ? ioam_end_of_path_cb (vm, node, b0, ip0, adj0) : next0;
754 /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
755 ioam_end_of_path_validation(vm, ip0, hbh0);
756 /* Pop the trace data */
757 vlib_buffer_advance (b0, (hbh0->length+1)<<3);
758 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
759 ((hbh0->length+1)<<3);
760 ip0->payload_length = clib_host_to_net_u16 (new_l0);
761 ip0->protocol = hbh0->protocol;
762 copy_src0 = (u64 *)ip0;
763 copy_dst0 = copy_src0 + (hbh0->length+1);
764 copy_dst0 [4] = copy_src0[4];
765 copy_dst0 [3] = copy_src0[3];
766 copy_dst0 [2] = copy_src0[2];
767 copy_dst0 [1] = copy_src0[1];
768 copy_dst0 [0] = copy_src0[0];
771 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
772 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
774 ip6_pop_hop_by_hop_trace_t *t =
775 vlib_add_trace (vm, node, b0, sizeof (*t));
776 t->next_index = next0;
779 /* verify speculative enqueue, maybe switch current next frame */
780 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
781 to_next, n_left_to_next,
785 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
788 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
789 IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
790 vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
791 IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
792 return frame->n_vectors;
795 VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) = {
796 .function = ip6_pop_hop_by_hop_node_fn,
797 .name = "ip6-pop-hop-by-hop",
798 .vector_size = sizeof (u32),
799 .format_trace = format_ip6_pop_hop_by_hop_trace,
800 .type = VLIB_NODE_TYPE_INTERNAL,
802 .n_errors = ARRAY_LEN(ip6_pop_hop_by_hop_error_strings),
803 .error_strings = ip6_pop_hop_by_hop_error_strings,
805 /* See ip/lookup.h */
806 .n_next_nodes = IP_LOOKUP_N_NEXT,
807 .next_nodes = IP6_LOOKUP_NEXT_NODES,
810 VLIB_NODE_FUNCTION_MULTIARCH (ip6_pop_hop_by_hop_node,
811 ip6_pop_hop_by_hop_node_fn)
813 static clib_error_t *
814 ip6_hop_by_hop_ioam_init (vlib_main_t * vm)
816 ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
819 hm->vnet_main = vnet_get_main();
820 hm->unix_time_0 = (u32) time (0); /* Store starting time */
821 hm->vlib_time_0 = vlib_time_now (vm);
822 hm->ioam_flag = IOAM_HBYH_MOD;
823 hm->trace_tsp = TSP_MICROSECONDS; /* Micro seconds */
826 * Register the handlers
827 * XXX: This should be done dynamically based on OAM feature being enabled or not.
829 if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST, ip6_hbh_ioam_trace_data_list_handler,
830 ip6_hbh_ioam_trace_data_list_trace_handler) < 0)
831 return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST failed"));
832 if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK, ip6_hbh_ioam_proof_of_work_handler,
833 ip6_hbh_ioam_proof_of_work_trace_handler) < 0)
834 return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK failed"));
839 VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init);
841 int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts,
842 int has_pow_option, int has_ppc_option)
846 ip6_hop_by_hop_header_t *hbh;
847 ioam_trace_option_t * trace_option;
848 ioam_pow_option_t * pow_option;
850 u8 trace_data_size = 0;
854 if (trace_option_elts == 0 && has_pow_option == 0)
857 /* Work out how much space we need */
858 size = sizeof (ip6_hop_by_hop_header_t);
860 if (trace_option_elts)
862 size += sizeof (ip6_hop_by_hop_option_t);
864 trace_data_size = fetch_trace_data_size(trace_type);
865 if (trace_data_size == 0)
866 return VNET_API_ERROR_INVALID_VALUE;
868 if (trace_option_elts * trace_data_size > 254)
869 return VNET_API_ERROR_INVALID_VALUE;
871 size += trace_option_elts * trace_data_size;
875 size += sizeof (ip6_hop_by_hop_option_t);
876 size += sizeof (ioam_pow_option_t);
879 /* Round to a multiple of 8 octets */
880 rnd_size = (size + 7) & ~7;
882 /* allocate it, zero-fill / pad by construction */
883 vec_validate (rewrite, rnd_size-1);
885 hbh = (ip6_hop_by_hop_header_t *) rewrite;
886 /* Length of header in 8 octet units, not incl first 8 octets */
887 hbh->length = (rnd_size>>3) - 1;
888 current = (u8 *)(hbh+1);
890 if (trace_option_elts)
892 trace_option = (ioam_trace_option_t *)current;
893 trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
894 | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
895 trace_option->hdr.length =
896 2 /*ioam_trace_type,data_list_elts_left */ +
897 trace_option_elts * trace_data_size;
898 trace_option->ioam_trace_type = trace_type & TRACE_TYPE_MASK;
899 trace_option->data_list_elts_left = trace_option_elts;
900 current += sizeof (ioam_trace_option_t) +
901 trace_option_elts * trace_data_size;
905 pow_option = (ioam_pow_option_t *)current;
906 pow_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK
907 | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
908 pow_option->hdr.length = sizeof (ioam_pow_option_t) -
909 sizeof (ip6_hop_by_hop_option_t);
910 current += sizeof (ioam_pow_option_t);
918 clear_ioam_rewrite_fn(void)
920 ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
922 vec_free(hm->rewrite);
927 hm->trace_option_elts = 0;
928 hm->has_pow_option = 0;
929 hm->has_ppc_option = 0;
930 hm->trace_tsp = TSP_MICROSECONDS;
935 clib_error_t * clear_ioam_rewrite_command_fn (vlib_main_t * vm,
936 unformat_input_t * input,
937 vlib_cli_command_t * cmd)
939 return(clear_ioam_rewrite_fn());
942 VLIB_CLI_COMMAND (ip6_clear_ioam_trace_cmd, static) = {
943 .path = "clear ioam rewrite",
944 .short_help = "clear ioam rewrite",
945 .function = clear_ioam_rewrite_command_fn,
949 ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id,
950 u32 app_data, int has_pow_option, u32 trace_tsp,
954 ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
955 rv = ip6_ioam_set_rewrite (&hm->rewrite, trace_type, trace_option_elts,
956 has_pow_option, has_ppc_option);
961 hm->node_id = node_id;
962 hm->app_data = app_data;
963 hm->trace_type = trace_type;
964 hm->trace_option_elts = trace_option_elts;
965 hm->has_pow_option = has_pow_option;
966 hm->has_ppc_option = has_ppc_option;
967 hm->trace_tsp = trace_tsp;
971 return clib_error_return_code(0, rv, 0, "ip6_ioam_set_rewrite returned %d", rv);
978 static clib_error_t *
979 ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
980 unformat_input_t * input,
981 vlib_cli_command_t * cmd)
983 u32 trace_option_elts = 0;
984 u32 trace_type = 0, node_id = 0;
985 u32 app_data = 0, trace_tsp = TSP_MICROSECONDS;
986 int has_pow_option = 0;
987 int has_ppc_option = 0;
988 clib_error_t * rv = 0;
990 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
992 if (unformat (input, "trace-type 0x%x trace-elts %d "
993 "trace-tsp %d node-id 0x%x app-data 0x%x",
994 &trace_type, &trace_option_elts, &trace_tsp,
995 &node_id, &app_data))
997 else if (unformat (input, "pow"))
999 else if (unformat (input, "ppc encap"))
1000 has_ppc_option = PPC_ENCAP;
1001 else if (unformat (input, "ppc decap"))
1002 has_ppc_option = PPC_DECAP;
1003 else if (unformat (input, "ppc none"))
1004 has_ppc_option = PPC_NONE;
1010 rv = ip6_ioam_trace_profile_set(trace_option_elts, trace_type, node_id,
1011 app_data, has_pow_option, trace_tsp, has_ppc_option);
1017 VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
1018 .path = "set ioam rewrite",
1019 .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>]",
1020 .function = ip6_set_ioam_rewrite_command_fn,
1023 static clib_error_t *
1024 ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
1025 unformat_input_t * input,
1026 vlib_cli_command_t * cmd)
1028 ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
1032 if (!is_zero_ip6_address(&hm->adj))
1034 s = format(s, " REWRITE FLOW CONFIGS - \n");
1035 s = format(s, " Destination Address : %U\n",
1036 format_ip6_address, &hm->adj, sizeof(ip6_address_t));
1037 s = format(s, " Flow operation : %d (%s)\n", hm->ioam_flag,
1038 (hm->ioam_flag == IOAM_HBYH_ADD) ? "Add" :
1039 ((hm->ioam_flag == IOAM_HBYH_MOD) ? "Mod" : "Pop"));
1043 s = format(s, " REWRITE FLOW CONFIGS - Not configured\n");
1046 if (hm->trace_option_elts)
1048 s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - \n");
1049 s = format(s, " Trace Type : 0x%x (%d)\n",
1050 hm->trace_type, hm->trace_type);
1051 s = format(s, " Trace timestamp precision : %d (%s)\n", hm->trace_tsp,
1052 (hm->trace_tsp == TSP_SECONDS) ? "Seconds" :
1053 ((hm->trace_tsp == TSP_MILLISECONDS) ? "Milliseconds" :
1054 (((hm->trace_tsp == TSP_MICROSECONDS) ? "Microseconds" : "Nanoseconds"))));
1055 s = format(s, " Num of trace nodes : %d\n",
1056 hm->trace_option_elts);
1057 s = format(s, " Node-id : 0x%x (%d)\n",
1058 hm->node_id, hm->node_id);
1059 s = format(s, " App Data : 0x%x (%d)\n",
1060 hm->app_data, hm->app_data);
1064 s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n");
1067 s = format(s, " POW OPTION - %d (%s)\n",
1068 hm->has_pow_option, (hm->has_pow_option?"Enabled":"Disabled"));
1069 if (hm->has_pow_option)
1070 s = format(s, "Try 'show ioam sc-profile' for more information\n");
1072 s = format(s, " EDGE TO EDGE - PPC OPTION - %d (%s)\n",
1073 hm->has_ppc_option, ppc_state[hm->has_ppc_option]);
1074 if (hm->has_ppc_option)
1075 s = format(s, "Try 'show ioam ppc' for more information\n");
1077 vlib_cli_output(vm, "%v", s);
1082 VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
1083 .path = "show ioam summary",
1084 .short_help = "Summary of IOAM configuration",
1085 .function = ip6_show_ioam_summary_cmd_fn,
1088 int ip6_ioam_set_destination (ip6_address_t *addr, u32 mask_width, u32 vrf_id,
1089 int is_add, int is_pop, int is_none)
1091 ip6_main_t * im = &ip6_main;
1092 ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
1093 ip_lookup_main_t * lm = &im->lookup_main;
1094 ip_adjacency_t * adj;
1099 BVT(clib_bihash_kv) kv, value;
1101 if ((is_add + is_pop + is_none) != 1)
1102 return VNET_API_ERROR_INVALID_VALUE_2;
1104 /* Go find the adjacency we're supposed to tickle */
1105 p = hash_get (im->fib_index_by_table_id, vrf_id);
1108 return VNET_API_ERROR_NO_SUCH_FIB;
1112 len = vec_len (im->prefix_lengths_in_search_order);
1114 for (i = 0; i < len; i++)
1116 int dst_address_length = im->prefix_lengths_in_search_order[i];
1117 ip6_address_t * mask = &im->fib_masks[dst_address_length];
1119 if (dst_address_length != mask_width)
1122 kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
1123 kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
1124 kv.key[2] = ((u64)((fib_index))<<32) | dst_address_length;
1126 rv = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value);
1131 return VNET_API_ERROR_NO_SUCH_ENTRY;
1135 /* Got it, modify as directed... */
1136 adj_index = value.value;
1137 adj = ip_get_adjacency (lm, adj_index);
1139 /* Restore original lookup-next action */
1140 if (adj->saved_lookup_next_index)
1142 adj->lookup_next_index = adj->saved_lookup_next_index;
1143 adj->saved_lookup_next_index = 0;
1146 /* Save current action */
1147 if (is_add || is_pop)
1148 adj->saved_lookup_next_index = adj->lookup_next_index;
1151 adj->lookup_next_index = IP_LOOKUP_NEXT_ADD_HOP_BY_HOP;
1154 adj->lookup_next_index = IP_LOOKUP_NEXT_POP_HOP_BY_HOP;
1157 hm->ioam_flag = (is_add ? IOAM_HBYH_ADD :
1158 (is_pop ? IOAM_HBYH_POP : IOAM_HBYH_MOD));
1162 static clib_error_t *
1163 ip6_set_ioam_destination_command_fn (vlib_main_t * vm,
1164 unformat_input_t * input,
1165 vlib_cli_command_t * cmd)
1168 u32 mask_width = ~0;
1175 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1177 if (unformat (input, "%U/%d",
1178 unformat_ip6_address, &addr, &mask_width))
1180 else if (unformat (input, "vrf-id %d", &vrf_id))
1182 else if (unformat (input, "add"))
1184 else if (unformat (input, "pop"))
1186 else if (unformat (input, "none"))
1192 if ((is_add + is_pop + is_none) != 1)
1193 return clib_error_return (0, "One of (add, pop, none) required");
1194 if (mask_width == ~0)
1195 return clib_error_return (0, "<address>/<mask-width> required");
1197 rv = ip6_ioam_set_destination (&addr, mask_width, vrf_id,
1198 is_add, is_pop, is_none);
1205 return clib_error_return (0, "ip6_ioam_set_destination returned %d", rv);
1211 VLIB_CLI_COMMAND (ip6_set_ioam_destination_cmd, static) = {
1212 .path = "set ioam destination",
1213 .short_help = "set ioam destination <ip6-address>/<width> add | pop | none",
1214 .function = ip6_set_ioam_destination_command_fn,
1218 void vnet_register_ioam_end_of_path_callback (void *cb)
1220 ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main;
1222 hm->ioam_end_of_path_cb = cb;