aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/ioam/analyse
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/ioam/analyse')
-rw-r--r--src/plugins/ioam/analyse/ioam_analyse.h526
-rw-r--r--src/plugins/ioam/analyse/ioam_summary_export.c444
-rwxr-xr-xsrc/plugins/ioam/analyse/ioam_summary_export.h86
-rw-r--r--src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.c163
-rw-r--r--src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.h127
-rw-r--r--src/plugins/ioam/analyse/ip6/node.c523
6 files changed, 1869 insertions, 0 deletions
diff --git a/src/plugins/ioam/analyse/ioam_analyse.h b/src/plugins/ioam/analyse/ioam_analyse.h
new file mode 100644
index 00000000..ef2865da
--- /dev/null
+++ b/src/plugins/ioam/analyse/ioam_analyse.h
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IOAM_ANALYSE_H_
+#define PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IOAM_ANALYSE_H_
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vppinfra/types.h>
+#include <ioam/lib-e2e/e2e_util.h>
+#include <ioam/lib-trace/trace_util.h>
+#include <ioam/lib-trace/trace_config.h>
+
+#define IOAM_FLOW_TEMPLATE_ID 260
+#define IOAM_TRACE_MAX_NODES 10
+#define IOAM_MAX_PATHS_PER_FLOW 10
+
+typedef struct
+{
+ u16 ingress_if;
+ u16 egress_if;
+ u32 node_id;
+ u32 state_up;
+} ioam_path_map_t;
+
+/** @brief Analysed iOAM trace data.
+ @note cache aligned.
+*/
+typedef struct
+{
+ /** No of nodes in path. */
+ u8 num_nodes;
+
+ /** Data contained in trace - NodeId, TTL, Ingress & Egress Link, Timestamp. */
+ u8 trace_type;
+
+ /** Flag to indicate whether node is allocated. */
+ u8 is_free;
+
+ u8 pad[5];
+
+ /** Actual PATH flow has taken. */
+ ioam_path_map_t path[IOAM_TRACE_MAX_NODES];
+
+ /** Num of pkts in the flow going over path. */
+ u32 pkt_counter;
+
+ /** Num of bytes in the flow going over path. */
+ u32 bytes_counter;
+
+ /** Minumum Dealay for the flow. */
+ u32 min_delay;
+
+ /** Maximum Dealay for the flow. */
+ u32 max_delay;
+
+ /** Average Dealay for the flow. */
+ u32 mean_delay;
+
+ u32 reserve;
+} ioam_analyse_trace_record;
+
+typedef struct
+{
+ ioam_analyse_trace_record path_data[IOAM_MAX_PATHS_PER_FLOW];
+} ioam_analyse_trace_data;
+
+/** @brief Analysed iOAM pot data.
+ @note cache aligned.
+*/
+typedef struct
+{
+ /** Number of packets validated (passes through the service chain)
+ within the timestamps. */
+ u32 sfc_validated_count;
+
+ /** Number of packets invalidated (failed through the service chain)
+ within the timestamps. */
+ u32 sfc_invalidated_count;
+} ioam_analyse_pot_data;
+
+/** @brief Analysed iOAM data.
+ @note cache aligned.
+*/
+typedef struct ioam_analyser_data_t_
+{
+ u8 is_free;
+ u8 pad[3];
+
+ /** Num of pkts sent for this flow. */
+ u32 pkt_sent;
+
+ /** Num of pkts matching this flow. */
+ u32 pkt_counter;
+
+ /** Num of bytes matching this flow. */
+ u32 bytes_counter;
+
+ /** Analysed iOAM trace data. */
+ ioam_analyse_trace_data trace_data;
+
+ /** Analysed iOAM pot data. */
+ ioam_analyse_pot_data pot_data;
+
+ /** Analysed iOAM seqno data. */
+ seqno_rx_info seqno_data;
+
+ /** Cache of previously analysed data, useful for export. */
+ struct ioam_analyser_data_t_ *chached_data_list;
+
+ /** Lock to since we use this to export the data in other thread. */
+ volatile u32 *writer_lock;
+} ioam_analyser_data_t;
+
+always_inline f64
+ip6_ioam_analyse_calc_delay (ioam_trace_hdr_t * trace, u16 trace_len,
+ u8 oneway)
+{
+ u16 size_of_all_traceopts;
+ u8 size_of_traceopt_per_node;
+ u8 num_nodes;
+ u32 *start_elt, *end_elt, *uturn_elt;;
+ u32 start_time, end_time;
+ u8 done = 0;
+
+ size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
+ // Unknown trace type
+ if (size_of_traceopt_per_node == 0)
+ return 0;
+ size_of_all_traceopts = trace_len; /*ioam_trace_type,data_list_elts_left */
+
+ num_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
+ if ((num_nodes == 0) || (num_nodes <= trace->data_list_elts_left))
+ return 0;
+
+ num_nodes -= trace->data_list_elts_left;
+
+ start_elt = trace->elts;
+ end_elt =
+ trace->elts +
+ (u32) ((size_of_traceopt_per_node / sizeof (u32)) * (num_nodes - 1));
+
+ if (oneway && (trace->ioam_trace_type & BIT_TTL_NODEID))
+ {
+ done = 0;
+ do
+ {
+ uturn_elt = start_elt - size_of_traceopt_per_node / sizeof (u32);
+
+ if ((clib_net_to_host_u32 (*start_elt) >> 24) <=
+ (clib_net_to_host_u32 (*uturn_elt) >> 24))
+ done = 1;
+ }
+ while (!done && (start_elt = uturn_elt) != end_elt);
+ }
+ if (trace->ioam_trace_type & BIT_TTL_NODEID)
+ {
+ start_elt++;
+ end_elt++;
+ }
+ if (trace->ioam_trace_type & BIT_ING_INTERFACE)
+ {
+ start_elt++;
+ end_elt++;
+ }
+ start_time = clib_net_to_host_u32 (*start_elt);
+ end_time = clib_net_to_host_u32 (*end_elt);
+
+ return (f64) (end_time - start_time);
+}
+
+always_inline void
+ip6_ioam_analyse_set_paths_down (ioam_analyser_data_t * data)
+{
+ ioam_analyse_trace_data *trace_data;
+ ioam_analyse_trace_record *trace_record;
+ ioam_path_map_t *path;
+ u8 k, i;
+
+ while (__sync_lock_test_and_set (data->writer_lock, 1))
+ ;
+
+ trace_data = &data->trace_data;
+
+ for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
+ {
+ trace_record = trace_data->path_data + i;
+
+ if (trace_record->is_free)
+ continue;
+
+ path = trace_record->path;
+
+ for (k = 0; k < trace_record->num_nodes; k++)
+ path[k].state_up = 0;
+ }
+ *(data->writer_lock) = 0;
+}
+
+always_inline void
+ip6_ioam_analyse_hbh_trace_loopback (ioam_analyser_data_t * data,
+ ioam_trace_hdr_t * trace, u16 trace_len)
+{
+ ioam_analyse_trace_data *trace_data;
+ ioam_analyse_trace_record *trace_record;
+ ioam_path_map_t *path;
+ u8 i, j, k, num_nodes, max_nodes;
+ u8 *ptr;
+ u32 nodeid;
+ u16 ingress_if, egress_if;
+ u16 size_of_traceopt_per_node;
+ u16 size_of_all_traceopts;
+
+ while (__sync_lock_test_and_set (data->writer_lock, 1))
+ ;
+
+ trace_data = &data->trace_data;
+
+ size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
+ if (0 == size_of_traceopt_per_node)
+ goto end;
+
+ size_of_all_traceopts = trace_len;
+
+ ptr = (u8 *) trace->elts;
+ max_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
+ num_nodes = max_nodes - trace->data_list_elts_left;
+
+ for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
+ {
+ trace_record = trace_data->path_data + i;
+ path = trace_record->path;
+
+ if (trace_record->is_free)
+ continue;
+
+ for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
+ {
+ ptr =
+ (u8 *) ((u8 *) trace->elts +
+ (size_of_traceopt_per_node * (j - 1)));
+
+ nodeid = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
+ ptr += 4;
+
+ if (nodeid != path[k].node_id)
+ goto end;
+
+ if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
+ (trace->ioam_trace_type == TRACE_TYPE_IF))
+ {
+ ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ ptr += 2;
+ egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ if ((ingress_if != path[k].ingress_if) ||
+ (egress_if != path[k].egress_if))
+ {
+ goto end;
+ }
+ }
+ /* Found Match - set path hop state to up */
+ path[k].state_up = 1;
+ }
+ }
+end:
+ *(data->writer_lock) = 0;
+}
+
+always_inline int
+ip6_ioam_analyse_hbh_trace (ioam_analyser_data_t * data,
+ ioam_trace_hdr_t * trace, u16 pak_len,
+ u16 trace_len)
+{
+ ioam_analyse_trace_data *trace_data;
+ u16 size_of_traceopt_per_node;
+ u16 size_of_all_traceopts;
+ u8 i, j, k, num_nodes, max_nodes;
+ u8 *ptr;
+ u32 nodeid;
+ u16 ingress_if, egress_if;
+ ioam_path_map_t *path = NULL;
+ ioam_analyse_trace_record *trace_record;
+
+ while (__sync_lock_test_and_set (data->writer_lock, 1))
+ ;
+
+ trace_data = &data->trace_data;
+
+ size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
+ // Unknown trace type
+ if (size_of_traceopt_per_node == 0)
+ goto DONE;
+ size_of_all_traceopts = trace_len;
+
+ ptr = (u8 *) trace->elts;
+ max_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
+ num_nodes = max_nodes - trace->data_list_elts_left;
+
+ for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
+ {
+ trace_record = trace_data->path_data + i;
+
+ if (trace_record->is_free ||
+ (num_nodes != trace_record->num_nodes) ||
+ (trace->ioam_trace_type != trace_record->trace_type))
+ continue;
+
+ path = trace_record->path;
+
+ for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
+ {
+ ptr =
+ (u8 *) ((u8 *) trace->elts +
+ (size_of_traceopt_per_node * (j - 1)));
+
+ nodeid = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
+ ptr += 4;
+
+ if (nodeid != path[k].node_id)
+ break;
+
+ if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
+ (trace->ioam_trace_type == TRACE_TYPE_IF))
+ {
+ ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ ptr += 2;
+ egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ if ((ingress_if != path[k].ingress_if) ||
+ (egress_if != path[k].egress_if))
+ {
+ break;
+ }
+ }
+ }
+
+ if (k == num_nodes)
+ {
+ goto found_match;
+ }
+ }
+
+ for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
+ {
+ trace_record = trace_data->path_data + i;
+ if (trace_record->is_free)
+ {
+ trace_record->is_free = 0;
+ trace_record->num_nodes = num_nodes;
+ trace_record->trace_type = trace->ioam_trace_type;
+ path = trace_data->path_data[i].path;
+ trace_record->pkt_counter = 0;
+ trace_record->bytes_counter = 0;
+ trace_record->min_delay = 0xFFFFFFFF;
+ trace_record->max_delay = 0;
+ trace_record->mean_delay = 0;
+ break;
+ }
+ }
+
+ for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
+ {
+ ptr =
+ (u8 *) ((u8 *) trace->elts + (size_of_traceopt_per_node * (j - 1)));
+
+ path[k].node_id = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
+ ptr += 4;
+
+ if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
+ (trace->ioam_trace_type == TRACE_TYPE_IF))
+ {
+ path[k].ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ ptr += 2;
+ path[k].egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ }
+ }
+
+found_match:
+ /* Set path state to UP */
+ for (k = 0; k < num_nodes; k++)
+ path[k].state_up = 1;
+
+ trace_record->pkt_counter++;
+ trace_record->bytes_counter += pak_len;
+ if (trace->ioam_trace_type & BIT_TIMESTAMP)
+ {
+ /* Calculate time delay */
+ u32 delay = (u32) ip6_ioam_analyse_calc_delay (trace, trace_len, 0);
+ if (delay < trace_record->min_delay)
+ trace_record->min_delay = delay;
+ else if (delay > trace_record->max_delay)
+ trace_record->max_delay = delay;
+
+ u64 sum = (trace_record->mean_delay * data->seqno_data.rx_packets);
+ trace_record->mean_delay =
+ (u32) ((sum + delay) / (data->seqno_data.rx_packets + 1));
+ }
+DONE:
+ *(data->writer_lock) = 0;
+ return 0;
+}
+
+always_inline int
+ip6_ioam_analyse_hbh_e2e (ioam_analyser_data_t * data,
+ ioam_e2e_packet_t * e2e, u16 len)
+{
+ while (__sync_lock_test_and_set (data->writer_lock, 1))
+ ;
+
+ ioam_analyze_seqno (&data->seqno_data,
+ (u64) clib_net_to_host_u32 (e2e->e2e_data));
+
+ *(data->writer_lock) = 0;
+ return 0;
+}
+
+always_inline u8 *
+format_path_map (u8 * s, va_list * args)
+{
+ ioam_path_map_t *pm = va_arg (*args, ioam_path_map_t *);
+ u32 num_of_elts = va_arg (*args, u32);
+ u32 i;
+
+ for (i = 0; i < num_of_elts; i++)
+ {
+ s =
+ format (s,
+ "node_id: 0x%x, ingress_if: 0x%x, egress_if:0x%x, state:%s\n",
+ pm->node_id, pm->ingress_if, pm->egress_if,
+ pm->state_up ? "UP" : "DOWN");
+ pm++;
+ }
+
+ return (s);
+}
+
+always_inline u8 *
+print_analyse_flow (u8 * s, ioam_analyser_data_t * record)
+{
+ int j;
+ ioam_analyse_trace_record *trace_record;
+
+ s = format (s, "pkt_sent : %u\n", record->pkt_sent);
+ s = format (s, "pkt_counter : %u\n", record->pkt_counter);
+ s = format (s, "bytes_counter : %u\n", record->bytes_counter);
+
+ s = format (s, "Trace data: \n");
+
+ for (j = 0; j < IOAM_MAX_PATHS_PER_FLOW; j++)
+ {
+ trace_record = record->trace_data.path_data + j;
+ if (trace_record->is_free)
+ continue;
+
+ s = format (s, "path_map:\n%U", format_path_map,
+ trace_record->path, trace_record->num_nodes);
+ s = format (s, "pkt_counter: %u\n", trace_record->pkt_counter);
+ s = format (s, "bytes_counter: %u\n", trace_record->bytes_counter);
+
+ s = format (s, "min_delay: %u\n", trace_record->min_delay);
+ s = format (s, "max_delay: %u\n", trace_record->max_delay);
+ s = format (s, "mean_delay: %u\n", trace_record->mean_delay);
+ }
+
+ s = format (s, "\nPOT data: \n");
+ s = format (s, "sfc_validated_count : %u\n",
+ record->pot_data.sfc_validated_count);
+ s = format (s, "sfc_invalidated_count : %u\n",
+ record->pot_data.sfc_invalidated_count);
+
+ s = format (s, "\nSeqno Data:\n");
+ s = format (s,
+ "RX Packets : %lu\n"
+ "Lost Packets : %lu\n"
+ "Duplicate Packets : %lu\n"
+ "Reordered Packets : %lu\n",
+ record->seqno_data.rx_packets,
+ record->seqno_data.lost_packets,
+ record->seqno_data.dup_packets,
+ record->seqno_data.reordered_packets);
+
+ s = format (s, "\n");
+ return s;
+}
+
+always_inline void
+ioam_analyse_init_data (ioam_analyser_data_t * data)
+{
+ u16 j;
+ ioam_analyse_trace_data *trace_data;
+
+ data->is_free = 1;
+
+ /* We maintain data corresponding to last IP-Fix export, this may
+ * get extended in future to maintain history of data */
+ vec_validate_aligned (data->chached_data_list, 0, CLIB_CACHE_LINE_BYTES);
+
+ data->writer_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
+ CLIB_CACHE_LINE_BYTES);
+ *(data->writer_lock) = 0;
+
+ trace_data = &(data->trace_data);
+ for (j = 0; j < IOAM_MAX_PATHS_PER_FLOW; j++)
+ trace_data->path_data[j].is_free = 1;
+}
+
+#endif /* PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IOAM_ANALYSE_H_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/analyse/ioam_summary_export.c b/src/plugins/ioam/analyse/ioam_summary_export.c
new file mode 100644
index 00000000..af2d39ab
--- /dev/null
+++ b/src/plugins/ioam/analyse/ioam_summary_export.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/ip/ip6_packet.h>
+#include <ioam/analyse/ioam_summary_export.h>
+#include <ioam/analyse/ip6/ip6_ioam_analyse.h>
+
+u8 *
+ioam_template_rewrite (flow_report_main_t * frm, flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address, u16 collector_port)
+{
+ ip4_header_t *ip;
+ udp_header_t *udp;
+ ipfix_message_header_t *h;
+ ipfix_set_header_t *s;
+ ipfix_template_header_t *t;
+ ipfix_field_specifier_t *f;
+ ipfix_field_specifier_t *first_field;
+ u8 *rewrite = 0;
+ ip4_ipfix_template_packet_t *tp;
+ u32 field_count = 0;
+ u32 field_index = 0;
+ flow_report_stream_t *stream;
+
+ stream = &frm->streams[fr->stream_index];
+
+ /* Determine field count */
+#define _(field,mask,item,length) \
+ { \
+ field_count++; \
+ fr->fields_to_send = clib_bitmap_set (fr->fields_to_send, \
+ field_index, 1); \
+ } \
+ field_index++;
+
+ foreach_ioam_ipfix_field;
+#undef _
+
+ /* Add Src address, dest address, src port, dest port
+ * path map, number of paths manually */
+ field_count += 6;
+
+ /* allocate rewrite space */
+ vec_validate_aligned (rewrite,
+ sizeof (ip4_ipfix_template_packet_t)
+ + field_count * sizeof (ipfix_field_specifier_t) - 1,
+ CLIB_CACHE_LINE_BYTES);
+
+ tp = (ip4_ipfix_template_packet_t *) rewrite;
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+ h = (ipfix_message_header_t *) (udp + 1);
+ s = (ipfix_set_header_t *) (h + 1);
+ t = (ipfix_template_header_t *) (s + 1);
+ first_field = f = (ipfix_field_specifier_t *) (t + 1);
+
+ ip->ip_version_and_header_length = 0x45;
+ ip->ttl = 254;
+ ip->protocol = IP_PROTOCOL_UDP;
+ ip->src_address.as_u32 = src_address->as_u32;
+ ip->dst_address.as_u32 = collector_address->as_u32;
+ udp->src_port = clib_host_to_net_u16 (collector_port);
+ udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
+ udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
+
+ h->domain_id = clib_host_to_net_u32 (stream->domain_id); //fr->domain_id);
+
+ /* Add Src address, dest address, src port, dest port
+ * path map, number of paths manually */
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
+ sourceIPv6Address,
+ sizeof (ip6_address_t));
+ f++;
+
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
+ destinationIPv6Address,
+ sizeof (ip6_address_t));
+ f++;
+
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
+ sourceTransportPort, 2);
+ f++;
+
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
+ destinationTransportPort, 2);
+ f++;
+
+#define _(field,mask,item,length) \
+ { \
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */, \
+ item, length); \
+ f++; \
+ }
+ foreach_ioam_ipfix_field;
+#undef _
+
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
+ ioamNumberOfPaths, 2);
+ f++;
+
+ /* Add ioamPathMap manually */
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
+ ioamPathMap,
+ (sizeof (ioam_path) +
+ (sizeof (ioam_path_map_t) *
+ IOAM_TRACE_MAX_NODES)));
+ f++;
+
+ /* Back to the template packet... */
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+
+ ASSERT (f - first_field);
+ /* Field count in this template */
+ t->id_count = ipfix_id_count (IOAM_FLOW_TEMPLATE_ID, f - first_field);
+
+ /* set length in octets */
+ s->set_id_length =
+ ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
+
+ /* message length in octets */
+ h->version_length = version_length ((u8 *) f - (u8 *) h);
+
+ ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
+ ip->checksum = ip4_header_checksum (ip);
+
+ return rewrite;
+}
+
+u16
+ioam_analyse_add_ipfix_record (flow_report_t * fr,
+ ioam_analyser_data_t * record,
+ vlib_buffer_t * b0, u16 offset,
+ ip6_address_t * src, ip6_address_t * dst,
+ u16 src_port, u16 dst_port)
+{
+ while (__sync_lock_test_and_set (record->writer_lock, 1))
+ ;
+
+ int field_index = 0;
+ u16 tmp;
+ int i, j;
+ u16 num_paths = 0;
+ u16 num_paths_offset;
+
+
+ /* Add IPv6 source address manually */
+ memcpy (b0->data + offset, &src->as_u64[0], sizeof (u64));
+ offset += sizeof (u64);
+ memcpy (b0->data + offset, &src->as_u64[1], sizeof (u64));
+ offset += sizeof (u64);
+
+ /* Add IPv6 destination address manually */
+ memcpy (b0->data + offset, &dst->as_u64[0], sizeof (u64));
+ offset += sizeof (u64);
+ memcpy (b0->data + offset, &dst->as_u64[1], sizeof (u64));
+ offset += sizeof (u64);
+
+ /* Add source port manually */
+ tmp = clib_host_to_net_u16 (src_port);
+ memcpy (b0->data + offset, &tmp, sizeof (u16));
+ offset += sizeof (u16);
+
+ /* Add dest port manually */
+ tmp = clib_host_to_net_u16 (dst_port);
+ memcpy (b0->data + offset, &tmp, sizeof (u16));
+ offset += sizeof (u16);
+
+#define _(field,mask,item,length) \
+ if (clib_bitmap_get (fr->fields_to_send, field_index)) \
+ { \
+ /* Expect only 4 bytes */ \
+ u32 tmp; \
+ tmp = clib_host_to_net_u32((u32)record->field - (u32)record->chached_data_list->field);\
+ memcpy (b0->data + offset, &tmp, length); \
+ offset += length; \
+ }
+ field_index++;
+ foreach_ioam_ipfix_field;
+#undef _
+
+ /* Store num_paths_offset here and update later */
+ num_paths_offset = offset;
+ offset += sizeof (u16);
+
+ /* Add ioamPathMap manually */
+ for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
+ {
+ ioam_analyse_trace_record *trace = record->trace_data.path_data + i;
+ ioam_analyse_trace_record *trace_cached =
+ record->chached_data_list->trace_data.path_data + i;
+ ioam_path *path = (ioam_path *) (b0->data + offset);
+
+ if (!trace->is_free)
+ {
+ num_paths++;
+
+ path->num_nodes = trace->num_nodes;
+
+ path->trace_type = trace->trace_type;
+ if (0 < (trace->pkt_counter - trace_cached->pkt_counter))
+ {
+ u64 new_sum = trace->mean_delay * record->seqno_data.rx_packets;
+ u64 old_sum =
+ trace_cached->mean_delay *
+ record->chached_data_list->seqno_data.rx_packets;
+ path->mean_delay =
+ (u32) ((new_sum - old_sum) / (trace->pkt_counter -
+ trace_cached->pkt_counter));
+ path->mean_delay = clib_host_to_net_u32 (path->mean_delay);
+ }
+ else
+ path->mean_delay = 0;
+
+ path->bytes_counter =
+ trace->bytes_counter - trace_cached->bytes_counter;
+ path->bytes_counter = clib_host_to_net_u32 (path->bytes_counter);
+
+ path->pkt_counter = trace->pkt_counter - trace_cached->pkt_counter;
+ path->pkt_counter = clib_host_to_net_u32 (path->pkt_counter);
+ offset += sizeof (ioam_path);
+
+ for (j = 0; j < trace->num_nodes; j++)
+ {
+ path->path[j].node_id =
+ clib_host_to_net_u32 (trace->path[j].node_id);
+ path->path[j].ingress_if =
+ clib_host_to_net_u16 (trace->path[j].ingress_if);
+ path->path[j].egress_if =
+ clib_host_to_net_u16 (trace->path[j].egress_if);
+ path->path[j].state_up = trace->path[j].state_up;
+ }
+
+ //offset += (sizeof(ioam_path_map_t) * trace->num_nodes);
+ offset += (sizeof (ioam_path_map_t) * IOAM_TRACE_MAX_NODES); //FIXME
+ }
+ }
+
+ num_paths = clib_host_to_net_u16 (num_paths);
+ memcpy (b0->data + num_paths_offset, &num_paths, sizeof (u16));
+
+ /* Update cache */
+ *(record->chached_data_list) = *record;
+ record->chached_data_list->chached_data_list = NULL;
+
+ *(record->writer_lock) = 0;
+ return offset;
+}
+
+vlib_frame_t *
+ioam_send_flows (flow_report_main_t * frm, flow_report_t * fr,
+ vlib_frame_t * f, u32 * to_next, u32 node_index)
+{
+ vlib_buffer_t *b0 = NULL;
+ u32 next_offset = 0;
+ u32 bi0 = ~0;
+ int i;
+ ip4_ipfix_template_packet_t *tp;
+ ipfix_message_header_t *h;
+ ipfix_set_header_t *s = NULL;
+ ip4_header_t *ip;
+ udp_header_t *udp;
+ u32 records_this_buffer;
+ u16 new_l0, old_l0;
+ ip_csum_t sum0;
+ vlib_main_t *vm = frm->vlib_main;
+ ip6_address_t temp;
+ ioam_analyser_data_t *record = NULL;
+ flow_report_stream_t *stream;
+ ioam_analyser_data_t *aggregated_data;
+ u16 data_len;
+
+ stream = &frm->streams[fr->stream_index];
+
+ memset (&temp, 0, sizeof (ip6_address_t));
+
+ aggregated_data = ioam_analyser_main.aggregated_data;
+ data_len = vec_len (aggregated_data);
+
+ vec_foreach_index (i, aggregated_data)
+ {
+ u8 flush = 0;
+ record = aggregated_data + i;
+
+ /* Flush if last entry */
+ if (i == (data_len - 1))
+ flush = 1;
+
+ if (!record->is_free)
+ {
+
+ if (PREDICT_FALSE (b0 == NULL))
+ {
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ break;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ memcpy (b0->data, fr->rewrite, vec_len (fr->rewrite));
+ b0->current_data = 0;
+ b0->current_length = vec_len (fr->rewrite);
+ b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
+
+ tp = vlib_buffer_get_current (b0);
+ ip = &tp->ip4;
+ h = &tp->ipfix.h;
+ s = &tp->ipfix.s;
+
+ /* FIXUP: message header export_time */
+ h->export_time = clib_host_to_net_u32 (((u32) time (NULL)));
+
+ /* FIXUP: message header sequence_number */
+ h->sequence_number = stream->sequence_number++;
+ h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
+ next_offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
+ records_this_buffer = 0;
+ }
+
+ next_offset = ioam_analyse_add_ipfix_record (fr, record,
+ b0, next_offset,
+ &temp, &temp, 0, 0);
+ records_this_buffer++;
+
+ /* Flush data if packet len is about to reach path mtu */
+ if (next_offset > (frm->path_mtu - 250))
+ flush = 1;
+ }
+
+ if (PREDICT_FALSE (flush && b0))
+ {
+ s->set_id_length = ipfix_set_id_length (IOAM_FLOW_TEMPLATE_ID,
+ next_offset - (sizeof (*ip) +
+ sizeof (*udp) +
+ sizeof (*h)));
+ b0->current_length = next_offset;
+ b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
+ tp = vlib_buffer_get_current (b0);
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+
+ sum0 = ip->checksum;
+ old_l0 = ip->length;
+ new_l0 = clib_host_to_net_u16 ((u16) next_offset);
+ sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
+ length /* changed member */ );
+
+ ip->checksum = ip_csum_fold (sum0);
+ ip->length = new_l0;
+ udp->length =
+ clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
+
+ if (frm->udp_checksum)
+ {
+ /* RFC 7011 section 10.3.2. */
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+ }
+
+ to_next[0] = bi0;
+ f->n_vectors++;
+ to_next++;
+
+ if (f->n_vectors == VLIB_FRAME_SIZE)
+ {
+ vlib_put_frame_to_node (vm, node_index, f);
+ f = vlib_get_frame_to_node (vm, node_index);
+ f->n_vectors = 0;
+ to_next = vlib_frame_vector_args (f);
+ }
+ b0 = 0;
+ bi0 = ~0;
+ }
+ }
+
+ return f;
+}
+
+clib_error_t *
+ioam_flow_create (u8 del)
+{
+ vnet_flow_report_add_del_args_t args;
+ int rv;
+ u32 domain_id = 0;
+ flow_report_main_t *frm = &flow_report_main;
+ u16 template_id;
+
+ memset (&args, 0, sizeof (args));
+ args.rewrite_callback = ioam_template_rewrite;
+ args.flow_data_callback = ioam_send_flows;
+ del ? (args.is_add = 0) : (args.is_add = 1);
+ args.domain_id = domain_id;
+
+ rv = vnet_flow_report_add_del (frm, &args, &template_id);
+
+ switch (rv)
+ {
+ case 0:
+ break;
+ case VNET_API_ERROR_NO_SUCH_ENTRY:
+ return clib_error_return (0, "registration not found...");
+ default:
+ return clib_error_return (0, "vnet_flow_report_add_del returned %d",
+ rv);
+ }
+
+ return 0;
+}
+
+clib_error_t *
+ioam_flow_report_init (vlib_main_t * vm)
+{
+ clib_error_t *error;
+
+ if ((error = vlib_call_init_function (vm, flow_report_init)))
+ return error;
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ioam_flow_report_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/analyse/ioam_summary_export.h b/src/plugins/ioam/analyse/ioam_summary_export.h
new file mode 100755
index 00000000..b4355061
--- /dev/null
+++ b/src/plugins/ioam/analyse/ioam_summary_export.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_ip6_ioam_flow_report_h__
+#define __included_ip6_ioam_flow_report_h__
+
+#include <ioam/analyse/ioam_analyse.h>
+#include <vnet/flow/flow_report.h>
+
+#define foreach_ioam_ipfix_info_element \
+_(ioamPacketSent, 5239, u32) \
+_(ioamPacketCount, 5237, u32) \
+_(ioamByteCount, 5238, u32) \
+_(ioamPathMap, 5262, u32) \
+_(ioamNumberOfPaths, 5264, u16) \
+_(ioamSfcValidatedCount, 5278, u32) \
+_(ioamSfcInValidatedCount, 5279, u32) \
+_(ioamSeqnoRxCount, 5280, u32) \
+_(ioamSeqnoLostCount, 5281, u32) \
+_(ioamSeqnoReorderedCount, 5282, u32) \
+_(ioamSeqnoDupCount, 5283, u32)
+
+
+typedef enum
+{
+#define _(n,v,t) n = v,
+ foreach_ioam_ipfix_info_element
+#undef _
+} ioam_ipfix_info_element_id_t;
+
+#define foreach_ioam_ipfix_field \
+_(pkt_sent, 0xffffffff, ioamPacketSent, 4) \
+_(pkt_counter, 0xffffffff, ioamPacketCount, 4) \
+_(bytes_counter, 0xffffffff, ioamByteCount, 4) \
+_(pot_data.sfc_validated_count, 0xffffffff, ioamSfcValidatedCount, 4) \
+_(pot_data.sfc_invalidated_count, 0xffffffff, ioamSfcInValidatedCount, 4) \
+_(seqno_data.rx_packets, 0xffffffff, ioamSeqnoRxCount, 4) \
+_(seqno_data.lost_packets, 0xffffffff, ioamSeqnoLostCount, 4) \
+_(seqno_data.reordered_packets, 0xffffffff, ioamSeqnoReorderedCount, 4) \
+_(seqno_data.dup_packets, 0xffffffff, ioamSeqnoDupCount, 4)
+
+clib_error_t *ioam_flow_report_init (vlib_main_t * vm);
+
+typedef struct
+{
+ u8 num_nodes;
+ u8 trace_type;
+ u16 reserve;
+ u32 mean_delay;
+ u32 pkt_counter;
+ u32 bytes_counter;
+ ioam_path_map_t path[0];
+} ioam_path;
+
+clib_error_t *ioam_flow_create (u8 del);
+
+u8 *ioam_template_rewrite (flow_report_main_t * frm, flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address, u16 collector_port);
+
+u16 ioam_analyse_add_ipfix_record (flow_report_t * fr,
+ ioam_analyser_data_t * record,
+ vlib_buffer_t * b0, u16 offset,
+ ip6_address_t * src, ip6_address_t * dst,
+ u16 src_port, u16 dst_port);
+
+#endif /* __included_ip6_ioam_flow_report_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.c b/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.c
new file mode 100644
index 00000000..39442b62
--- /dev/null
+++ b/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ioam/analyse/ioam_analyse.h>
+#include <ioam/export-common/ioam_export.h>
+#include <ioam/analyse/ip6/ip6_ioam_analyse.h>
+#include <ioam/analyse/ioam_summary_export.h>
+#include <vnet/ip/ip.h>
+#include <ioam/ipfixcollector/ipfixcollector.h>
+
+extern ioam_export_main_t ioam_export_main;
+static clib_error_t *
+ioam_analyse_enable_disable (vlib_main_t * vm,
+ int is_add, int is_export, int remote_listen)
+{
+ ipfix_client_add_del_t ipfix_reg;
+ clib_error_t *rv = 0;
+
+ ipfix_reg.client_name = format (0, "ip6-hbh-analyse-remote");
+ ipfix_reg.client_node = analyse_node_remote.index;
+ ipfix_reg.ipfix_setid = IPFIX_IOAM_EXPORT_ID;
+
+ if (is_export)
+ {
+ rv = ioam_flow_create (!is_add);
+ if (rv)
+ goto ret;
+ }
+
+ if (is_add)
+ {
+ ip6_ioam_analyse_register_handlers ();
+ if (remote_listen)
+ {
+ ipfix_reg.del = 0;
+ ipfix_collector_reg_setid (vm, &ipfix_reg);
+ }
+ else
+ {
+ ioam_export_set_next_node (&ioam_export_main,
+ (u8 *) "ip6-hbh-analyse-local");
+ }
+ }
+ else
+ {
+ ip6_ioam_analyse_unregister_handlers ();
+ if (remote_listen)
+ {
+ ipfix_reg.del = 1;
+ ipfix_collector_reg_setid (vm, &ipfix_reg);
+ }
+ else
+ ioam_export_reset_next_node (&ioam_export_main);
+ }
+
+ret:
+ vec_free (ipfix_reg.client_name);
+ return rv;
+}
+
+static clib_error_t *
+set_ioam_analyse_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ int is_export = 0;
+ int is_add = 1;
+ int remote_listen = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "export-ipfix-collector"))
+ is_export = 1;
+ else if (unformat (input, "disable"))
+ is_add = 0;
+ else if (unformat (input, "listen-ipfix"))
+ remote_listen = 1;
+ else
+ break;
+ }
+
+ return (ioam_analyse_enable_disable (vm, is_add, is_export, remote_listen));
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_ioam_analyse_command, static) = {
+ .path = "set ioam analyse",
+ .short_help = "set ioam analyse [export-ipfix-collector] [disable] [listen-ipfix]",
+ .function = set_ioam_analyse_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+show_ioam_analyse_cmd_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
+ ioam_analyser_data_t *record = NULL;
+ u8 i;
+ u8 *s = 0;
+
+ vec_reset_length (s);
+ s = format (0, "iOAM Analyse Information: \n");
+ vec_foreach_index (i, am->aggregated_data)
+ {
+ record = am->aggregated_data + i;
+ if (record->is_free)
+ continue;
+
+ s = format (s, "Flow Number: %u\n", i);
+ s = print_analyse_flow (s, record);
+ s = format (s, "\n");
+ }
+ vlib_cli_output (vm, "%v", s);
+
+ vec_free (s);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (ip6_show_ioam_ipfix_cmd, static) = {
+ .path = "show ioam analyse ",
+ .short_help = "show ioam analyser information",
+ .function = show_ioam_analyse_cmd_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+ioam_analyse_init (vlib_main_t * vm)
+{
+ ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
+ u16 i;
+
+ vec_validate_aligned (am->aggregated_data, 50, CLIB_CACHE_LINE_BYTES);
+ vec_foreach_index (i, am->aggregated_data)
+ {
+ ioam_analyse_init_data (am->aggregated_data + i);
+ }
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ioam_analyse_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.h b/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.h
new file mode 100644
index 00000000..5a2a2d70
--- /dev/null
+++ b/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IP6_IOAM_ANALYSE_NODE_H_
+#define PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IP6_IOAM_ANALYSE_NODE_H_
+
+#include <ioam/analyse/ioam_analyse.h>
+#include <vnet/ip/ip6_hop_by_hop.h>
+#include <ioam/encap/ip6_ioam_trace.h>
+
+/** @brief IP6-iOAM analyser main structure.
+ @note cache aligned.
+*/
+typedef struct
+{
+ /** Array of function pointer to analyse each hop-by-hop option. */
+ int (*analyse_hbh_handler[MAX_IP6_HBH_OPTION]) (u32 flow_id,
+ ip6_hop_by_hop_option_t *
+ opt, u16 len);
+
+ /** This contains the aggregated data from the time VPP started analysing. */
+ ioam_analyser_data_t *aggregated_data;
+
+} ip6_ioam_analyser_main_t;
+
+extern ip6_ioam_analyser_main_t ioam_analyser_main;
+
+extern vlib_node_registration_t analyse_node_local;
+extern vlib_node_registration_t analyse_node_remote;
+
+void ip6_ioam_analyse_register_handlers (void);
+
+void ip6_ioam_analyse_unregister_handlers (void);
+
+clib_error_t *ip6_ioam_analyse_init (vlib_main_t * vm);
+
+inline static ioam_analyser_data_t *
+ioam_analyse_get_data_from_flow_id (u32 flow_id)
+{
+ if (flow_id >= vec_len (ioam_analyser_main.aggregated_data))
+ return NULL;
+
+ if (ioam_analyser_main.aggregated_data[flow_id].is_free)
+ ioam_analyser_main.aggregated_data[flow_id].is_free = 0;
+
+ return (ioam_analyser_main.aggregated_data + flow_id);
+}
+
+always_inline void *
+ip6_ioam_find_hbh_option (ip6_hop_by_hop_header_t * hbh0, u8 option)
+{
+ ip6_hop_by_hop_option_t *opt0, *limit0;
+ u8 type0;
+
+ opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
+ limit0 =
+ (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
+
+ while (opt0 < limit0)
+ {
+ type0 = opt0->type;
+ if (type0 == option)
+ return ((void *) opt0);
+
+ if (0 == type0)
+ {
+ opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
+ continue;
+ }
+ opt0 = (ip6_hop_by_hop_option_t *)
+ (((u8 *) opt0) + opt0->length + sizeof (ip6_hop_by_hop_option_t));
+ }
+
+ return NULL;
+}
+
+always_inline int
+ip6_ioam_analyse_compare_path_delay (ip6_hop_by_hop_header_t * hbh0,
+ ip6_hop_by_hop_header_t * hbh1,
+ bool oneway)
+{
+ ioam_trace_option_t *trace0 = NULL, *trace1 = NULL;
+ f64 delay0, delay1;
+
+ trace0 =
+ ip6_ioam_find_hbh_option (hbh0, HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
+ trace1 =
+ ip6_ioam_find_hbh_option (hbh1, HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
+
+ if (PREDICT_FALSE ((trace0 == NULL) && (trace1 == NULL)))
+ return 0;
+
+ if (PREDICT_FALSE (trace1 == NULL))
+ return 1;
+
+ if (PREDICT_FALSE (trace0 == NULL))
+ return -1;
+
+ delay0 = ip6_ioam_analyse_calc_delay (&trace0->trace_hdr,
+ trace0->hdr.length - 2, oneway);
+ delay1 = ip6_ioam_analyse_calc_delay (&trace1->trace_hdr,
+ trace1->hdr.length - 2, oneway);
+
+ return (delay0 - delay1);
+}
+
+#endif /* PLUGINS_IOAM_PLUGIN_IOAM_ANALYSE_IP6_IOAM_ANALYSE_NODE_H_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/analyse/ip6/node.c b/src/plugins/ioam/analyse/ip6/node.c
new file mode 100644
index 00000000..6db6355e
--- /dev/null
+++ b/src/plugins/ioam/analyse/ip6/node.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/ip.h>
+#include <ioam/export-common/ioam_export.h>
+#include <ioam/encap/ip6_ioam_trace.h>
+#include <ioam/encap/ip6_ioam_pot.h>
+#include <ioam/lib-pot/pot_util.h>
+#include <ioam/encap/ip6_ioam_e2e.h>
+#include <ioam/analyse/ioam_analyse.h>
+#include <ioam/analyse/ip6/ip6_ioam_analyse.h>
+#include <vnet/plugin/plugin.h>
+
+typedef struct
+{
+ u32 next_index;
+ u32 flow_id;
+} analyse_trace_t;
+
+vlib_node_registration_t analyse_node_local;
+vlib_node_registration_t analyse_node_remote;
+
+#define foreach_analyse_error \
+_(ANALYSED, "Packets analysed for summarization") \
+_(FAILED, "Packets analysis failed") \
+
+typedef enum
+{
+#define _(sym,str) ANALYSE_ERROR_##sym,
+ foreach_analyse_error
+#undef _
+ ANALYSE_N_ERROR,
+} analyse_error_t;
+
+static char *analyse_error_strings[] = {
+#define _(sym,string) string,
+ foreach_analyse_error
+#undef _
+};
+
+typedef enum
+{
+ ANALYSE_NEXT_IP4_LOOKUP,
+ ANALYSE_NEXT_IP4_DROP,
+ ANALYSE_N_NEXT,
+} analyse_next_t;
+
+ip6_ioam_analyser_main_t ioam_analyser_main;
+
+/* packet trace format function */
+static u8 *
+format_analyse_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ analyse_trace_t *t = va_arg (*args, analyse_trace_t *);
+
+ s = format (s, "IP6-ioam-analyse: flow_id %d, next index %d",
+ t->flow_id, t->next_index);
+ return s;
+}
+
+always_inline u8
+ioam_analyse_hbh (u32 flow_id,
+ ip6_hop_by_hop_header_t * hbh0,
+ ip6_hop_by_hop_option_t * opt0,
+ ip6_hop_by_hop_option_t * limit0, u16 len)
+{
+ ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
+ u8 type0;
+ u8 error0 = 0;
+
+ while (opt0 < limit0)
+ {
+ type0 = opt0->type;
+ switch (type0)
+ {
+ case 0: /* Pad1 */
+ opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
+ continue;
+ case 1: /* PadN */
+ break;
+ default:
+ if (am->analyse_hbh_handler[type0])
+ {
+ if (PREDICT_TRUE
+ ((*am->analyse_hbh_handler[type0]) (flow_id, opt0,
+ len) < 0))
+ {
+ error0 = ANALYSE_ERROR_FAILED;
+ return (error0);
+ }
+ }
+ }
+ opt0 =
+ (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
+ sizeof (ip6_hop_by_hop_option_t));
+ }
+ return (error0);
+}
+
+/**
+ * @brief IPv6 InBandOAM Analyse node.
+ * @node ip6-hbh-analyse-local, ip6-hbh-analyse-remote
+ *
+ * This function receives IP-FIX packets containing IPv6-iOAM records, analyses
+ * them and collects/aggregates the statistics.
+ *
+ * @param vm vlib_main_t corresponding to the current thread.
+ * @param node vlib_node_runtime_t data for this node.
+ * @param frame vlib_frame_t whose contents should be dispatched.
+ *
+ * @par Graph mechanics: buffer, next index usage
+ *
+ * <em>Uses:</em>
+ * - <code>vlib_buffer_get_current(p0)</code>
+ * - Walks on each ioam record present in IP-Fix record, analyse them and
+ * store the statistics.
+ *
+ * <em>Next Index:</em>
+ * - Dispatches the packet to ip4-lookup if executed under ip6-hbh-analyse-local
+ * node context and to ip4-drop if executed under ip6-hbh-analyse-remote node
+ * context.
+ */
+static uword
+ip6_ioam_analyse_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ analyse_next_t next_index;
+ u32 pkts_analysed = 0;
+ u32 pkts_failed = 0;
+ u8 remote = 0;
+ u32 next0 = ANALYSE_NEXT_IP4_LOOKUP;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ if (PREDICT_FALSE (analyse_node_remote.index == node->node_index))
+ {
+ remote = 1;
+ next0 = ANALYSE_NEXT_IP4_DROP;
+ }
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *p0;
+ ip4_header_t *ip40;
+ u8 *data, *limit;
+ u16 num_ioam_records;
+
+ /* speculatively enqueue p0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, bi0);
+ if (PREDICT_FALSE (remote))
+ {
+ vlib_buffer_advance (p0, -(word) (sizeof (udp_header_t) +
+ sizeof (ip4_header_t) +
+ sizeof
+ (ipfix_message_header_t) +
+ sizeof (ipfix_set_header_t)));
+ }
+ data = (u8 *) vlib_buffer_get_current (p0);
+ ip40 = (ip4_header_t *) vlib_buffer_get_current (p0);
+ limit = data + clib_net_to_host_u16 (ip40->length);
+ data += sizeof (ip4_header_t) + sizeof (udp_header_t)
+ + sizeof (ipfix_message_header_t) + sizeof (ipfix_set_header_t);
+
+ num_ioam_records = (limit - data) / DEFAULT_EXPORT_SIZE;
+
+ while (num_ioam_records >= 4)
+ {
+ /* Prefetch next 2 ioam records */
+ {
+ CLIB_PREFETCH (data + (2 * DEFAULT_EXPORT_SIZE),
+ (DEFAULT_EXPORT_SIZE), LOAD);
+ CLIB_PREFETCH (data + (3 * DEFAULT_EXPORT_SIZE),
+ (DEFAULT_EXPORT_SIZE), LOAD);
+ }
+
+ num_ioam_records -= 2;
+
+ ip6_header_t *ip60, *ip61;
+ ip6_hop_by_hop_header_t *hbh0, *hbh1;
+ ip6_hop_by_hop_option_t *opt0, *limit0, *opt1, *limit1;
+ u32 flow_id0, flow_id1;
+ u8 error0, error1;
+ ioam_analyser_data_t *data0, *data1;
+ u16 p_len0, p_len1;
+
+ ip60 = (ip6_header_t *) data;
+ ip61 = (ip6_header_t *) (data + DEFAULT_EXPORT_SIZE);
+
+ data += (2 * DEFAULT_EXPORT_SIZE);
+
+ hbh0 = (ip6_hop_by_hop_header_t *) (ip60 + 1);
+ hbh1 = (ip6_hop_by_hop_header_t *) (ip61 + 1);
+
+ opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
+ opt1 = (ip6_hop_by_hop_option_t *) (hbh1 + 1);
+
+ limit0 =
+ (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 +
+ ((hbh0->length + 1) << 3));
+ limit1 =
+ (ip6_hop_by_hop_option_t *) ((u8 *) hbh1 +
+ ((hbh1->length + 1) << 3));
+
+ flow_id0 =
+ clib_net_to_host_u32
+ (ip60->ip_version_traffic_class_and_flow_label) & 0xFFFFF;
+ flow_id1 =
+ clib_net_to_host_u32
+ (ip61->ip_version_traffic_class_and_flow_label) & 0xFFFFF;
+
+ p_len0 = clib_net_to_host_u16 (ip60->payload_length);
+ p_len1 = clib_net_to_host_u16 (ip61->payload_length);
+
+ error0 =
+ ioam_analyse_hbh (flow_id0, hbh0, opt0, limit0, p_len0);
+ error1 =
+ ioam_analyse_hbh (flow_id1, hbh1, opt1, limit1, p_len0);
+
+ if (PREDICT_TRUE ((error0 == 0) && (error1 == 0)))
+ {
+ pkts_analysed += 2;
+ data0 = ioam_analyse_get_data_from_flow_id (flow_id0);
+ data1 = ioam_analyse_get_data_from_flow_id (flow_id1);
+
+ while (__sync_lock_test_and_set (data0->writer_lock, 1))
+ ;
+ data0->pkt_counter++;
+ data0->bytes_counter += p_len0;
+ *(data0->writer_lock) = 0;
+
+ while (__sync_lock_test_and_set (data1->writer_lock, 1))
+ ;
+ data1->pkt_counter++;
+ data1->bytes_counter += p_len1;
+ *(data1->writer_lock) = 0;
+ }
+ else if (error0 == 0)
+ {
+ pkts_analysed++;
+ pkts_failed++;
+
+ data0 = ioam_analyse_get_data_from_flow_id (flow_id0);
+ while (__sync_lock_test_and_set (data0->writer_lock, 1))
+ ;
+ data0->pkt_counter++;
+ data0->bytes_counter += p_len0;
+ *(data0->writer_lock) = 0;
+ }
+ else if (error1 == 0)
+ {
+ pkts_analysed++;
+ pkts_failed++;
+
+ data1 = ioam_analyse_get_data_from_flow_id (flow_id1);
+ while (__sync_lock_test_and_set (data1->writer_lock, 1))
+ ;
+ data1->pkt_counter++;
+ data1->bytes_counter += p_len1;
+ *(data1->writer_lock) = 0;
+ }
+ else
+ pkts_failed += 2;
+ }
+
+ while (num_ioam_records > 0)
+ {
+ num_ioam_records--;
+
+ ip6_header_t *ip60;
+ ip6_hop_by_hop_header_t *hbh0;
+ ip6_hop_by_hop_option_t *opt0, *limit0;
+ u32 flow_id0;
+ u8 error0;
+ ioam_analyser_data_t *data0;
+ u16 p_len0;
+
+ ip60 = (ip6_header_t *) data;
+ data += (1 * DEFAULT_EXPORT_SIZE);
+ hbh0 = (ip6_hop_by_hop_header_t *) (ip60 + 1);
+ opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
+ limit0 =
+ (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 +
+ ((hbh0->length + 1) << 3));
+
+ flow_id0 =
+ clib_net_to_host_u32
+ (ip60->ip_version_traffic_class_and_flow_label) & 0xFFFFF;
+ p_len0 = clib_net_to_host_u16 (ip60->payload_length);
+ error0 =
+ ioam_analyse_hbh (flow_id0, hbh0, opt0, limit0, p_len0);
+
+ if (PREDICT_TRUE (error0 == 0))
+ {
+ pkts_analysed++;
+ data0 = ioam_analyse_get_data_from_flow_id (flow_id0);
+ while (__sync_lock_test_and_set (data0->writer_lock, 1))
+ ;
+ data0->pkt_counter++;
+ data0->bytes_counter +=
+ clib_net_to_host_u16 (ip60->payload_length);
+ *(data0->writer_lock) = 0;
+ }
+ else
+ pkts_failed++;
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ vlib_node_increment_counter (vm, node->node_index, ANALYSE_ERROR_ANALYSED,
+ pkts_analysed);
+
+ if (PREDICT_FALSE (pkts_failed))
+ vlib_node_increment_counter (vm, node->node_index, ANALYSE_ERROR_FAILED,
+ pkts_failed);
+
+ return frame->n_vectors;
+}
+
+int
+ip6_ioam_analyse_hbh_trace_internal (u32 flow_id,
+ ip6_hop_by_hop_option_t * opt, u16 len)
+{
+ ioam_analyser_data_t *data;
+ ioam_trace_option_t *trace = (ioam_trace_option_t *) opt;
+
+ data = ioam_analyse_get_data_from_flow_id (flow_id);
+ ASSERT (data != NULL);
+
+ (void) ip6_ioam_analyse_hbh_trace (data, &trace->trace_hdr, len,
+ (trace->hdr.length - 2)
+ /*ioam_trace_type,data_list_elts_left */
+ );
+ return 0;
+}
+
+int
+ip6_ioam_analyse_hbh_pot (u32 flow_id, ip6_hop_by_hop_option_t * opt0,
+ u16 len)
+{
+
+ ioam_pot_option_t *pot0;
+ u64 random = 0;
+ u64 cumulative = 0;
+ pot_profile *pot_profile = 0;
+ int ret;
+ ioam_analyser_data_t *data;
+
+ data = ioam_analyse_get_data_from_flow_id (flow_id);
+
+ pot0 = (ioam_pot_option_t *) opt0;
+ random = clib_net_to_host_u64 (pot0->random);
+ cumulative = clib_net_to_host_u64 (pot0->cumulative);
+ pot_profile = pot_profile_get_active ();
+ ret = pot_validate (pot_profile, cumulative, random);
+
+ while (__sync_lock_test_and_set (data->writer_lock, 1))
+ ;
+
+ (0 == ret) ? (data->pot_data.sfc_validated_count++) :
+ (data->pot_data.sfc_invalidated_count++);
+
+ *(data->writer_lock) = 0;
+ return 0;
+}
+
+int
+ip6_ioam_analyse_hbh_e2e_internal (u32 flow_id, ip6_hop_by_hop_option_t * opt,
+ u16 len)
+{
+ ioam_analyser_data_t *data;
+ ioam_e2e_option_t *e2e;
+
+ data = ioam_analyse_get_data_from_flow_id (flow_id);
+ e2e = (ioam_e2e_option_t *) opt;
+ ip6_ioam_analyse_hbh_e2e (data, &e2e->e2e_hdr, len);
+ return 0;
+}
+
+int
+ip6_ioam_analyse_register_hbh_handler (u8 option,
+ int options (u32 flow_id,
+ ip6_hop_by_hop_option_t *
+ opt, u16 len))
+{
+ ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
+
+ ASSERT (option < ARRAY_LEN (am->analyse_hbh_handler));
+
+ /* Already registered */
+ if (am->analyse_hbh_handler[option])
+ return (-1);
+
+ am->analyse_hbh_handler[option] = options;
+
+ return (0);
+}
+
+int
+ip6_ioam_analyse_unregister_hbh_handler (u8 option)
+{
+ ip6_ioam_analyser_main_t *am = &ioam_analyser_main;
+
+ ASSERT (option < ARRAY_LEN (am->analyse_hbh_handler));
+
+ /* Not registered */
+ if (!am->analyse_hbh_handler[option])
+ return (-1);
+
+ am->analyse_hbh_handler[option] = NULL;
+ return (0);
+}
+
+void
+ip6_ioam_analyse_register_handlers ()
+{
+ ip6_ioam_analyse_register_hbh_handler (HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST,
+ ip6_ioam_analyse_hbh_trace_internal);
+ ip6_ioam_analyse_register_hbh_handler
+ (HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT, ip6_ioam_analyse_hbh_pot);
+ ip6_ioam_analyse_register_hbh_handler (HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE,
+ ip6_ioam_analyse_hbh_e2e_internal);
+}
+
+void
+ip6_ioam_analyse_unregister_handlers ()
+{
+ ip6_ioam_analyse_unregister_hbh_handler
+ (HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
+ ip6_ioam_analyse_unregister_hbh_handler
+ (HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT);
+ ip6_ioam_analyse_unregister_hbh_handler (HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE);
+}
+
+/* *INDENT-OFF* */
+
+/*
+ * Node for IP6 analyse - packets
+ */
+VLIB_REGISTER_NODE (analyse_node_local) = {
+ .function = ip6_ioam_analyse_node_fn,
+ .name = "ip6-hbh-analyse-local",
+ .vector_size = sizeof (u32),
+ .format_trace = format_analyse_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (analyse_error_strings),
+ .error_strings = analyse_error_strings,
+ .n_next_nodes = ANALYSE_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [ANALYSE_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [ANALYSE_NEXT_IP4_DROP] = "ip4-drop",
+ },
+};
+
+/*
+ * Node for IP6 analyse - packets
+ */
+VLIB_REGISTER_NODE (analyse_node_remote) =
+{
+ .function = ip6_ioam_analyse_node_fn,
+ .name = "ip6-hbh-analyse-remote",
+ .vector_size = sizeof (u32),
+ .format_trace = format_analyse_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (analyse_error_strings),
+ .error_strings = analyse_error_strings,
+ .n_next_nodes = ANALYSE_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [ANALYSE_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [ANALYSE_NEXT_IP4_DROP] = "ip4-drop",
+ },
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */