diff options
author | AkshayaNadahalli <anadahal@cisco.com> | 2016-12-01 16:33:51 +0530 |
---|---|---|
committer | Neale Ranns <nranns@cisco.com> | 2017-02-13 08:36:59 +0000 |
commit | fdd81af6afe6c782ad2c1a139210378badec626b (patch) | |
tree | b562b391bded3afc0f5b4d0f83eceb20f2c8e772 /src/plugins/ioam | |
parent | fed79e83910459ed700615d2a5a24024f835a66c (diff) |
VPP-632 : InBand OAM Analyser
Refer to jira ticket for more details.
Change-Id: I6facb9ef8553a21464f9a2e612706f152badbb68
Signed-off-by: AkshayaNadahalli <anadahal@cisco.com>
Diffstat (limited to 'src/plugins/ioam')
27 files changed, 2738 insertions, 289 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..a416cb1a --- /dev/null +++ b/src/plugins/ioam/analyse/ioam_analyse.h @@ -0,0 +1,398 @@ +/* + * 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> + +#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; +} 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) +{ + u16 size_of_traceopt_per_node, size_of_all_traceopts; + u8 num_nodes; + u32 *start_elt, *end_elt; + u32 start_time, end_time; + + size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type); + 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); + + num_nodes -= trace->data_list_elts_left; + + start_elt = trace->elts; + end_elt = + trace->elts + + (size_of_traceopt_per_node * (num_nodes - 1) / sizeof (u32)); + + 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 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); + 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: + 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); + 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)); + } + + *(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\n", + pm->node_id, pm->ingress_if, pm->egress_if); + 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..00567323 --- /dev/null +++ b/src/plugins/ioam/analyse/ioam_summary_export.c @@ -0,0 +1,437 @@ +/* + * 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, (1 * sizeof (ioam_path))); + 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); + + for (j = 0; j < IOAM_TRACE_MAX_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); + } + + offset += sizeof (ioam_path); + } + } + + 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; + udp = &tp->udp; + 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; + + 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); + + 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..9be31d2e --- /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[IOAM_TRACE_MAX_NODES]; +} 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..c22ef0f4 --- /dev/null +++ b/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.c @@ -0,0 +1,162 @@ +/* + * 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> + +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..f6abdce3 --- /dev/null +++ b/src/plugins/ioam/analyse/ip6/ip6_ioam_analyse.h @@ -0,0 +1,68 @@ +/* + * 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> + +/** @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); +} + +#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: + */ diff --git a/src/plugins/ioam/encap/ip6_ioam_e2e.c b/src/plugins/ioam/encap/ip6_ioam_e2e.c index 2831a351..cdaf740d 100644 --- a/src/plugins/ioam/encap/ip6_ioam_e2e.c +++ b/src/plugins/ioam/encap/ip6_ioam_e2e.c @@ -37,7 +37,7 @@ static u8 * ioam_e2e_trace_handler (u8 * s, if (e2e) { - seqno = clib_net_to_host_u32 (e2e->e2e_data); + seqno = clib_net_to_host_u32 (e2e->e2e_hdr.e2e_data); } s = format (s, "SeqNo = 0x%Lx", seqno); @@ -108,7 +108,7 @@ ioam_e2e_flow_handler (u32 ctx, u8 add) { pool_get(ioam_e2e_main.e2e_data, data); data->flow_ctx = ctx; - ioam_seqno_init_bitmap(&data->seqno_data); + ioam_seqno_init_data(&data->seqno_data); return ((u32) (data - ioam_e2e_main.e2e_data)); } diff --git a/src/plugins/ioam/encap/ip6_ioam_e2e.h b/src/plugins/ioam/encap/ip6_ioam_e2e.h index 18f35f80..fcec4a14 100644 --- a/src/plugins/ioam/encap/ip6_ioam_e2e.h +++ b/src/plugins/ioam/encap/ip6_ioam_e2e.h @@ -16,8 +16,16 @@ #ifndef __included_ip6_ioam_e2e_h__ #define __included_ip6_ioam_e2e_h__ +#include "../lib-e2e/e2e_util.h" #include "ip6_ioam_seqno.h" +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct { + ip6_hop_by_hop_option_t hdr; + ioam_e2e_packet_t e2e_hdr; +}) ioam_e2e_option_t; +/* *INDENT-ON* */ + typedef struct ioam_e2e_data_t_ { u32 flow_ctx; u32 pad; @@ -44,4 +52,13 @@ ioam_e2ec_get_seqno_data_from_flow_ctx (u32 flow_ctx) return &(data->seqno_data); } +static inline u32 +ioam_e2e_get_cur_seqno_from_flow_ctx (u32 flow_ctx) +{ + ioam_seqno_data *data = NULL; + + data = ioam_e2ec_get_seqno_data_from_flow_ctx(flow_ctx); + return data->seq_num; +} + #endif /* __included_ioam_e2e_h__ */ diff --git a/src/plugins/ioam/encap/ip6_ioam_pot.c b/src/plugins/ioam/encap/ip6_ioam_pot.c index 05f42c91..9a761233 100644 --- a/src/plugins/ioam/encap/ip6_ioam_pot.c +++ b/src/plugins/ioam/encap/ip6_ioam_pot.c @@ -16,26 +16,15 @@ #include <vnet/vnet.h> #include <vnet/pg/pg.h> #include <vppinfra/error.h> - #include <vnet/ip/ip6.h> -#include <vnet/ip/ip6_hop_by_hop.h> -#include <vnet/ip/ip6_hop_by_hop_packet.h> #include <vppinfra/hash.h> #include <vppinfra/error.h> #include <vppinfra/elog.h> +#include <ioam/encap/ip6_ioam_pot.h> #include <ioam/lib-pot/pot_util.h> -typedef CLIB_PACKED(struct { - ip6_hop_by_hop_option_t hdr; - u8 pot_type; -#define PROFILE_ID_MASK 0xF - u8 reserved_profile_id; /* 4 bits reserved, 4 bits to carry profile id */ - u64 random; - u64 cumulative; -}) ioam_pot_option_t; - #define foreach_ip6_hop_by_hop_ioam_pot_stats \ _(PROCESSED, "Pkts with ip6 hop-by-hop pot options") \ _(PROFILE_MISS, "Pkts with ip6 hop-by-hop pot options but no profile set") \ @@ -180,19 +169,19 @@ ip6_hbh_ioam_proof_of_transit_pop_handler (vlib_buffer_t *b, ip6_header_t *ip, int rv = 0; pot_profile *pot_profile = 0; u8 result = 0; - + 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(); result = pot_validate (pot_profile, cumulative, random); - - if (result == 1) + + if (result == 1) { ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PASSED, 1); } - else + else { ip6_ioam_stats_increment_counter (IP6_IOAM_POT_FAILED, 1); } diff --git a/src/plugins/ioam/encap/ip6_ioam_pot.h b/src/plugins/ioam/encap/ip6_ioam_pot.h new file mode 100644 index 00000000..01ce4ac5 --- /dev/null +++ b/src/plugins/ioam/encap/ip6_ioam_pot.h @@ -0,0 +1,40 @@ +/* + * 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_ENCAP_IP6_IOAM_POT_H_ +#define PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_POT_H_ + +#include <vnet/ip/ip6_hop_by_hop_packet.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct { + ip6_hop_by_hop_option_t hdr; + u8 pot_type; + #define PROFILE_ID_MASK 0xF + u8 reserved_profile_id; /* 4 bits reserved, 4 bits to carry profile id */ + u64 random; + u64 cumulative; +}) ioam_pot_option_t; +/* *INDENT-ON* */ + +#endif /* PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_POT_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/encap/ip6_ioam_seqno.c b/src/plugins/ioam/encap/ip6_ioam_seqno.c index 0b4d4192..01977123 100644 --- a/src/plugins/ioam/encap/ip6_ioam_seqno.c +++ b/src/plugins/ioam/encap/ip6_ioam_seqno.c @@ -28,20 +28,6 @@ #include "ip6_ioam_seqno.h" #include "ip6_ioam_e2e.h" -ioam_seqno_data_main_t ioam_seqno_main; - -void ioam_seqno_init_bitmap (ioam_seqno_data *data) -{ - seqno_bitmap *bitmap = &data->seqno_rx.bitmap; - bitmap->window_size = SEQNO_WINDOW_SIZE; - bitmap->array_size = SEQNO_WINDOW_ARRAY_SIZE; - bitmap->mask = 32 * SEQNO_WINDOW_ARRAY_SIZE - 1; - bitmap->array[0] = 0x00000000;/* pretend we haven seen sequence numbers 0*/ - bitmap->highest = 0; - - data->seq_num = 0; - return ; -} /* * This Routine gets called from IPv6 hop-by-hop option handling. @@ -60,7 +46,7 @@ ioam_seqno_encap_handler (vlib_buffer_t *b, ip6_header_t *ip, data = ioam_e2ec_get_seqno_data_from_flow_ctx(opaque_index); e2e = (ioam_e2e_option_t *) opt; - e2e->e2e_data = clib_host_to_net_u32(++data->seq_num); + e2e->e2e_hdr.e2e_data = clib_host_to_net_u32(++data->seq_num); return (rv); } @@ -79,31 +65,8 @@ ioam_seqno_decap_handler (vlib_buffer_t *b, ip6_header_t *ip, data = ioam_e2ec_get_seqno_data_from_flow_ctx(opaque_index); e2e = (ioam_e2e_option_t *) opt; - ioam_analyze_seqno(&data->seqno_rx, (u64) clib_net_to_host_u32(e2e->e2e_data)); + ioam_analyze_seqno(&data->seqno_rx, + (u64) clib_net_to_host_u32(e2e->e2e_hdr.e2e_data)); return (rv); } - -u8 * -show_ioam_seqno_cmd_fn (u8 *s, ioam_seqno_data *seqno_data, u8 enc) -{ - seqno_rx_info *rx; - - s = format(s, "SeqNo Data:\n"); - if (enc) - { - s = format(s, " Current Seq. Number : %llu\n", seqno_data->seq_num); - } - else - { - rx = &seqno_data->seqno_rx; - s = format(s, " Highest Seq. Number : %llu\n", rx->bitmap.highest); - s = format(s, " Packets received : %llu\n", rx->rx_packets); - s = format(s, " Lost packets : %llu\n", rx->lost_packets); - s = format(s, " Reordered packets : %llu\n", rx->reordered_packets); - s = format(s, " Duplicate packets : %llu\n", rx->dup_packets); - } - - format(s, "\n"); - return s; -} diff --git a/src/plugins/ioam/encap/ip6_ioam_seqno.h b/src/plugins/ioam/encap/ip6_ioam_seqno.h index 13a84db0..5c140246 100644 --- a/src/plugins/ioam/encap/ip6_ioam_seqno.h +++ b/src/plugins/ioam/encap/ip6_ioam_seqno.h @@ -18,42 +18,7 @@ #include <vnet/ip/ip6_packet.h> #include <vnet/ip/ip6_hop_by_hop.h> - -#define SEQ_CHECK_VALUE 0x80000000 /* for seq number wraparound detection */ - -#define SEQNO_WINDOW_SIZE 2048 -#define SEQNO_WINDOW_ARRAY_SIZE 64 - -typedef struct seqno_bitmap_ { - u32 window_size; - u32 array_size; - u32 mask; - u32 pad; - u64 highest; - u64 array[SEQNO_WINDOW_ARRAY_SIZE]; /* Will be alloc to array_size */ -} seqno_bitmap; - -typedef struct seqno_rx_info_ { - u64 rx_packets; - u64 lost_packets; - u64 reordered_packets; - u64 dup_packets; - seqno_bitmap bitmap; -} seqno_rx_info; - -/* This structure is 64-byte aligned */ -typedef struct ioam_seqno_data_ { - union { - u32 seq_num; /* Useful only for encap node */ - seqno_rx_info seqno_rx; - }; -} ioam_seqno_data; - -typedef struct ioam_seqno_data_main_t_ { - ioam_seqno_data *seqno_data; -} ioam_seqno_data_main_t; - -void ioam_seqno_init_bitmap(ioam_seqno_data *data); +#include <ioam/lib-e2e/e2e_util.h> int ioam_seqno_encap_handler(vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt); @@ -62,9 +27,4 @@ int ioam_seqno_decap_handler(vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt); -void ioam_analyze_seqno(seqno_rx_info *ppc_rx, u64 seqno); - -u8 * -show_ioam_seqno_cmd_fn(u8 *s, ioam_seqno_data *seqno_data, u8 enc); - #endif diff --git a/src/plugins/ioam/encap/ip6_ioam_seqno_analyse.c b/src/plugins/ioam/encap/ip6_ioam_seqno_analyse.c deleted file mode 100644 index 4638871c..00000000 --- a/src/plugins/ioam/encap/ip6_ioam_seqno_analyse.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2016 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 <vnet/vnet.h> -#include "ip6_ioam_seqno.h" - -static inline void BIT_SET (u64 *p, u32 n) -{ - p[ n>>5 ] |= (1 << (n&31)); -} - -static inline int BIT_TEST (u64 *p, u32 n) -{ - return p[ n>>5 ] & (1 << (n&31)); -} - -static void BIT_CLEAR (u64 *p, u64 start, int num_bits, u32 mask) -{ - int n, t; - int start_index = (start >> 5); - int mask_index = (mask >> 5); - - start_index &= mask_index; - if (start & 0x1f) - { - int start_bit = (start & 0x1f); - - n = (1 << start_bit)-1; - t = start_bit + num_bits; - if (t < 32) - { - n |= ~((1 << t)-1); - p[ start_index ] &= n; - return; - } - p[ start_index ] &= n; - start_index = (start_index + 1) & mask_index; - num_bits -= (32 - start_bit); - } - while (num_bits >= 32) - { - p[ start_index ] = 0; - start_index = (start_index + 1) & mask_index; - num_bits -= 32; - } - n = ~((1 << num_bits) - 1); - p[ start_index ] &= n; -} - -static inline u8 seqno_check_wraparound(u32 a, u32 b) -{ - if ((a != b) && (a > b) && ((a - b) > SEQ_CHECK_VALUE)) - { - return 1; - } - return 0; -} - -/* - * Function to analyze the PPC value recevied. - * - Updates the bitmap with received sequence number - * - counts the received/lost/duplicate/reordered packets - */ -void ioam_analyze_seqno (seqno_rx_info *seqno_rx, u64 seqno) -{ - int diff; - static int peer_dead_count; - seqno_bitmap *bitmap = &seqno_rx->bitmap; - - seqno_rx->rx_packets++; - - if (seqno > bitmap->highest) - { /* new larger sequence number */ - peer_dead_count = 0; - diff = seqno - bitmap->highest; - if (diff < bitmap->window_size) - { - if (diff > 1) - { /* diff==1 is *such* a common case it's a win to optimize it */ - BIT_CLEAR(bitmap->array, bitmap->highest+1, diff-1, bitmap->mask); - seqno_rx->lost_packets += diff -1; - } - } - else - { - seqno_rx->lost_packets += diff -1; - memset( bitmap->array, 0, bitmap->array_size * sizeof(u64) ); - } - BIT_SET(bitmap->array, seqno & bitmap->mask); - bitmap->highest = seqno; - return; - } - - /* we've seen a bigger seq number before */ - diff = bitmap->highest - seqno; - if (diff >= bitmap->window_size) - { - if (seqno_check_wraparound(bitmap->highest, seqno)) - { - memset( bitmap->array, 0, bitmap->array_size * sizeof(u64)); - BIT_SET(bitmap->array, seqno & bitmap->mask); - bitmap->highest = seqno; - return; - } - else - { - peer_dead_count++; - if (peer_dead_count > 25) - { - peer_dead_count = 0; - memset( bitmap->array, 0, bitmap->array_size * sizeof(u64) ); - BIT_SET(bitmap->array, seqno & bitmap->mask); - bitmap->highest = seqno; - } - //ppc_rx->reordered_packets++; - } - return; - } - - if (BIT_TEST(bitmap->array, seqno & bitmap->mask)) - { - seqno_rx->dup_packets++; - return; /* Already seen */ - } - seqno_rx->reordered_packets++; - seqno_rx->lost_packets--; - BIT_SET(bitmap->array, seqno & bitmap->mask); - return; -} diff --git a/src/plugins/ioam/encap/ip6_ioam_trace.c b/src/plugins/ioam/encap/ip6_ioam_trace.c index a836dfe8..6972ba4b 100644 --- a/src/plugins/ioam/encap/ip6_ioam_trace.c +++ b/src/plugins/ioam/encap/ip6_ioam_trace.c @@ -27,7 +27,7 @@ #include <vppinfra/elog.h> #include <vnet/plugin/plugin.h> -#include <ioam/lib-trace/trace_util.h> +#include <ioam/encap/ip6_ioam_trace.h> /* Timestamp precision multipliers for seconds, milliseconds, microseconds * and nanoseconds respectively. @@ -40,15 +40,6 @@ typedef union u32 as_u32[2]; } time_u64_t; -/* *INDENT-OFF* */ -typedef CLIB_PACKED(struct { - ip6_hop_by_hop_option_t hdr; - u8 ioam_trace_type; - u8 data_list_elts_left; - u32 elts[0]; /* Variable type. So keep it generic */ -}) ioam_trace_option_t; -/* *INDENT-ON* */ - extern ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main; extern ip6_main_t ip6_main; @@ -201,8 +192,9 @@ ip6_hop_by_hop_ioam_trace_rewrite_handler (u8 * rewrite_string, HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE; trace_option->hdr.length = 2 /*ioam_trace_type,data_list_elts_left */ + trace_option_elts * trace_data_size; - trace_option->ioam_trace_type = profile->trace_type & TRACE_TYPE_MASK; - trace_option->data_list_elts_left = trace_option_elts; + trace_option->trace_hdr.ioam_trace_type = + profile->trace_type & TRACE_TYPE_MASK; + trace_option->trace_hdr.data_list_elts_left = trace_option_elts; *rewrite_size = sizeof (ioam_trace_option_t) + (trace_option_elts * trace_data_size); @@ -238,24 +230,24 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip, time_u64.as_u64 = 0; - if (PREDICT_TRUE (trace->data_list_elts_left)) + if (PREDICT_TRUE (trace->trace_hdr.data_list_elts_left)) { - trace->data_list_elts_left--; + trace->trace_hdr.data_list_elts_left--; /* fetch_trace_data_size returns in bytes. Convert it to 4-bytes * to skip to this node's location. */ elt_index = - trace->data_list_elts_left * - fetch_trace_data_size (trace->ioam_trace_type) / 4; - elt = &trace->elts[elt_index]; - if (trace->ioam_trace_type & BIT_TTL_NODEID) + trace->trace_hdr.data_list_elts_left * + fetch_trace_data_size (trace->trace_hdr.ioam_trace_type) / 4; + elt = &trace->trace_hdr.elts[elt_index]; + if (trace->trace_hdr.ioam_trace_type & BIT_TTL_NODEID) { *elt = clib_host_to_net_u32 ((ip->hop_limit << 24) | profile->node_id); elt++; } - if (trace->ioam_trace_type & BIT_ING_INTERFACE) + if (trace->trace_hdr.ioam_trace_type & BIT_ING_INTERFACE) { *elt = (vnet_buffer (b)->sw_if_index[VLIB_RX] & 0xFFFF) << 16 | @@ -264,7 +256,7 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip, elt++; } - if (trace->ioam_trace_type & BIT_TIMESTAMP) + if (trace->trace_hdr.ioam_trace_type & BIT_TIMESTAMP) { /* Send least significant 32 bits */ f64 time_f64 = @@ -276,7 +268,7 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip, elt++; } - if (trace->ioam_trace_type & BIT_APPDATA) + if (trace->trace_hdr.ioam_trace_type & BIT_APPDATA) { /* $$$ set elt0->app_data */ *elt = clib_host_to_net_u32 (profile->app_data); @@ -302,17 +294,19 @@ ip6_hbh_ioam_trace_data_list_trace_handler (u8 * s, trace = (ioam_trace_option_t *) opt; s = - format (s, " Trace Type 0x%x , %d elts left\n", trace->ioam_trace_type, - trace->data_list_elts_left); + format (s, " Trace Type 0x%x , %d elts left\n", + trace->trace_hdr.ioam_trace_type, + trace->trace_hdr.data_list_elts_left); trace_data_size_in_words = - fetch_trace_data_size (trace->ioam_trace_type) / 4; - elt = &trace->elts[0]; - while ((u8 *) elt < ((u8 *) (&trace->elts[0]) + trace->hdr.length - 2 - /* -2 accounts for ioam_trace_type,elts_left */ )) + fetch_trace_data_size (trace->trace_hdr.ioam_trace_type) / 4; + elt = &trace->trace_hdr.elts[0]; + while ((u8 *) elt < + ((u8 *) (&trace->trace_hdr.elts[0]) + trace->hdr.length - 2 + /* -2 accounts for ioam_trace_type,elts_left */ )) { s = format (s, " [%d] %U\n", elt_index, format_ioam_data_list_element, - elt, &trace->ioam_trace_type); + elt, &trace->trace_hdr.ioam_trace_type); elt_index++; elt += trace_data_size_in_words; } diff --git a/src/plugins/ioam/encap/ip6_ioam_trace.h b/src/plugins/ioam/encap/ip6_ioam_trace.h new file mode 100644 index 00000000..b332b319 --- /dev/null +++ b/src/plugins/ioam/encap/ip6_ioam_trace.h @@ -0,0 +1,38 @@ +/* + * 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_ENCAP_IP6_IOAM_TRACE_H_ +#define PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_TRACE_H_ + +#include <vnet/ip/ip6_hop_by_hop_packet.h> +#include <ioam/lib-trace/trace_util.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct { + ip6_hop_by_hop_option_t hdr; + ioam_trace_hdr_t trace_hdr; +}) ioam_trace_option_t; +/* *INDENT-ON* */ + + +#endif /* PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_TRACE_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/export-common/ioam_export.h b/src/plugins/ioam/export-common/ioam_export.h index e6ab0344..e84dab0b 100644 --- a/src/plugins/ioam/export-common/ioam_export.h +++ b/src/plugins/ioam/export-common/ioam_export.h @@ -20,6 +20,7 @@ #include <vnet/ip/ip_packet.h> #include <vnet/ip/ip4_packet.h> #include <vnet/ip/ip6_packet.h> +#include <vnet/ip/ip6_hop_by_hop.h> #include <vnet/ip/udp.h> #include <vnet/flow/ipfix_packet.h> @@ -43,6 +44,7 @@ typedef struct { /* API message ID base */ u16 msg_id_base; + u16 set_id; /* TODO: to support multiple collectors all this has to be grouped and create a vector here */ u8 *record_header; @@ -67,14 +69,15 @@ typedef struct /* convenience */ vlib_main_t *vlib_main; vnet_main_t *vnet_main; - u32 ip4_lookup_node_index; + ethernet_main_t *ethernet_main; + u32 next_node_index; uword my_hbh_slot; u32 export_process_node_index; } ioam_export_main_t; -ioam_export_main_t ioam_export_main; -ioam_export_main_t vxlan_gpe_ioam_export_main; +extern ioam_export_main_t ioam_export_main; +extern ioam_export_main_t vxlan_gpe_ioam_export_main; extern vlib_node_registration_t export_node; extern vlib_node_registration_t vxlan_export_node; @@ -86,6 +89,24 @@ extern vlib_node_registration_t vxlan_export_node; */ #define DEFAULT_EXPORT_RECORDS 7 +inline static void +ioam_export_set_next_node (ioam_export_main_t * em, u8 * next_node_name) +{ + vlib_node_t *next_node; + + next_node = vlib_get_node_by_name (em->vlib_main, next_node_name); + em->next_node_index = next_node->index; +} + +inline static void +ioam_export_reset_next_node (ioam_export_main_t * em) +{ + vlib_node_t *next_node; + + next_node = vlib_get_node_by_name (em->vlib_main, (u8 *) "ip4-lookup"); + em->next_node_index = next_node->index; +} + always_inline ioam_export_buffer_t * ioam_export_get_my_buffer (ioam_export_main_t * em, u32 thread_id) { @@ -154,15 +175,13 @@ ioam_export_thread_buffer_init (ioam_export_main_t * em, vlib_main_t * vm) int no_of_threads = vec_len (vlib_worker_threads); int i; ioam_export_buffer_t *eb = 0; - vlib_node_t *ip4_lookup_node; pool_alloc_aligned (em->buffer_pool, no_of_threads - 1, CLIB_CACHE_LINE_BYTES); vec_validate_aligned (em->buffer_per_thread, no_of_threads - 1, CLIB_CACHE_LINE_BYTES); vec_validate_aligned (em->lockp, no_of_threads - 1, CLIB_CACHE_LINE_BYTES); - ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup"); - em->ip4_lookup_node_index = ip4_lookup_node->index; + if (!em->buffer_per_thread || !em->buffer_pool || !em->lockp) { return (-1); @@ -186,6 +205,7 @@ ioam_export_thread_buffer_init (ioam_export_main_t * em, vlib_main_t * vm) } #define IPFIX_IOAM_EXPORT_ID 272 +#define IPFIX_VXLAN_IOAM_EXPORT_ID 273 /* Used to build the rewrite */ /* data set packet */ @@ -241,8 +261,8 @@ ioam_export_header_create (ioam_export_main_t * em, 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 (4939 /* $$FIXME */ ); - udp->dst_port = clib_host_to_net_u16 (4939); + udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); + udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); /* FIXUP: UDP length */ udp->length = clib_host_to_net_u16 (vec_len (rewrite) + (DEFAULT_EXPORT_RECORDS * @@ -253,7 +273,7 @@ ioam_export_header_create (ioam_export_main_t * em, h->domain_id = clib_host_to_net_u32 (em->domain_id); /*FIXUP: Setid length in octets if records exported are not default */ - s->set_id_length = ipfix_set_id_length (IPFIX_IOAM_EXPORT_ID, + s->set_id_length = ipfix_set_id_length (em->set_id, (sizeof (*s) + (DEFAULT_EXPORT_RECORDS * DEFAULT_EXPORT_SIZE))); @@ -309,11 +329,10 @@ ioam_export_send_buffer (ioam_export_main_t * em, vlib_main_t * vm, /* FIXUP: lengths if different from default */ if (PREDICT_FALSE (eb->records_in_this_buffer != DEFAULT_EXPORT_RECORDS)) { - s->set_id_length = - ipfix_set_id_length (IPFIX_IOAM_EXPORT_ID /* set_id */ , - b0->current_length - (sizeof (*ip) + - sizeof (*udp) + - sizeof (*h))); + s->set_id_length = ipfix_set_id_length (em->set_id /* set_id */ , + b0->current_length - + (sizeof (*ip) + sizeof (*udp) + + sizeof (*h))); h->version_length = version_length (b0->current_length - (sizeof (*ip) + sizeof (*udp))); sum0 = ip->checksum; @@ -328,12 +347,12 @@ ioam_export_send_buffer (ioam_export_main_t * em, vlib_main_t * vm, /* Enqueue pkts to ip4-lookup */ - nf = vlib_get_frame_to_node (vm, em->ip4_lookup_node_index); + nf = vlib_get_frame_to_node (vm, em->next_node_index); nf->n_vectors = 0; to_next = vlib_frame_vector_args (nf); nf->n_vectors = 1; to_next[0] = eb->buffer_index; - vlib_put_frame_to_node (vm, em->ip4_lookup_node_index, nf); + vlib_put_frame_to_node (vm, em->next_node_index, nf); return (1); } @@ -452,7 +471,7 @@ ioam_export_process_common (ioam_export_main_t * em, vlib_main_t * vm, return 0; /* not so much */ } -#define ioam_export_node_common(EM, VM, N, F, HTYPE, L, V, NEXT) \ +#define ioam_export_node_common(EM, VM, N, F, HTYPE, L, V, NEXT, FIXUP_FUNC) \ do { \ u32 n_left_from, *from, *to_next; \ export_next_t next_index; \ @@ -510,6 +529,7 @@ do { \ ip_len1 = \ ip_len1 > DEFAULT_EXPORT_SIZE ? DEFAULT_EXPORT_SIZE : ip_len1; \ copy3cachelines (eb0->data + eb0->current_length, ip0, ip_len0); \ + FIXUP_FUNC(eb0, p0); \ eb0->current_length += DEFAULT_EXPORT_SIZE; \ my_buf->records_in_this_buffer++; \ if (my_buf->records_in_this_buffer >= DEFAULT_EXPORT_RECORDS) \ @@ -522,6 +542,7 @@ do { \ if (PREDICT_FALSE (eb0 == 0)) \ goto NO_BUFFER1; \ copy3cachelines (eb0->data + eb0->current_length, ip1, ip_len1); \ + FIXUP_FUNC(eb0, p1); \ eb0->current_length += DEFAULT_EXPORT_SIZE; \ my_buf->records_in_this_buffer++; \ if (my_buf->records_in_this_buffer >= DEFAULT_EXPORT_RECORDS) \ @@ -578,6 +599,7 @@ do { \ ip_len0 = \ ip_len0 > DEFAULT_EXPORT_SIZE ? DEFAULT_EXPORT_SIZE : ip_len0; \ copy3cachelines (eb0->data + eb0->current_length, ip0, ip_len0); \ + FIXUP_FUNC(eb0, p0); \ eb0->current_length += DEFAULT_EXPORT_SIZE; \ my_buf->records_in_this_buffer++; \ if (my_buf->records_in_this_buffer >= DEFAULT_EXPORT_RECORDS) \ diff --git a/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_ioam_export.c b/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_ioam_export.c index 0924d68f..f05b5303 100644 --- a/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_ioam_export.c +++ b/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_ioam_export.c @@ -83,6 +83,8 @@ do { \ #define foreach_vxlan_gpe_ioam_export_plugin_api_msg \ _(VXLAN_GPE_IOAM_EXPORT_ENABLE_DISABLE, vxlan_gpe_ioam_export_enable_disable) +ioam_export_main_t vxlan_gpe_ioam_export_main; + extern void vxlan_gpe_set_next_override (uword next); /* Action function shared between message handler and debug CLI */ int @@ -242,6 +244,8 @@ vxlan_gpe_ioam_export_init (vlib_main_t * vm) clib_error_t *error = 0; u8 *name; + em->set_id = IPFIX_VXLAN_IOAM_EXPORT_ID; + name = format (0, "vxlan_gpe_ioam_export_%08x%c", api_version, 0); /* Ask for a correctly-sized block of API message decode slots */ @@ -254,6 +258,7 @@ vxlan_gpe_ioam_export_init (vlib_main_t * vm) em->my_hbh_slot = ~0; em->vlib_main = vm; em->vnet_main = vnet_get_main (); + ioam_export_reset_next_node (em); vec_free (name); return error; diff --git a/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_node.c b/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_node.c index f75b7081..8120f4a7 100644 --- a/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_node.c +++ b/src/plugins/ioam/export-vxlan-gpe/vxlan_gpe_node.c @@ -17,9 +17,9 @@ #include <vnet/pg/pg.h> #include <vppinfra/error.h> #include <vnet/ip/ip.h> -#include <ioam/export-common/ioam_export.h> #include <vnet/vxlan-gpe/vxlan_gpe.h> #include <vnet/vxlan-gpe/vxlan_gpe_packet.h> +#include <ioam/export-common/ioam_export.h> typedef struct { @@ -121,6 +121,12 @@ copy3cachelines (void *dst, const void *src, size_t n) #endif } +static void +vxlan_gpe_export_fixup_func (vlib_buffer_t * export_buf, + vlib_buffer_t * pak_buf) +{ + /* Todo: on implementing VXLAN GPE analyse */ +} static uword vxlan_gpe_export_node_fn (vlib_main_t * vm, @@ -129,7 +135,8 @@ vxlan_gpe_export_node_fn (vlib_main_t * vm, ioam_export_main_t *em = &vxlan_gpe_ioam_export_main; ioam_export_node_common (em, vm, node, frame, ip4_header_t, length, ip_version_and_header_length, - EXPORT_NEXT_VXLAN_GPE_INPUT); + EXPORT_NEXT_VXLAN_GPE_INPUT, + vxlan_gpe_export_fixup_func); return frame->n_vectors; } diff --git a/src/plugins/ioam/export/ioam_export.c b/src/plugins/ioam/export/ioam_export.c index 06634baa..1112ce4f 100644 --- a/src/plugins/ioam/export/ioam_export.c +++ b/src/plugins/ioam/export/ioam_export.c @@ -81,6 +81,10 @@ do { \ #define foreach_ioam_export_plugin_api_msg \ _(IOAM_EXPORT_IP6_ENABLE_DISABLE, ioam_export_ip6_enable_disable) +ioam_export_main_t ioam_export_main; + +vlib_node_registration_t export_node; + /* Action function shared between message handler and debug CLI */ int @@ -232,6 +236,8 @@ ioam_export_init (vlib_main_t * vm) em->vlib_main = vm; em->vnet_main = vnet_get_main (); + em->set_id = IPFIX_IOAM_EXPORT_ID; + ioam_export_reset_next_node (em); name = format (0, "ioam_export_%08x%c", api_version, 0); diff --git a/src/plugins/ioam/export/node.c b/src/plugins/ioam/export/node.c index 19f143df..b32d7841 100644 --- a/src/plugins/ioam/export/node.c +++ b/src/plugins/ioam/export/node.c @@ -17,8 +17,10 @@ #include <vnet/pg/pg.h> #include <vppinfra/error.h> #include <vnet/ip/ip.h> +#include <vnet/ip/ip6_hop_by_hop.h> #include <ioam/export-common/ioam_export.h> + typedef struct { u32 next_index; @@ -119,6 +121,20 @@ copy3cachelines (void *dst, const void *src, size_t n) #endif } +static void +ip6_export_fixup_func (vlib_buffer_t * export_buf, vlib_buffer_t * pak_buf) +{ + ip6_header_t *ip6_temp = + (ip6_header_t *) (export_buf->data + export_buf->current_length); + u32 flow_label_temp = + clib_net_to_host_u32(ip6_temp->ip_version_traffic_class_and_flow_label) + & 0xFFF00000; + flow_label_temp |= + IOAM_MASK_DECAP_BIT((vnet_buffer(pak_buf)->l2_classify.opaque_index)); + ip6_temp->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32(flow_label_temp); +} + static uword ip6_export_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) @@ -126,7 +142,7 @@ ip6_export_node_fn (vlib_main_t * vm, ioam_export_main_t *em = &ioam_export_main; ioam_export_node_common(em, vm, node, frame, ip6_header_t, payload_length, ip_version_traffic_class_and_flow_label, - EXPORT_NEXT_POP_HBYH); + EXPORT_NEXT_POP_HBYH, ip6_export_fixup_func); return frame->n_vectors; } diff --git a/src/plugins/ioam/ipfixcollector/ipfixcollector.c b/src/plugins/ioam/ipfixcollector/ipfixcollector.c new file mode 100644 index 00000000..4ae47edc --- /dev/null +++ b/src/plugins/ioam/ipfixcollector/ipfixcollector.c @@ -0,0 +1,105 @@ +/* + * 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 <vnet/ip/ip.h> +#include <vnet/plugin/plugin.h> +#include <vnet/ip/udp.h> +#include <ioam/ipfixcollector/ipfixcollector.h> + +ipfix_collector_main_t ipfix_collector_main; + +/** + * @brief IP-FIX SetID registration function. + * + * This function can be used by other VPP graph nodes to receive IP-FIX packets + * with a particular setid. + * + * @param vlib_main_t Vlib main of the graph node which is interseted in + * getting IP-Fix packet. + * @param ipfix_client_add_del_t Structure describing the client node which + * is interested in getting the IP-Fix packets for + * a SetID. + * + * @returns 0 on success. + * @returns Error codes(<0) otherwise. + */ +int +ipfix_collector_reg_setid (vlib_main_t * vm, ipfix_client_add_del_t * info) +{ + ipfix_collector_main_t *cm = &ipfix_collector_main; + uword *p = NULL; + int i; + ipfix_client *client = 0; + + if ((!info) || (!info->client_name)) + return IPFIX_COLLECTOR_ERR_INVALID_PARAM; + + p = hash_get (cm->client_reg_table, info->ipfix_setid); + client = p ? pool_elt_at_index (cm->client_reg_pool, (*p)) : NULL; + + if (info->del) + { + if (!client) + return 0; //There is no registered handler, so send success + + hash_unset (cm->client_reg_table, info->ipfix_setid); + vec_free (client->client_name); + pool_put (cm->client_reg_pool, client); + return 0; + } + + if (client) + return IPFIX_COLLECTOR_ERR_REG_EXISTS; + + pool_get (cm->client_reg_pool, client); + i = client - cm->client_reg_pool; + client->client_name = vec_dup (info->client_name); + client->client_node = info->client_node; + client->client_next_node = vlib_node_add_next (vm, + ipfix_collector_node.index, + client->client_node); + client->set_id = info->ipfix_setid; + + hash_set (cm->client_reg_table, info->ipfix_setid, i); + return 0; +} + +static clib_error_t * +ipfix_collector_init (vlib_main_t * vm) +{ + clib_error_t *error = 0; + ipfix_collector_main_t *cm = &ipfix_collector_main; + + cm->vlib_main = vm; + cm->vnet_main = vnet_get_main (); + + cm->client_reg_pool = NULL; + cm->client_reg_table = hash_create (0, sizeof (uword)); + + udp_register_dst_port (vm, + UDP_DST_PORT_ipfix, + ipfix_collector_node.index, 1 /* is_ip4 */ ); + return error; +} + +VLIB_INIT_FUNCTION (ipfix_collector_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/ipfixcollector/ipfixcollector.h b/src/plugins/ioam/ipfixcollector/ipfixcollector.h new file mode 100644 index 00000000..ee570316 --- /dev/null +++ b/src/plugins/ioam/ipfixcollector/ipfixcollector.h @@ -0,0 +1,124 @@ +/* + * 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_IPFIXCOLLECTOR_PLUGIN_IPFIXCOLLECTOR_IPFIXCOLLECTOR_H_ +#define PLUGINS_IPFIXCOLLECTOR_PLUGIN_IPFIXCOLLECTOR_IPFIXCOLLECTOR_H_ + +#include <vppinfra/pool.h> +#include <vppinfra/hash.h> +#include <vppinfra/error.h> + +#define IPFIX_COLLECTOR_CLIENT_NAME_MAX 64 + +#define IPFIX_COLLECTOR_ERR_INVALID_PARAM -1 +#define IPFIX_COLLECTOR_ERR_REG_EXISTS -2 + +/** @brief Structure other nodes to use for registering with IP-FIX collector. +*/ +typedef struct +{ + /** String containing name of the client interested in getting + ip-fix packets. */ + u8 *client_name; + + /** Node index where packets have to be redirected. */ + u32 client_node; + + /** Setid of IPFix for which client is intereseted in getting packets. */ + u16 ipfix_setid; + + /** Add(0) or del(1) operation. */ + u16 del; +} ipfix_client_add_del_t; + +/** @brief IP-FIX collector internal client structure to store SetID to + client node ID. +*/ +typedef struct +{ + /** String containing name of the client interested in getting + ip-fix packets. */ + u8 *client_name; + + /** Node index where packets have to be redirected. */ + u32 client_node; + + /** ipfix-collector next index where packets have to be redirected. */ + u32 client_next_node; + + /** Setid of IPFix for which client is intereseted in getting packets. */ + u16 set_id; +} ipfix_client; + +/** @brief IP-FIX collector main structure to SetID to client node ID mapping. + @note cache aligned. +*/ +typedef struct +{ + /** Hash table to map IP-FIX setid to a client registration pool. SetId is + key to hash map. */ + uword *client_reg_table; + + /** Pool of Client node information for the IP-FIX SetID. */ + ipfix_client *client_reg_pool; + + /** Pointer to VLib main for the node - ipfix-collector. */ + vlib_main_t *vlib_main; + + /** Pointer to vnet main for convenience. */ + vnet_main_t *vnet_main; +} ipfix_collector_main_t; + +extern vlib_node_registration_t ipfix_collector_node; + +extern ipfix_collector_main_t ipfix_collector_main; + +/** + * @brief IP-FIX SetID registration function. + * + * This function can be used by other VPP graph nodes to receive IP-FIX packets + * with a particular setid. + * + * @param vlib_main_t Vlib main of the graph node which is interseted in + * getting IP-Fix packet. + * @param ipfix_client_add_del_t Structure describing the client node which + * is interested in getting the IP-Fix packets for + * a SetID. + * + * @returns 0 on success. + * @returns Error codes(<0) otherwise. + */ +int +ipfix_collector_reg_setid (vlib_main_t * vm, ipfix_client_add_del_t * info); + +always_inline ipfix_client * +ipfix_collector_get_client (u16 set_id) +{ + ipfix_collector_main_t *cm = &ipfix_collector_main; + uword *p; + + p = hash_get (cm->client_reg_table, set_id); + return (p ? pool_elt_at_index (cm->client_reg_pool, (*p)) : NULL); +} + +#endif /* PLUGINS_IPFIXCOLLECTOR_PLUGIN_IPFIXCOLLECTOR_IPFIXCOLLECTOR_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/ipfixcollector/node.c b/src/plugins/ioam/ipfixcollector/node.c new file mode 100644 index 00000000..fce997ae --- /dev/null +++ b/src/plugins/ioam/ipfixcollector/node.c @@ -0,0 +1,301 @@ +/* + * 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 <vnet/pg/pg.h> +#include <vppinfra/error.h> +#include <ioam/ipfixcollector/ipfixcollector.h> +#include <vnet/flow/ipfix_packet.h> + +#define foreach_ipfix_collector_error \ +_(PROCESSED, "Number of IP-Fix packets processed") \ +_(NO_LISTENER, "Number of IP-Fix packets with no listener") + +typedef enum +{ +#define _(sym,str) IPFIX_COLLECTOR_ERROR_##sym, + foreach_ipfix_collector_error +#undef _ + IPFIX_COLLECTOR_N_ERROR, +} flowperpkt_error_t; + +static char *ipfix_collector_error_strings[] = { +#define _(sym,string) string, + foreach_ipfix_collector_error +#undef _ +}; + +typedef enum +{ + IPFIX_COLLECTOR_NEXT_DROP, + IPFIX_COLLECTOR_N_NEXT, +} ipfix_collector_next_t; + +typedef struct +{ + u32 next_node; + u16 set_id; + u16 pad; +} ipfix_collector_trace_t; + +vlib_node_registration_t ipfix_collector_node; + +/* packet trace format function */ +static u8 * +format_ipfix_collector_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 *); + ipfix_collector_trace_t *t = va_arg (*args, ipfix_collector_trace_t *); + + s = format (s, + "IPFIX_COLLECTOR: set_id %u, next_node %u", t->set_id, + t->next_node); + return s; +} + +/** + * @brief Node to receive IP-Fix packets. + * @node ipfix-collector + * + * This function receives IP-FIX packets and forwards them to other graph nodes + * based on SetID field in IP-FIX. + * + * @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> + * - Parses IP-Fix packet to extract SetId which will be used to decide + * next node where packets should be enqueued. + * + * <em>Next Index:</em> + * - Dispatches the packet to other VPP graph nodes based on their registartion + * for the IP-Fix SetId using API ipfix_collector_reg_setid(). + */ +uword +ipfix_collector_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, *from, *to_next; + word n_no_listener = 0; + word n_listener = 0; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + u32 next0, next1; + ipfix_message_header_t *ipfix0, *ipfix1; + ipfix_set_header_t *set0, *set1; + u16 set_id0, set_id1; + ipfix_client *client0, *client1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, + (sizeof (ipfix_message_header_t) + + sizeof (ipfix_set_header_t)), LOAD); + CLIB_PREFETCH (p3->data, + (sizeof (ipfix_message_header_t) + + sizeof (ipfix_set_header_t)), LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + ipfix0 = vlib_buffer_get_current (b0); + ipfix1 = vlib_buffer_get_current (b1); + + set0 = (ipfix_set_header_t *) (ipfix0 + 1); + set1 = (ipfix_set_header_t *) (ipfix1 + 1); + + set_id0 = (u16) (clib_net_to_host_u32 (set0->set_id_length) >> 16); + set_id1 = (u16) (clib_net_to_host_u32 (set1->set_id_length) >> 16); + + client0 = ipfix_collector_get_client (set_id0); + client1 = ipfix_collector_get_client (set_id1); + + if (PREDICT_TRUE (NULL != client0)) + { + next0 = client0->client_next_node; + n_listener++; + } + else + { + next0 = IPFIX_COLLECTOR_NEXT_DROP; + n_no_listener++; + } + + if (PREDICT_TRUE (NULL != client1)) + { + next1 = client1->client_next_node; + n_listener++; + } + else + { + next1 = IPFIX_COLLECTOR_NEXT_DROP; + n_no_listener++; + } + + vlib_buffer_advance (b0, + (sizeof (ipfix_message_header_t) + + sizeof (ipfix_set_header_t))); + vlib_buffer_advance (b1, + (sizeof (ipfix_message_header_t) + + sizeof (ipfix_set_header_t))); + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ipfix_collector_trace_t *tr = vlib_add_trace (vm, node, + b0, sizeof (*tr)); + tr->next_node = (client0 ? client0->client_node : 0xFFFFFFFF); + tr->set_id = set_id0; + } + if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) + { + ipfix_collector_trace_t *tr = vlib_add_trace (vm, node, + b1, sizeof (*tr)); + tr->next_node = (client1 ? client1->client_node : 0xFFFFFFFF); + tr->set_id = set_id1; + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + ipfix_message_header_t *ipfix0; + ipfix_set_header_t *set0; + u16 set_id0; + ipfix_client *client0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ipfix0 = vlib_buffer_get_current (b0); + + set0 = (ipfix_set_header_t *) (ipfix0 + 1); + + set_id0 = (u16) (clib_net_to_host_u32 (set0->set_id_length) >> 16); + + client0 = ipfix_collector_get_client (set_id0); + + if (PREDICT_TRUE (NULL != client0)) + { + next0 = client0->client_next_node; + n_listener++; + } + else + { + next0 = IPFIX_COLLECTOR_NEXT_DROP; + n_no_listener++; + } + + vlib_buffer_advance (b0, + (sizeof (ipfix_message_header_t) + + sizeof (ipfix_set_header_t))); + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ipfix_collector_trace_t *tr = vlib_add_trace (vm, node, + b0, sizeof (*tr)); + tr->next_node = (client0 ? client0->client_node : 0xFFFFFFFF); + tr->set_id = set_id0; + } + + 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_error_count (vm, node->node_index, + IPFIX_COLLECTOR_ERROR_NO_LISTENER, n_no_listener); + vlib_error_count (vm, node->node_index, + IPFIX_COLLECTOR_ERROR_PROCESSED, n_listener); + return from_frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ipfix_collector_node) = { + .function = ipfix_collector_node_fn, + .name = "ipfix-collector", + .vector_size = sizeof (u32), + .format_trace = format_ipfix_collector_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(ipfix_collector_error_strings), + .error_strings = ipfix_collector_error_strings, + + .n_next_nodes = IPFIX_COLLECTOR_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [IPFIX_COLLECTOR_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/lib-e2e/e2e_util.h b/src/plugins/ioam/lib-e2e/e2e_util.h new file mode 100644 index 00000000..f8a4ebd4 --- /dev/null +++ b/src/plugins/ioam/lib-e2e/e2e_util.h @@ -0,0 +1,37 @@ +/* + * 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_LIB_E2E_E2E_UTIL_H_ +#define PLUGINS_IOAM_PLUGIN_IOAM_LIB_E2E_E2E_UTIL_H_ + +#include <ioam/lib-e2e/ioam_seqno_lib.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct { + u8 e2e_type; + u8 reserved; + u32 e2e_data; +}) ioam_e2e_packet_t; +/* *INDENT-ON* */ + +#endif /* PLUGINS_IOAM_PLUGIN_IOAM_LIB_E2E_E2E_UTIL_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/lib-e2e/ioam_seqno_lib.c b/src/plugins/ioam/lib-e2e/ioam_seqno_lib.c new file mode 100644 index 00000000..bf78c1e3 --- /dev/null +++ b/src/plugins/ioam/lib-e2e/ioam_seqno_lib.c @@ -0,0 +1,84 @@ +/* + * 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 <ioam/lib-e2e/ioam_seqno_lib.h> + +u8 * +show_ioam_seqno_cmd_fn (u8 * s, ioam_seqno_data * seqno_data, u8 enc) +{ + seqno_rx_info *rx; + + s = format (s, "SeqNo Data:\n"); + if (enc) + { + s = format (s, " Current Seq. Number : %llu\n", seqno_data->seq_num); + } + else + { + rx = &seqno_data->seqno_rx; + s = show_ioam_seqno_analyse_data_fn (s, rx); + } + + format (s, "\n"); + return s; +} + +u8 * +show_ioam_seqno_analyse_data_fn (u8 * s, seqno_rx_info * rx) +{ + s = format (s, " Highest Seq. Number : %llu\n", rx->bitmap.highest); + s = format (s, " Packets received : %llu\n", rx->rx_packets); + s = format (s, " Lost packets : %llu\n", rx->lost_packets); + s = format (s, " Reordered packets : %llu\n", rx->reordered_packets); + s = format (s, " Duplicate packets : %llu\n", rx->dup_packets); + + format (s, "\n"); + return s; +} + +void +ioam_seqno_init_data (ioam_seqno_data * data) +{ + data->seq_num = 0; + ioam_seqno_init_rx_info (&data->seqno_rx); + return; +} + +void +ioam_seqno_init_rx_info (seqno_rx_info * data) +{ + seqno_bitmap *bitmap = &data->bitmap; + bitmap->window_size = SEQNO_WINDOW_SIZE; + bitmap->array_size = SEQNO_WINDOW_ARRAY_SIZE; + bitmap->mask = 32 * SEQNO_WINDOW_ARRAY_SIZE - 1; + bitmap->array[0] = 0x00000000; /* pretend we haven seen sequence numbers 0 */ + bitmap->highest = 0; + + data->dup_packets = 0; + data->lost_packets = 0; + data->reordered_packets = 0; + data->rx_packets = 0; + return; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/lib-e2e/ioam_seqno_lib.h b/src/plugins/ioam/lib-e2e/ioam_seqno_lib.h new file mode 100644 index 00000000..6bd38ff2 --- /dev/null +++ b/src/plugins/ioam/lib-e2e/ioam_seqno_lib.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2016 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_LIB_E2E_IOAM_SEQNO_LIB_H_ +#define PLUGINS_IOAM_PLUGIN_IOAM_LIB_E2E_IOAM_SEQNO_LIB_H_ + +#include <vppinfra/types.h> + +#define SEQ_CHECK_VALUE 0x80000000 /* for seq number wraparound detection */ + +#define SEQNO_WINDOW_SIZE 2048 +#define SEQNO_WINDOW_ARRAY_SIZE 64 + +typedef struct seqno_bitmap_ +{ + u32 window_size; + u32 array_size; + u32 mask; + u32 pad; + u64 highest; + u64 array[SEQNO_WINDOW_ARRAY_SIZE]; /* Will be alloc to array_size */ +} seqno_bitmap; + +typedef struct seqno_rx_info_ +{ + u64 rx_packets; + u64 lost_packets; + u64 reordered_packets; + u64 dup_packets; + seqno_bitmap bitmap; +} seqno_rx_info; + +/* This structure is 64-byte aligned */ +typedef struct ioam_seqno_data_ +{ + union + { + u32 seq_num; /* Useful only for encap node */ + seqno_rx_info seqno_rx; + }; +} ioam_seqno_data; + +static inline void +BIT_SET (u64 * p, u32 n) +{ + p[n >> 5] |= (1 << (n & 31)); +} + +static inline int +BIT_TEST (u64 * p, u32 n) +{ + return p[n >> 5] & (1 << (n & 31)); +} + +static void +BIT_CLEAR (u64 * p, u64 start, int num_bits, u32 mask) +{ + int n, t; + int start_index = (start >> 5); + int mask_index = (mask >> 5); + + start_index &= mask_index; + if (start & 0x1f) + { + int start_bit = (start & 0x1f); + + n = (1 << start_bit) - 1; + t = start_bit + num_bits; + if (t < 32) + { + n |= ~((1 << t) - 1); + p[start_index] &= n; + return; + } + p[start_index] &= n; + start_index = (start_index + 1) & mask_index; + num_bits -= (32 - start_bit); + } + while (num_bits >= 32) + { + p[start_index] = 0; + start_index = (start_index + 1) & mask_index; + num_bits -= 32; + } + n = ~((1 << num_bits) - 1); + p[start_index] &= n; +} + +static inline u8 +seqno_check_wraparound (u32 a, u32 b) +{ + if ((a != b) && (a > b) && ((a - b) > SEQ_CHECK_VALUE)) + { + return 1; + } + return 0; +} + +/* + * Function to analyze the PPC value recevied. + * - Updates the bitmap with received sequence number + * - counts the received/lost/duplicate/reordered packets + */ +inline static void +ioam_analyze_seqno (seqno_rx_info * seqno_rx, u64 seqno) +{ + int diff; + static int peer_dead_count; + seqno_bitmap *bitmap = &seqno_rx->bitmap; + + seqno_rx->rx_packets++; + + if (seqno > bitmap->highest) + { /* new larger sequence number */ + peer_dead_count = 0; + diff = seqno - bitmap->highest; + if (diff < bitmap->window_size) + { + if (diff > 1) + { /* diff==1 is *such* a common case it's a win to optimize it */ + BIT_CLEAR (bitmap->array, bitmap->highest + 1, diff - 1, + bitmap->mask); + seqno_rx->lost_packets += diff - 1; + } + } + else + { + seqno_rx->lost_packets += diff - 1; + memset (bitmap->array, 0, bitmap->array_size * sizeof (u64)); + } + BIT_SET (bitmap->array, seqno & bitmap->mask); + bitmap->highest = seqno; + return; + } + + /* we've seen a bigger seq number before */ + diff = bitmap->highest - seqno; + if (diff >= bitmap->window_size) + { + if (seqno_check_wraparound (bitmap->highest, seqno)) + { + memset (bitmap->array, 0, bitmap->array_size * sizeof (u64)); + BIT_SET (bitmap->array, seqno & bitmap->mask); + bitmap->highest = seqno; + return; + } + else + { + peer_dead_count++; + if (peer_dead_count > 25) + { + peer_dead_count = 0; + memset (bitmap->array, 0, bitmap->array_size * sizeof (u64)); + BIT_SET (bitmap->array, seqno & bitmap->mask); + bitmap->highest = seqno; + } + //ppc_rx->reordered_packets++; + } + return; + } + + if (BIT_TEST (bitmap->array, seqno & bitmap->mask)) + { + seqno_rx->dup_packets++; + return; /* Already seen */ + } + seqno_rx->reordered_packets++; + seqno_rx->lost_packets--; + BIT_SET (bitmap->array, seqno & bitmap->mask); + return; +} + +u8 *show_ioam_seqno_analyse_data_fn (u8 * s, seqno_rx_info * rx); + +u8 *show_ioam_seqno_cmd_fn (u8 * s, ioam_seqno_data * seqno_data, u8 enc); + +void ioam_seqno_init_data (ioam_seqno_data * data); + +void ioam_seqno_init_rx_info (seqno_rx_info * data); + +#endif /* PLUGINS_IOAM_PLUGIN_IOAM_LIB_E2E_IOAM_SEQNO_LIB_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/lib-trace/trace_util.h b/src/plugins/ioam/lib-trace/trace_util.h index 556f07ee..60802ade 100644 --- a/src/plugins/ioam/lib-trace/trace_util.h +++ b/src/plugins/ioam/lib-trace/trace_util.h @@ -89,7 +89,14 @@ int trace_profile_create (trace_profile * profile, u8 trace_type, u8 num_elts, void clear_trace_profiles (void); - +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct +{ + u8 ioam_trace_type; + u8 data_list_elts_left; + u32 elts[0]; /* Variable type. So keep it generic */ +}) ioam_trace_hdr_t; +/* *INDENT-ON* */ #define BIT_TTL_NODEID (1<<0) #define BIT_ING_INTERFACE (1<<1) |