aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/ioam.am54
-rw-r--r--src/plugins/ioam/analyse/ioam_analyse.h106
-rw-r--r--src/plugins/ioam/analyse/ioam_summary_export.c13
-rwxr-xr-xsrc/plugins/ioam/analyse/ioam_summary_export.h2
-rw-r--r--src/plugins/ioam/encap/ip6_ioam_e2e.h2
-rw-r--r--src/plugins/ioam/encap/ip6_ioam_seqno.c4
-rw-r--r--src/plugins/ioam/encap/ip6_ioam_trace.c80
-rw-r--r--src/plugins/ioam/encap/ip6_ioam_trace.h12
-rw-r--r--src/plugins/ioam/lib-trace/trace_util.h28
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping.api73
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping.h138
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_all_api_h.h24
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_api.c190
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_export.c304
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_msg_enum.h37
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_node.c803
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_packet.h154
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_test.c269
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_util.c330
-rw-r--r--src/plugins/ioam/udp-ping/udp_ping_util.h83
-rw-r--r--src/vnet/ip/ip6.h27
-rw-r--r--src/vnet/ip/ip6_forward.c32
-rw-r--r--src/vnet/ip/ip6_hop_by_hop.h36
23 files changed, 2742 insertions, 59 deletions
diff --git a/src/plugins/ioam.am b/src/plugins/ioam.am
index 4346e3c0..d3816413 100644
--- a/src/plugins/ioam.am
+++ b/src/plugins/ioam.am
@@ -181,34 +181,62 @@ IOAM_IP6_MANYCAST_NOINST_HDR = \
ioam/ip6/ioam_cache_msg_enum.h \
ioam/ip6/ioam_cache.api.h
+# udp ping
+########################################
+
+UDP_PING_SRC = \
+ ioam/udp-ping/udp_ping_node.c \
+ ioam/udp-ping/udp_ping_util.c \
+ ioam/udp-ping/udp_ping_export.c \
+ ioam/udp-ping/udp_ping_api.c
+
+UDP_PING_NOINST_HDR = \
+ ioam/udp-ping/udp_ping_packet.h \
+ ioam/udp-ping/udp_ping.h \
+ ioam/udp-ping/udp_ping_util.h \
+ ioam/udp-ping/udp_ping_all_api_h.h \
+ ioam/udp-ping/udp_ping_msg_enum.h \
+ ioam/udp-ping/udp_ping.api.h
+
+UDP_PING_API = ioam/udp-ping/udp_ping.api
+
+udp_ping_test_plugin_la_SOURCES = \
+ ioam/udp-ping/udp_ping_test.c \
+ ioam/udp-ping/udp_ping_plugin.api.h
+
+vppapitestplugins_LTLIBRARIES += udp_ping_test_plugin.la
+
########################################
# iOAM plugins
########################################
-ioam_plugin_la_SOURCES = \
- $(IOAM_POT_SRC) \
- $(IOAM_EXPORT_SRC) \
- $(IOAM_TRACE_SRC) \
+ioam_plugin_la_SOURCES = \
+ $(IOAM_POT_SRC) \
+ $(IOAM_EXPORT_SRC) \
+ $(IOAM_TRACE_SRC) \
$(IOAM_VXLAN_GPE_SRC) \
- $(IOAM_E2E_SRC) \
- $(IPFIX_COLLECTOR_SRC) \
- $(IOAM_ANALYSE_SRC) \
- $(IOAM_IP6_MANYCAST_SRC)
+ $(IOAM_E2E_SRC) \
+ $(IPFIX_COLLECTOR_SRC) \
+ $(IOAM_ANALYSE_SRC) \
+ $(IOAM_IP6_MANYCAST_SRC) \
+ $(UDP_PING_SRC)
-API_FILES += \
+API_FILES += \
$(IOAM_POT_API) \
$(IOAM_EXPORT_API) \
$(IOAM_TRACE_API) \
- $(IOAM_VXLAN_GPE_API) \
- $(IOAM_IP6_MANYCAST_API)
+ $(IOAM_VXLAN_GPE_API) \
+ $(IOAM_IP6_MANYCAST_API) \
+ $(UDP_PING_API)
noinst_HEADERS += \
$(IOAM_POT_NOINST_HDR) \
$(IOAM_EXPORT_NOINST_HDR) \
$(IOAM_TRACE_NOINST_HDR) \
$(IOAM_VXLAN_GPE_NOINST_HDR) \
- $(IOAM_E2E_NOINST_HDR) \
- $(IOAM_IP6_MANYCAST_NOINST_HDR)
+ $(IOAM_E2E_NOINST_HDR) \
+ $(IOAM_IP6_MANYCAST_NOINST_HDR) \
+ $(UDP_PING_NOINST_HDR)
vppplugins_LTLIBRARIES += ioam_plugin.la
diff --git a/src/plugins/ioam/analyse/ioam_analyse.h b/src/plugins/ioam/analyse/ioam_analyse.h
index 3c69d71f..b7576012 100644
--- a/src/plugins/ioam/analyse/ioam_analyse.h
+++ b/src/plugins/ioam/analyse/ioam_analyse.h
@@ -31,6 +31,7 @@ typedef struct
u16 ingress_if;
u16 egress_if;
u32 node_id;
+ u32 state_up;
} ioam_path_map_t;
/** @brief Analysed iOAM trace data.
@@ -176,6 +177,100 @@ ip6_ioam_analyse_calc_delay (ioam_trace_hdr_t * trace, u16 trace_len,
return (f64) (end_time - start_time);
}
+always_inline void
+ip6_ioam_analyse_set_paths_down (ioam_analyser_data_t * data)
+{
+ ioam_analyse_trace_data *trace_data;
+ ioam_analyse_trace_record *trace_record;
+ ioam_path_map_t *path;
+ u8 k, i;
+
+ while (__sync_lock_test_and_set (data->writer_lock, 1))
+ ;
+
+ trace_data = &data->trace_data;
+
+ for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
+ {
+ trace_record = trace_data->path_data + i;
+
+ if (trace_record->is_free)
+ continue;
+
+ path = trace_record->path;
+
+ for (k = 0; k < trace_record->num_nodes; k++)
+ path[k].state_up = 0;
+ }
+ *(data->writer_lock) = 0;
+}
+
+always_inline void
+ip6_ioam_analyse_hbh_trace_loopback (ioam_analyser_data_t * data,
+ ioam_trace_hdr_t * trace, u16 trace_len)
+{
+ ioam_analyse_trace_data *trace_data;
+ ioam_analyse_trace_record *trace_record;
+ ioam_path_map_t *path;
+ u8 i, j, k, num_nodes, max_nodes;
+ u8 *ptr;
+ u32 nodeid;
+ u16 ingress_if, egress_if;
+ u16 size_of_traceopt_per_node;
+ u16 size_of_all_traceopts;
+
+ while (__sync_lock_test_and_set (data->writer_lock, 1))
+ ;
+
+ trace_data = &data->trace_data;
+
+ size_of_traceopt_per_node = fetch_trace_data_size (trace->ioam_trace_type);
+ size_of_all_traceopts = trace_len;
+
+ ptr = (u8 *) trace->elts;
+ max_nodes = (u8) (size_of_all_traceopts / size_of_traceopt_per_node);
+ num_nodes = max_nodes - trace->data_list_elts_left;
+
+ for (i = 0; i < IOAM_MAX_PATHS_PER_FLOW; i++)
+ {
+ trace_record = trace_data->path_data + i;
+ path = trace_record->path;
+
+ if (trace_record->is_free)
+ continue;
+
+ for (j = max_nodes, k = 0; k < num_nodes; j--, k++)
+ {
+ ptr =
+ (u8 *) ((u8 *) trace->elts +
+ (size_of_traceopt_per_node * (j - 1)));
+
+ nodeid = clib_net_to_host_u32 (*((u32 *) ptr)) & 0x00ffffff;
+ ptr += 4;
+
+ if (nodeid != path[k].node_id)
+ goto end;
+
+ if ((trace->ioam_trace_type == TRACE_TYPE_IF_TS_APP) ||
+ (trace->ioam_trace_type == TRACE_TYPE_IF))
+ {
+ ingress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ ptr += 2;
+ egress_if = clib_net_to_host_u16 (*((u16 *) ptr));
+ if ((ingress_if != path[k].ingress_if) ||
+ (egress_if != path[k].egress_if))
+ {
+ goto end;
+ }
+ }
+ /* Found Match - set path hop state to up */
+ path[k].state_up = 1;
+ }
+ }
+end:
+ *(data->writer_lock) = 0;
+}
+
always_inline int
ip6_ioam_analyse_hbh_trace (ioam_analyser_data_t * data,
ioam_trace_hdr_t * trace, u16 pak_len,
@@ -285,6 +380,10 @@ ip6_ioam_analyse_hbh_trace (ioam_analyser_data_t * data,
}
found_match:
+ /* Set path state to UP */
+ for (k = 0; k < num_nodes; k++)
+ path[k].state_up = 1;
+
trace_record->pkt_counter++;
trace_record->bytes_counter += pak_len;
if (trace->ioam_trace_type & BIT_TIMESTAMP)
@@ -328,8 +427,11 @@ format_path_map (u8 * s, va_list * args)
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);
+ s =
+ format (s,
+ "node_id: 0x%x, ingress_if: 0x%x, egress_if:0x%x, state:%s\n",
+ pm->node_id, pm->ingress_if, pm->egress_if,
+ pm->state_up ? "UP" : "DOWN");
pm++;
}
diff --git a/src/plugins/ioam/analyse/ioam_summary_export.c b/src/plugins/ioam/analyse/ioam_summary_export.c
index afda3099..17fcf7a5 100644
--- a/src/plugins/ioam/analyse/ioam_summary_export.c
+++ b/src/plugins/ioam/analyse/ioam_summary_export.c
@@ -114,7 +114,10 @@ ioam_template_rewrite (flow_report_main_t * frm, flow_report_t * fr,
/* Add ioamPathMap manually */
f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
- ioamPathMap, (1 * sizeof (ioam_path)));
+ ioamPathMap,
+ (sizeof (ioam_path) +
+ (sizeof (ioam_path_map_t) *
+ IOAM_TRACE_MAX_NODES)));
f++;
/* Back to the template packet... */
@@ -229,8 +232,9 @@ ioam_analyse_add_ipfix_record (flow_report_t * fr,
path->pkt_counter = trace->pkt_counter - trace_cached->pkt_counter;
path->pkt_counter = clib_host_to_net_u32 (path->pkt_counter);
+ offset += sizeof (ioam_path);
- for (j = 0; j < IOAM_TRACE_MAX_NODES; j++)
+ for (j = 0; j < trace->num_nodes; j++)
{
path->path[j].node_id =
clib_host_to_net_u32 (trace->path[j].node_id);
@@ -238,9 +242,11 @@ ioam_analyse_add_ipfix_record (flow_report_t * fr,
clib_host_to_net_u16 (trace->path[j].ingress_if);
path->path[j].egress_if =
clib_host_to_net_u16 (trace->path[j].egress_if);
+ path->path[j].state_up = trace->path[j].state_up;
}
- offset += sizeof (ioam_path);
+ //offset += (sizeof(ioam_path_map_t) * trace->num_nodes);
+ offset += (sizeof (ioam_path_map_t) * IOAM_TRACE_MAX_NODES); //FIXME
}
}
@@ -312,6 +318,7 @@ ioam_send_flows (flow_report_main_t * frm, flow_report_t * fr,
tp = vlib_buffer_get_current (b0);
ip = &tp->ip4;
+ udp = &tp->udp;
h = &tp->ipfix.h;
s = &tp->ipfix.s;
diff --git a/src/plugins/ioam/analyse/ioam_summary_export.h b/src/plugins/ioam/analyse/ioam_summary_export.h
index 9be31d2e..b4355061 100755
--- a/src/plugins/ioam/analyse/ioam_summary_export.h
+++ b/src/plugins/ioam/analyse/ioam_summary_export.h
@@ -60,7 +60,7 @@ typedef struct
u32 mean_delay;
u32 pkt_counter;
u32 bytes_counter;
- ioam_path_map_t path[IOAM_TRACE_MAX_NODES];
+ ioam_path_map_t path[0];
} ioam_path;
clib_error_t *ioam_flow_create (u8 del);
diff --git a/src/plugins/ioam/encap/ip6_ioam_e2e.h b/src/plugins/ioam/encap/ip6_ioam_e2e.h
index fcec4a14..fb83403d 100644
--- a/src/plugins/ioam/encap/ip6_ioam_e2e.h
+++ b/src/plugins/ioam/encap/ip6_ioam_e2e.h
@@ -16,7 +16,7 @@
#ifndef __included_ip6_ioam_e2e_h__
#define __included_ip6_ioam_e2e_h__
-#include "../lib-e2e/e2e_util.h"
+#include <ioam/lib-e2e/e2e_util.h>
#include "ip6_ioam_seqno.h"
/* *INDENT-OFF* */
diff --git a/src/plugins/ioam/encap/ip6_ioam_seqno.c b/src/plugins/ioam/encap/ip6_ioam_seqno.c
index 01977123..08bf554b 100644
--- a/src/plugins/ioam/encap/ip6_ioam_seqno.c
+++ b/src/plugins/ioam/encap/ip6_ioam_seqno.c
@@ -44,6 +44,10 @@ ioam_seqno_encap_handler (vlib_buffer_t *b, ip6_header_t *ip,
int rv = 0;
ioam_seqno_data *data;
+ /* Bypass seqno processing */
+ if (PREDICT_FALSE(opaque_index == 0x7FFFFFFF))
+ return rv;
+
data = ioam_e2ec_get_seqno_data_from_flow_ctx(opaque_index);
e2e = (ioam_e2e_option_t *) opt;
e2e->e2e_hdr.e2e_data = clib_host_to_net_u32(++data->seq_num);
diff --git a/src/plugins/ioam/encap/ip6_ioam_trace.c b/src/plugins/ioam/encap/ip6_ioam_trace.c
index f1eb1bf0..e3182561 100644
--- a/src/plugins/ioam/encap/ip6_ioam_trace.c
+++ b/src/plugins/ioam/encap/ip6_ioam_trace.c
@@ -28,6 +28,9 @@
#include <vnet/plugin/plugin.h>
#include <ioam/encap/ip6_ioam_trace.h>
+#include <ioam/udp-ping/udp_ping.h>
+#include <ioam/udp-ping/udp_ping_packet.h>
+#include <ioam/udp-ping/udp_ping_util.h>
/* Timestamp precision multipliers for seconds, milliseconds, microseconds
* and nanoseconds respectively.
@@ -47,7 +50,9 @@ extern ip6_main_t ip6_main;
_(PROCESSED, "Pkts with ip6 hop-by-hop trace options") \
_(PROFILE_MISS, "Pkts with ip6 hop-by-hop trace options but no profile set") \
_(UPDATED, "Pkts with trace updated") \
- _(FULL, "Pkts with trace options but no space")
+ _(FULL, "Pkts with trace options but no space") \
+ _(LOOPBACK, "Pkts with trace options Loopback") \
+ _(LOOPBACK_REPLY, "Pkts with trace options Loopback Reply")
static char *ip6_hop_by_hop_ioam_trace_stats_strings[] = {
#define _(sym,string) string,
@@ -200,6 +205,63 @@ ip6_hop_by_hop_ioam_trace_rewrite_handler (u8 * rewrite_string,
return 0;
}
+always_inline void
+ip6_hbh_ioam_loopback_handler (vlib_buffer_t * b, ip6_header_t * ip,
+ ioam_trace_option_t * trace)
+{
+ u32 buffers;
+ ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
+ vlib_buffer_t *b0;
+ vlib_frame_t *nf = 0;
+ u32 *to_next;
+ vlib_node_t *next_node;
+ ip6_header_t *ip6;
+ ip6_hop_by_hop_header_t *hbh;
+ ioam_trace_option_t *opt;
+ u16 ip6_len;
+ udp_ping_t *udp;
+
+ next_node = vlib_get_node_by_name (hm->vlib_main, (u8 *) "ip6-lookup");
+ nf = vlib_get_frame_to_node (hm->vlib_main, next_node->index);
+ nf->n_vectors = 0;
+ to_next = vlib_frame_vector_args (nf);
+
+ if (vlib_buffer_alloc (hm->vlib_main, &buffers, 1) != 1)
+ return;
+
+ b0 = vlib_get_buffer (hm->vlib_main, buffers);
+ ip6_len = clib_net_to_host_u16 (ip->payload_length);
+ clib_memcpy (b0->data, ip, (ip6_len + sizeof (ip6_header_t)));
+ b0->current_data = 0;
+ b0->current_length = ip6_len + sizeof (ip6_header_t);
+ 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;
+
+ /* Change destination address */
+ ip6 = vlib_buffer_get_current (b0);
+ //ip6->src_address = ip->dst_address;
+ //ip6->dst_address = ip->src_address;
+
+ hbh = (ip6_hop_by_hop_header_t *) (ip6 + 1);
+ opt = (ioam_trace_option_t *)
+ ip6_hbh_get_option (hbh, HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
+
+ udp = (udp_ping_t *) ((u8 *) hbh + ((hbh->length + 1) << 3));
+ udp_ping_create_reply_from_probe_ip6 (ip6, hbh, udp);
+ //ip6_hbh_ioam_trace_reset_bit (opt, BIT_LOOPBACK);
+ ip6_hbh_ioam_trace_set_bit (opt, BIT_LOOPBACK_REPLY);
+ /* No need to trace loopback packet */
+ //opt->trace_hdr.data_list_elts_left = 0;
+
+ *to_next = buffers;
+ nf->n_vectors++;
+ to_next++;
+
+ vlib_put_frame_to_node (hm->vlib_main, next_node->index, nf);
+ ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_LOOPBACK, 1);
+}
int
ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip,
@@ -226,6 +288,13 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip,
return (-1);
}
+ /* Don't trace loopback reply packets */
+ if (trace->trace_hdr.ioam_trace_type & BIT_LOOPBACK_REPLY)
+ {
+ ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_LOOPBACK_REPLY,
+ 1);
+ return rv;
+ }
time_u64.as_u64 = 0;
@@ -273,6 +342,15 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip,
*elt = clib_host_to_net_u32 (profile->app_data);
elt++;
}
+
+
+ if (PREDICT_FALSE (trace->trace_hdr.ioam_trace_type & BIT_LOOPBACK))
+ {
+ /* if loopback flag set then copy the packet
+ * and send it back to source */
+ ip6_hbh_ioam_loopback_handler (b, ip, trace);
+ }
+
ip6_ioam_trace_stats_increment_counter (IP6_IOAM_TRACE_UPDATED, 1);
}
else
diff --git a/src/plugins/ioam/encap/ip6_ioam_trace.h b/src/plugins/ioam/encap/ip6_ioam_trace.h
index 620b70a8..4eda6110 100644
--- a/src/plugins/ioam/encap/ip6_ioam_trace.h
+++ b/src/plugins/ioam/encap/ip6_ioam_trace.h
@@ -28,6 +28,18 @@ typedef CLIB_PACKED(struct {
}) ioam_trace_option_t;
/* *INDENT-ON* */
+always_inline void
+ip6_hbh_ioam_trace_set_bit (ioam_trace_option_t * trace, u8 trace_bit)
+{
+ ioam_trace_set_bit (&trace->trace_hdr, trace_bit);
+}
+
+always_inline void
+ip6_hbh_ioam_trace_reset_bit (ioam_trace_option_t * trace, u8 trace_bit)
+{
+ ioam_trace_reset_bit (&trace->trace_hdr, trace_bit);
+}
+
#endif /* PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_TRACE_H_ */
/*
diff --git a/src/plugins/ioam/lib-trace/trace_util.h b/src/plugins/ioam/lib-trace/trace_util.h
index 7065b41e..c8b50470 100644
--- a/src/plugins/ioam/lib-trace/trace_util.h
+++ b/src/plugins/ioam/lib-trace/trace_util.h
@@ -103,7 +103,11 @@ typedef CLIB_PACKED (struct
#define BIT_EGR_INTERFACE (1<<2)
#define BIT_TIMESTAMP (1<<3)
#define BIT_APPDATA (1<<4)
-#define TRACE_TYPE_MASK 0x1F /* Mask of all above bits */
+#define BIT_LOOPBACK (1<<5)
+#define BIT_LOOPBACK_REPLY (1<<6)
+#define TRACE_TYPE_MASK 0x7F /* Mask of all above bits */
+
+#define TRACE_TYPE_IF_TS_APP_LOOP 0x3F
/*
0x00011111 iOAM-trace-type is 0x00011111 then the format of node
@@ -218,20 +222,32 @@ fetch_trace_data_size (u8 trace_type)
{
u8 trace_data_size = 0;
- if (trace_type == TRACE_TYPE_IF_TS_APP)
+ if ((trace_type & TRACE_TYPE_IF_TS_APP) == TRACE_TYPE_IF_TS_APP)
trace_data_size = sizeof (ioam_trace_if_ts_app_t);
- else if (trace_type == TRACE_TYPE_IF)
+ else if ((trace_type & TRACE_TYPE_IF) == TRACE_TYPE_IF)
trace_data_size = sizeof (ioam_trace_if_t);
- else if (trace_type == TRACE_TYPE_TS)
+ else if ((trace_type & TRACE_TYPE_TS) == TRACE_TYPE_TS)
trace_data_size = sizeof (ioam_trace_ts_t);
- else if (trace_type == TRACE_TYPE_APP)
+ else if ((trace_type & TRACE_TYPE_APP) == TRACE_TYPE_APP)
trace_data_size = sizeof (ioam_trace_app_t);
- else if (trace_type == TRACE_TYPE_TS_APP)
+ else if ((trace_type & TRACE_TYPE_TS_APP) == TRACE_TYPE_TS_APP)
trace_data_size = sizeof (ioam_trace_ts_app_t);
return trace_data_size;
}
+always_inline void
+ioam_trace_set_bit (ioam_trace_hdr_t * trace_hdr, u8 trace_bit)
+{
+ trace_hdr->ioam_trace_type |= trace_bit;
+}
+
+always_inline void
+ioam_trace_reset_bit (ioam_trace_hdr_t * trace_hdr, u8 trace_bit)
+{
+ trace_hdr->ioam_trace_type &= (~trace_bit);
+}
+
int ioam_trace_get_sizeof_handler (u32 * result);
int ip6_trace_profile_setup (void);
int ip6_trace_profile_cleanup (void);
diff --git a/src/plugins/ioam/udp-ping/udp_ping.api b/src/plugins/ioam/udp-ping/udp_ping.api
new file mode 100644
index 00000000..87945816
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping.api
@@ -0,0 +1,73 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * 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.
+ */
+
+
+/** \brief UDP-Probe Add/Delete request
+ @param src_ip_address - Source ipv4/v6 address for the udp-ping flow
+ @param dst_ip_address - Destination ipv4/v6 address for the udp-ping flow
+ @param start_src_port - Starting source port of port range for udp-ping
+ @param end_src_port - End source port of port range for udp-ping
+ @param start_dst_port - Starting destination port of port range for udp-ping
+ @param end_dst_port - End destination port of port range for udp-ping
+ @param interval - Time interval in seconds at which udp-probe need to be sent
+ @param is_ipv4 - To determine whether IPv4 or IPv6 address is used
+ @param dis - TRUE is delete, FALSE if Add
+*/
+define udp_ping_add_del_req {
+ u32 client_index;
+ u32 context;
+ u8 src_ip_address[16];
+ u8 dst_ip_address[16];
+ u16 start_src_port;
+ u16 end_src_port;
+ u16 start_dst_port;
+ u16 end_dst_port;
+ u16 interval;
+ u8 is_ipv4;
+ u8 dis;
+ u8 fault_det;
+ u8 reserve[3];
+};
+
+/** \brief Udp-probe add/del response
+ @param context - sender context, to match reply w/ request
+ @param retval - return value for request
+*/
+define udp_ping_add_del_reply {
+ u32 context;
+ i32 retval;
+};
+
+/** \brief Udp-probe export add/del request
+ @param context - sender context, to match reply w/ request
+ @param retval - return value for request
+ @param enable - If TRUE then enable export else disable
+*/
+define udp_ping_export_req {
+ u32 client_index;
+ u32 context;
+ u32 enable;
+};
+
+/** \brief Udp-probe export add/del response
+ @param context - sender context, to match reply w/ request
+ @param retval - return value for request
+*/
+define udp_ping_export_reply {
+ u32 context;
+ i32 retval;
+};
+
diff --git a/src/plugins/ioam/udp-ping/udp_ping.h b/src/plugins/ioam/udp-ping/udp_ping.h
new file mode 100644
index 00000000..26c42019
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping.h
@@ -0,0 +1,138 @@
+/*
+ * 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_UDP_PING_UDP_PING_H_
+#define PLUGINS_IOAM_PLUGIN_IOAM_UDP_PING_UDP_PING_H_
+
+#include <ioam/analyse/ioam_analyse.h>
+
+#define MAX_PING_RETRIES 5
+
+#define EVENT_SIG_RECHECK 2
+
+/** @brief udp-ping session data.
+ @note cache aligned.
+*/
+typedef struct
+{
+ /** UDP ping packet */
+ u8 *ping_rewrite;
+
+ /** Ping packet rewrite string len. */
+ u16 rewrite_len;
+
+ /** Number of times ping response was dropped.
+ * If retry > MAX_PING_RETRIES then declare connectivity as down.
+ */
+ u16 retry;
+
+ u16 reserve[2];
+
+ /** Analysed data. */
+ ioam_analyser_data_t analyse_data;
+
+ /** This is used by ioam e2e for identifying flow and add seq number. */
+ u32 flow_ctx;
+
+ /** No of packets sent for this flow. */
+ u32 pak_sent;
+} udp_ping_flow_data;
+
+/** @brief udp-ping flow data.
+ @note cache aligned.
+*/
+typedef struct
+{
+ /** Time at which next udp-ping probe has to be sent out. */
+ f64 next_send_time;
+
+ /** Interval for which ping packet to be sent. */
+ u16 interval;
+
+ u16 reserve[3];
+
+ /** Defines start port of the src port range. */
+ u16 start_src_port;
+
+ /** Defines end port of the src port range. */
+ u16 end_src_port;
+
+ /** Defines start port of the dest port range. */
+ u16 start_dst_port;
+
+ /** Defines end port of the dest port range. */
+ u16 end_dst_port;
+
+ /** Ping statistics. */
+ udp_ping_flow_data *stats;
+
+} udp_ping_flow;
+
+/** @brief udp-ping data.
+*/
+typedef struct
+{
+ /** Local source IPv4/6 address to be used. */
+ ip46_address_t src;
+
+ /** Remote destination IPv4/6 address to be used. */
+ ip46_address_t dst;
+
+ /** Per flow data. */
+ udp_ping_flow udp_data;
+
+ /** To enable fault detection/isolation in network. */
+ u8 fault_det;
+} ip46_udp_ping_flow;
+
+/** @brief udp-ping main data-structure.
+*/
+typedef struct
+{
+ /** Vector od udp-ping data */
+ ip46_udp_ping_flow *ip46_flow;
+
+ /** Stores the time interval at which process node has to wake up. */
+ u64 timer_interval;
+
+ /** 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;
+
+ /** API message ID base */
+ u16 msg_id_base;
+} udp_ping_main_t;
+
+extern udp_ping_main_t udp_ping_main;
+
+void
+ip46_udp_ping_set_flow (ip46_address_t src, ip46_address_t dst,
+ u16 start_src_port, u16 end_src_port,
+ u16 start_dst_port, u16 end_dst_port,
+ u16 interval, u8 fault_det, u8 is_disable);
+
+clib_error_t *udp_ping_flow_create (u8 del);
+
+#endif /* PLUGINS_IOAM_PLUGIN_IOAM_UDP_PING_UDP_PING_H_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_all_api_h.h b/src/plugins/ioam/udp-ping/udp_ping_all_api_h.h
new file mode 100644
index 00000000..1ed2e10b
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_all_api_h.h
@@ -0,0 +1,24 @@
+/*
+ * 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 the generated file, see BUILT_SOURCES in Makefile.am */
+#include <ioam/udp-ping/udp_ping.api.h>
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_api.c b/src/plugins/ioam/udp-ping/udp_ping_api.c
new file mode 100644
index 00000000..8cb8cc96
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_api.c
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+/*
+ *------------------------------------------------------------------
+ * udp_ping_api.c - UDP Ping related APIs to create
+ * and maintain ping flows
+ *------------------------------------------------------------------
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <ioam/udp-ping/udp_ping.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibsocket/api.h>
+
+/* define message IDs */
+#include <ioam/udp-ping/udp_ping_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_printfun
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_api_version
+
+/*
+ * A handy macro to set up a message reply.
+ * Assumes that the following variables are available:
+ * mp - pointer to request message
+ * rmp - pointer to reply message type
+ * rv - return value
+ */
+
+#define REPLY_MACRO(t) \
+ do { \
+ unix_shared_memory_queue_t * q = \
+ vl_api_client_index_to_input_queue (mp->client_index); \
+ if (!q) \
+ return; \
+ \
+ rmp = vl_msg_api_alloc (sizeof (*rmp)); \
+ rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base); \
+ rmp->context = mp->context; \
+ rmp->retval = ntohl(rv); \
+ \
+ vl_msg_api_send_shmem (q, (u8 *)&rmp); \
+ } while(0);
+
+#define REPLY_MACRO2(t, body) \
+ do { \
+ unix_shared_memory_queue_t * q; \
+ rv = vl_msg_api_pd_handler (mp, rv); \
+ q = vl_api_client_index_to_input_queue (mp->client_index); \
+ if (!q) \
+ return; \
+ \
+ rmp = vl_msg_api_alloc (sizeof (*rmp)); \
+ rmp->_vl_msg_id = ntohs((t)); \
+ rmp->context = mp->context; \
+ rmp->retval = ntohl(rv); \
+ do {body;} while (0); \
+ vl_msg_api_send_shmem (q, (u8 *)&rmp); \
+ } while(0);
+
+/* List of message types that this module understands */
+
+#define foreach_udp_ping_api_msg \
+ _(UDP_PING_ADD_DEL_REQ, udp_ping_add_del_req) \
+ _(UDP_PING_EXPORT_REQ, udp_ping_export_req) \
+
+static void vl_api_udp_ping_add_del_req_t_handler
+ (vl_api_udp_ping_add_del_req_t * mp)
+{
+ ip46_address_t dst, src;
+ int rv = 0;
+ udp_ping_main_t *sm = &udp_ping_main;
+ vl_api_udp_ping_add_del_reply_t *rmp;
+
+ if (mp->is_ipv4)
+ {
+ rv = -1; //Not supported
+ goto ERROROUT;
+ }
+
+ clib_memcpy ((void *) &src.ip6, (void *) mp->src_ip_address,
+ sizeof (ip6_address_t));
+ clib_memcpy ((void *) &dst.ip6, (void *) mp->dst_ip_address,
+ sizeof (ip6_address_t));
+
+ ip46_udp_ping_set_flow (src, dst,
+ ntohs (mp->start_src_port),
+ ntohs (mp->end_src_port),
+ ntohs (mp->start_dst_port),
+ ntohs (mp->end_dst_port),
+ ntohs (mp->interval), mp->fault_det, mp->dis);
+ rv = 0; //FIXME
+
+ERROROUT:
+ REPLY_MACRO (VL_API_UDP_PING_ADD_DEL_REPLY);
+}
+
+static void vl_api_udp_ping_export_req_t_handler
+ (vl_api_udp_ping_export_req_t * mp)
+{
+ udp_ping_main_t *sm = &udp_ping_main;
+ int rv = 0;
+ vl_api_udp_ping_export_reply_t *rmp;
+
+ (void) udp_ping_flow_create (!mp->enable);
+ rv = 0; //FIXME
+
+ REPLY_MACRO (VL_API_UDP_PING_EXPORT_REPLY);
+}
+
+/* Set up the API message handling tables */
+static clib_error_t *
+udp_ping_api_hookup (vlib_main_t * vm)
+{
+ udp_ping_main_t *sm = &udp_ping_main;
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \
+ #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_udp_ping_api_msg;
+#undef _
+
+ return 0;
+}
+
+static clib_error_t *
+udp_ping_api_init (vlib_main_t * vm)
+{
+ udp_ping_main_t *sm = &udp_ping_main;
+ clib_error_t *error = 0;
+ u8 *name;
+
+ name = format (0, "udp_ping_%08x%c", api_version, 0);
+
+ /* Ask for a correctly-sized block of API message decode slots */
+ sm->msg_id_base = vl_msg_api_get_msg_ids
+ ((char *) name, VL_MSG_FIRST_AVAILABLE);
+
+ error = udp_ping_api_hookup (vm);
+
+ vec_free (name);
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (udp_ping_api_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_export.c b/src/plugins/ioam/udp-ping/udp_ping_export.c
new file mode 100644
index 00000000..ce62d98f
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_export.c
@@ -0,0 +1,304 @@
+/*
+ * 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/flow/flow_report.h>
+#include <ioam/analyse/ioam_summary_export.h>
+#include <vnet/api_errno.h>
+#include <ioam/udp-ping/udp_ping.h>
+
+#define UDP_PING_EXPORT_RECORD_SIZE 400
+
+static u8 *
+udp_ping_template_rewrite (flow_report_main_t * frm, flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address, u16 collector_port)
+{
+ return ioam_template_rewrite (frm, fr, collector_address,
+ src_address, collector_port);
+}
+
+static vlib_frame_t *
+udp_ping_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, j;
+ 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;
+ flow_report_stream_t *stream;
+ udp_ping_flow_data *stats;
+ ip46_udp_ping_flow *ip46_flow;
+ u16 src_port, dst_port;
+ u16 data_len;
+
+ stream = &frm->streams[fr->stream_index];
+ data_len = vec_len (udp_ping_main.ip46_flow);
+
+ for (i = 0; i < data_len; i++)
+ {
+ if (pool_is_free_index (udp_ping_main.ip46_flow, i))
+ continue;
+
+ ip46_flow = pool_elt_at_index (udp_ping_main.ip46_flow, i);
+ j = 0;
+ for (src_port = ip46_flow->udp_data.start_src_port;
+ src_port <= ip46_flow->udp_data.end_src_port; src_port++)
+ {
+ for (dst_port = ip46_flow->udp_data.start_dst_port;
+ dst_port <= ip46_flow->udp_data.end_dst_port; dst_port++, j++)
+ {
+ stats = ip46_flow->udp_data.stats + j;
+ 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,
+ &stats->analyse_data,
+ b0, next_offset,
+ &ip46_flow->
+ src.ip6,
+ &ip46_flow->
+ dst.ip6, src_port,
+ dst_port);
+
+ //u32 pak_sent = clib_host_to_net_u32(stats->pak_sent);
+ //memcpy (b0->data + next_offset, &pak_sent, sizeof(u32));
+ //next_offset += sizeof(u32);
+
+ records_this_buffer++;
+
+ /* Flush data if packet len is about to reach path mtu */
+ if (next_offset > (frm->path_mtu - UDP_PING_EXPORT_RECORD_SIZE))
+ {
+ 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);
+ h = &tp->ipfix.h;
+ s = &tp->ipfix.s;
+
+ s->set_id_length =
+ ipfix_set_id_length (IOAM_FLOW_TEMPLATE_ID,
+ next_offset - (sizeof (*ip) +
+ sizeof (*udp) +
+ sizeof (*h)));
+ h->version_length =
+ version_length (next_offset -
+ (sizeof (*ip) + sizeof (*udp)));
+
+ 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));
+
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+
+ ASSERT (ip->checksum == ip4_header_checksum (ip));
+
+ 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;
+ }
+ }
+ }
+ }
+
+ if (b0)
+ {
+ 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);
+ h = &tp->ipfix.h;
+ s = &tp->ipfix.s;
+
+ s->set_id_length = ipfix_set_id_length (IOAM_FLOW_TEMPLATE_ID,
+ next_offset - (sizeof (*ip) +
+ sizeof (*udp) +
+ sizeof (*h)));
+ h->version_length =
+ version_length (next_offset - (sizeof (*ip) + sizeof (*udp)));
+
+ 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));
+
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+
+ ASSERT (ip->checksum == ip4_header_checksum (ip));
+
+ 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 *
+udp_ping_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 = udp_ping_template_rewrite;
+ args.flow_data_callback = udp_ping_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;
+}
+
+static clib_error_t *
+set_udp_ping_export_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ //int rv;
+ int is_add = 1;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "export-ipfix"))
+ is_add = 1;
+ else if (unformat (input, "disable"))
+ is_add = 0;
+ else
+ break;
+ }
+
+ if (is_add)
+ (void) udp_ping_flow_create (0);
+ else
+ (void) udp_ping_flow_create (1);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_udp_ping_export_command, static) = {
+ .path = "set udp-ping export-ipfix",
+ .short_help = "set udp-ping export-ipfix [disable]",
+ .function = set_udp_ping_export_command_fn,
+};
+/* *INDENT-ON* */
+
+clib_error_t *
+udp_ping_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 (udp_ping_flow_report_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_msg_enum.h b/src/plugins/ioam/udp-ping/udp_ping_msg_enum.h
new file mode 100644
index 00000000..dded1884
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_msg_enum.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 included_udp_ping_msg_enum_h
+#define included_udp_ping_msg_enum_h
+
+#include <vppinfra/byte_order.h>
+
+#define vl_msg_id(n,h) n,
+typedef enum
+{
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+ /* We'll want to know how many messages IDs we need... */
+ VL_MSG_FIRST_AVAILABLE,
+} vl_msg_id_t;
+#undef vl_msg_id
+
+#endif /* included_udp_ping_msg_enum_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_node.c b/src/plugins/ioam/udp-ping/udp_ping_node.c
new file mode 100644
index 00000000..4de8fe2f
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_node.c
@@ -0,0 +1,803 @@
+/*
+ * 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/vnet.h>
+#include <vlib/vlib.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibsocket/api.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/ip6_hop_by_hop.h>
+#include <ioam/encap/ip6_ioam_trace.h>
+#include <ioam/encap/ip6_ioam_e2e.h>
+#include <ioam/udp-ping/udp_ping_packet.h>
+#include <ioam/udp-ping/udp_ping.h>
+#include <ioam/udp-ping/udp_ping_util.h>
+#include <vnet/sr/sr_packet.h>
+
+typedef enum
+{
+ UDP_PING_NEXT_DROP,
+ UDP_PING_NEXT_PUNT,
+ UDP_PING_NEXT_UDP_LOOKUP,
+ UDP_PING_NEXT_ICMP,
+ UDP_PING_NEXT_IP6_LOOKUP,
+ UDP_PING_NEXT_IP6_DROP,
+ UDP_PING_N_NEXT,
+} udp_ping_next_t;
+
+udp_ping_main_t udp_ping_main;
+
+uword
+udp_ping_process (vlib_main_t * vm,
+ vlib_node_runtime_t * rt, vlib_frame_t * f);
+
+extern int
+ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip,
+ ip6_hop_by_hop_option_t * opt);
+
+typedef struct
+{
+ ip6_address_t src;
+ ip6_address_t dst;
+ u16 src_port;
+ u16 dst_port;
+ u16 handle;
+ u16 next_index;
+ u8 msg_type;
+} udp_ping_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_udp_ping_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 *);
+ udp_ping_trace_t *t = va_arg (*args, udp_ping_trace_t *);
+
+ s = format (s, "udp-ping-local: src %U, dst %U, src_port %u, dst_port %u "
+ "handle %u, next_index %u, msg_type %u",
+ format_ip6_address, &t->src,
+ format_ip6_address, &t->dst,
+ t->src_port, t->dst_port,
+ t->handle, t->next_index, t->msg_type);
+ return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (udp_ping_node, static) =
+{
+ .function = udp_ping_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "udp-ping-process",
+};
+/* *INDENT-ON* */
+
+void
+udp_ping_calculate_timer_interval (void)
+{
+ int i;
+ ip46_udp_ping_flow *flow = NULL;
+ u16 min_interval = 0x1e9;
+
+ for (i = 0; i < vec_len (udp_ping_main.ip46_flow); i++)
+ {
+ if (pool_is_free_index (udp_ping_main.ip46_flow, i))
+ continue;
+
+ flow = pool_elt_at_index (udp_ping_main.ip46_flow, i);
+
+ if (min_interval > flow->udp_data.interval)
+ min_interval = flow->udp_data.interval;
+ }
+
+ if (udp_ping_main.timer_interval != min_interval)
+ {
+ udp_ping_main.timer_interval = min_interval;
+ vlib_process_signal_event (udp_ping_main.vlib_main,
+ udp_ping_node.index, EVENT_SIG_RECHECK, 0);
+ }
+}
+
+void
+ip46_udp_ping_set_flow (ip46_address_t src, ip46_address_t dst,
+ u16 start_src_port, u16 end_src_port,
+ u16 start_dst_port, u16 end_dst_port,
+ u16 interval, u8 fault_det, u8 is_disable)
+{
+ u8 found = 0;
+ ip46_udp_ping_flow *flow = NULL;
+ int i;
+
+ for (i = 0; i < vec_len (udp_ping_main.ip46_flow); i++)
+ {
+ if (pool_is_free_index (udp_ping_main.ip46_flow, i))
+ continue;
+
+ flow = pool_elt_at_index (udp_ping_main.ip46_flow, i);
+ if ((0 == udp_ping_compare_flow (src, dst,
+ start_src_port, end_src_port,
+ start_dst_port, end_dst_port, flow)))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ u16 cur_interval;
+ if (is_disable)
+ {
+ cur_interval = flow->udp_data.interval;
+ udp_ping_free_flow_data (flow);
+ pool_put_index (udp_ping_main.ip46_flow, i);
+ if (udp_ping_main.timer_interval == interval)
+ udp_ping_calculate_timer_interval ();
+ return;
+ }
+
+ cur_interval = flow->udp_data.interval;
+ flow->udp_data.interval = interval;
+ if (udp_ping_main.timer_interval > interval)
+ {
+ udp_ping_main.timer_interval = interval;
+ vlib_process_signal_event (udp_ping_main.vlib_main,
+ udp_ping_node.index,
+ EVENT_SIG_RECHECK, 0);
+ }
+ else if (udp_ping_main.timer_interval == cur_interval)
+ udp_ping_calculate_timer_interval ();
+
+ return;
+ }
+
+ /* Delete operation and item not found */
+ if (is_disable)
+ return;
+
+ /* Alloc new session */
+ pool_get_aligned (udp_ping_main.ip46_flow, flow, CLIB_CACHE_LINE_BYTES);
+ udp_ping_populate_flow (src, dst,
+ start_src_port, end_src_port,
+ start_dst_port, end_dst_port,
+ interval, fault_det, flow);
+
+ udp_ping_create_rewrite (flow, (flow - udp_ping_main.ip46_flow));
+
+ if (udp_ping_main.timer_interval > interval)
+ {
+ udp_ping_main.timer_interval = interval;
+ vlib_process_signal_event (udp_ping_main.vlib_main,
+ udp_ping_node.index, EVENT_SIG_RECHECK, 0);
+ }
+ return;
+}
+
+uword
+unformat_port_range (unformat_input_t * input, va_list * args)
+{
+ u16 *start_port, *end_port;
+ uword c;
+ u8 colon_present = 0;
+
+ start_port = va_arg (*args, u16 *);
+ end_port = va_arg (*args, u16 *);
+
+ *start_port = *end_port = 0;
+ /* Get start port */
+ while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
+ {
+ switch (c)
+ {
+ case '0' ... '9':
+ *start_port = ((*start_port) * 10) + (c - '0');
+ break;
+
+ case ':':
+ colon_present = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (colon_present)
+ break;
+ }
+
+ if (!colon_present)
+ return 0;
+
+ /* Get end port */
+ while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
+ {
+ switch (c)
+ {
+ case '0' ... '9':
+ *end_port = ((*end_port) * 10) + (c - '0');
+ break;
+
+ default:
+ return 1;
+ }
+ }
+
+ if (end_port < start_port)
+ return 0;
+
+ return 1;
+}
+
+static clib_error_t *
+set_udp_ping_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ ip46_address_t dst, src;
+ u16 start_src_port, end_src_port;
+ u16 start_dst_port, end_dst_port;
+ u32 interval;
+ u8 is_disable = 0;
+ u8 fault_det = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat
+ (input, "src %U", unformat_ip46_address, &src, IP46_TYPE_ANY))
+ ;
+ else if (unformat (input, "src-port-range %U",
+ unformat_port_range, &start_src_port, &end_src_port))
+ ;
+ else
+ if (unformat
+ (input, "dst %U", unformat_ip46_address, &dst, IP46_TYPE_ANY))
+ ;
+ else if (unformat (input, "dst-port-range %U",
+ unformat_port_range, &start_dst_port, &end_dst_port))
+ ;
+ else if (unformat (input, "interval %d", &interval))
+ ;
+ else if (unformat (input, "fault-detect"))
+ fault_det = 1;
+ else if (unformat (input, "disable"))
+ is_disable = 1;
+ else
+ break;
+ }
+
+ ip46_udp_ping_set_flow (src, dst, start_src_port, end_src_port,
+ start_dst_port, end_dst_port, (u16) interval,
+ fault_det, is_disable);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_udp_ping_command, static) =
+{
+ .path = "set udp-ping",
+ .short_help =
+ "set udp-ping src <local IPv6 address> src-port-range <local port range> \
+ dst <remote IPv6 address> dst-port-range <destination port range> \
+ interval <time interval in sec for which ping packet will be sent> \
+ [disable]",
+ .function = set_udp_ping_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+show_udp_ping_summary_cmd_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ u8 *s = 0;
+ int i, j;
+ ip46_udp_ping_flow *ip46_flow;
+ u16 src_port, dst_port;
+ udp_ping_flow_data *stats;
+
+ s = format (s, "UDP-Ping data:\n");
+
+ for (i = 0; i < vec_len (udp_ping_main.ip46_flow); i++)
+ {
+ if (pool_is_free_index (udp_ping_main.ip46_flow, i))
+ continue;
+
+ ip46_flow = pool_elt_at_index (udp_ping_main.ip46_flow, i);
+ s = format (s, "Src: %U, Dst: %U\n",
+ format_ip46_address, &ip46_flow->src, IP46_TYPE_ANY,
+ format_ip46_address, &ip46_flow->dst, IP46_TYPE_ANY);
+
+ s = format (s, "Start src port: %u, End src port: %u\n",
+ ip46_flow->udp_data.start_src_port,
+ ip46_flow->udp_data.end_src_port);
+ s = format (s, "Start dst port: %u, End dst port: %u\n",
+ ip46_flow->udp_data.start_dst_port,
+ ip46_flow->udp_data.end_dst_port);
+ s = format (s, "Interval: %u\n", ip46_flow->udp_data.interval);
+
+ j = 0;
+ for (src_port = ip46_flow->udp_data.start_src_port;
+ src_port <= ip46_flow->udp_data.end_src_port; src_port++)
+ {
+ for (dst_port = ip46_flow->udp_data.start_dst_port;
+ dst_port <= ip46_flow->udp_data.end_dst_port; dst_port++)
+ {
+ stats = ip46_flow->udp_data.stats + j;
+ s =
+ format (s, "\nSrc Port - %u, Dst Port - %u, Flow CTX - %u\n",
+ src_port, dst_port, stats->flow_ctx);
+ s =
+ format (s, "Path State - %s\n",
+ (stats->retry > MAX_PING_RETRIES) ? "Down" : "Up");
+ s = format (s, "Path Data:\n");
+ s = print_analyse_flow (s,
+ &ip46_flow->udp_data.
+ stats[j].analyse_data);
+ j++;
+ }
+ }
+ s = format (s, "\n\n");
+ }
+
+ vlib_cli_output (vm, "%v", s);
+ vec_free (s);
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_udp_ping_cmd, static) =
+{
+ .path = "show udp-ping summary",
+ .short_help = "Summary of udp-ping",
+ .function = show_udp_ping_summary_cmd_fn,
+};
+/* *INDENT-ON* */
+
+/**
+ * @brief UDP-Ping Process node.
+ * @node udp-ping-process
+ *
+ * This is process node which wakes up when periodically to send
+ * out udp probe packets for all configured sessions.
+ *
+ * @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.
+ *
+ */
+uword
+udp_ping_process (vlib_main_t * vm,
+ vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ f64 now;
+ uword *event_data = 0;
+ int i;
+ ip46_udp_ping_flow *ip46_flow;
+
+ while (1)
+ {
+ vec_reset_length (event_data);
+ vlib_process_wait_for_event_or_clock (vm, udp_ping_main.timer_interval);
+ (void) vlib_process_get_events (vm, &event_data);
+ now = vlib_time_now (vm);
+
+ for (i = 0; i < vec_len (udp_ping_main.ip46_flow); i++)
+ {
+ if (pool_is_free_index (udp_ping_main.ip46_flow, i))
+ continue;
+
+ ip46_flow = pool_elt_at_index (udp_ping_main.ip46_flow, i);
+ if (ip46_flow->udp_data.next_send_time < now)
+ udp_ping_send_ip6_pak (udp_ping_main.vlib_main, ip46_flow);
+ }
+ }
+ return 0;
+}
+
+/**
+ * @brief HopByHop analyse function for udp-ping response.
+ *
+ * Walks through all hbh options present in udp-ping response
+ * and uses analyser library for the analysis.
+ *
+ */
+void
+udp_ping_analyse_hbh (vlib_buffer_t * b0,
+ u32 flow_id,
+ u16 src_port,
+ u16 dst_port,
+ ip6_hop_by_hop_option_t * opt0,
+ ip6_hop_by_hop_option_t * limit0, u16 len)
+{
+ u8 type0;
+ ip46_udp_ping_flow *ip46_flow;
+ u16 flow_index;
+ ioam_analyser_data_t *data;
+ ioam_e2e_option_t *e2e;
+ ioam_trace_option_t *trace;
+
+ ip46_flow = udp_ping_main.ip46_flow + flow_id;
+ flow_index = (src_port - ip46_flow->udp_data.start_src_port) *
+ (ip46_flow->udp_data.end_dst_port - ip46_flow->udp_data.start_dst_port +
+ 1);
+ flow_index += (dst_port - ip46_flow->udp_data.start_dst_port);
+ data = &(ip46_flow->udp_data.stats[flow_index].analyse_data);
+
+ data->pkt_counter++;
+ data->bytes_counter += len;
+
+ vnet_buffer (b0)->l2_classify.opaque_index =
+ ip46_flow->udp_data.stats[flow_index].flow_ctx;
+
+ while (opt0 < limit0)
+ {
+ type0 = opt0->type;
+ switch (type0)
+ {
+ case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST:
+ /* Add trace for here as it hasnt been done yet */
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
+ trace = (ioam_trace_option_t *) opt0;
+ if (PREDICT_FALSE
+ (trace->trace_hdr.ioam_trace_type & BIT_LOOPBACK_REPLY))
+ {
+ ip6_ioam_analyse_hbh_trace_loopback (data, &trace->trace_hdr,
+ (trace->hdr.length - 2));
+ return;
+ }
+ ip6_hbh_ioam_trace_data_list_handler (b0,
+ vlib_buffer_get_current (b0),
+ opt0);
+ (void) ip6_ioam_analyse_hbh_trace (data, &trace->trace_hdr, len,
+ (trace->hdr.length - 2));
+ break;
+ case HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE:
+ e2e = (ioam_e2e_option_t *) opt0;
+ (void) ip6_ioam_analyse_hbh_e2e (data, &e2e->e2e_hdr, len);
+ break;
+ case 0: /* Pad1 */
+ opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
+ continue;
+ case 1: /* PadN */
+ break;
+ default:
+ break;
+ }
+ opt0 = (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
+ sizeof (ip6_hop_by_hop_option_t));
+ }
+ ip46_flow->udp_data.stats[flow_index].retry = 0;
+}
+
+/**
+ * @brief UDP-Ping request/response handler function.
+ *
+ * Checks udp-ping packet type - request/response and handles them.
+ * If not udp-ping packet then, strips off hbh options and enques
+ * packet to protocol registered node to enable next protocol processing.
+ *
+ */
+void
+udp_ping_local_analyse (vlib_buffer_t * b0,
+ ip6_header_t * ip0,
+ ip6_hop_by_hop_header_t * hbh0, u16 * next0)
+{
+ ip6_main_t *im = &ip6_main;
+ ip_lookup_main_t *lm = &im->lookup_main;
+
+ *next0 = UDP_PING_NEXT_IP6_DROP;
+
+ if (PREDICT_TRUE (hbh0->protocol == IP_PROTOCOL_UDP))
+ {
+ ip6_hop_by_hop_option_t *opt0;
+ ip6_hop_by_hop_option_t *limit0;
+ u16 p_len0;
+ udp_ping_t *udp0;
+
+ /* Check for udp ping packet */
+ udp0 = (udp_ping_t *) ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
+ opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
+ if ((udp0->ping_data.probe_marker1 ==
+ clib_host_to_net_u32 (UDP_PING_PROBE_MARKER1)) &&
+ (udp0->ping_data.probe_marker2 ==
+ clib_host_to_net_u32 (UDP_PING_PROBE_MARKER2)))
+ {
+ if (udp0->ping_data.msg_type == UDP_PING_PROBE)
+ {
+ udp_ping_create_reply_from_probe_ip6 (ip0, hbh0, udp0);
+ /* Skip e2e processing */
+ vnet_buffer (b0)->l2_classify.opaque_index = 0x7FFFFFFF;
+ *next0 = UDP_PING_NEXT_IP6_LOOKUP;
+ return;
+ }
+
+ /* Reply */
+ opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
+ limit0 = (ip6_hop_by_hop_option_t *)
+ ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
+ p_len0 = clib_net_to_host_u16 (ip0->payload_length);
+ udp_ping_analyse_hbh (b0,
+ clib_net_to_host_u16 (udp0->
+ ping_data.sender_handle),
+ clib_net_to_host_u16 (udp0->udp.dst_port),
+ clib_net_to_host_u16 (udp0->udp.src_port),
+ opt0, limit0, p_len0);
+
+ /* UDP Ping packet, so return */
+ return;
+ }
+ }
+
+ /* If next header is SR, then destination may get overwritten to
+ * remote address. So pass it to SR processing as it may be local packet
+ * afterall
+ */
+ if (PREDICT_FALSE (hbh0->protocol == IPPROTO_IPV6_ROUTE))
+ goto end;
+
+ /* Other case remove hbh-ioam headers */
+ u64 *copy_dst0, *copy_src0;
+ u16 new_l0;
+
+ vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
+
+ new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
+ ((hbh0->length + 1) << 3);
+
+ ip0->payload_length = clib_host_to_net_u16 (new_l0);
+
+ ip0->protocol = hbh0->protocol;
+
+ copy_src0 = (u64 *) ip0;
+ copy_dst0 = copy_src0 + (hbh0->length + 1);
+ copy_dst0[4] = copy_src0[4];
+ copy_dst0[3] = copy_src0[3];
+ copy_dst0[2] = copy_src0[2];
+ copy_dst0[1] = copy_src0[1];
+ copy_dst0[0] = copy_src0[0];
+
+end:
+ *next0 = lm->local_next_by_ip_protocol[hbh0->protocol];
+ return;
+}
+
+/**
+ * @brief udp ping request/response packet receive node.
+ * @node udp-ping-local
+ *
+ * This function receives udp ping request/response packets and process them.
+ * For request packets, response is created and sent.
+ * For response packets, they are analysed and results stored.
+ *
+ * @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>udp_ping_local_analyse(p0, ip0, hbh0, &next0)</code>
+ * - Checks packet type - request/respnse and process them.
+ *
+ * <em>Next Index:</em>
+ * - Dispatches the packet to ip6-lookup/ip6-drop depending on type of packet.
+ */
+static uword
+udp_ping_local_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ udp_ping_next_t next_index;
+ u32 *from, *to_next, n_left_from, n_left_to_next;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ vlib_buffer_t *p0, *p1;
+ ip6_header_t *ip0, *ip1;
+ ip6_hop_by_hop_header_t *hbh0, *hbh1;
+ u16 next0, next1;
+ u32 pi0, pi1;
+
+ /* 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);
+
+ /* Prefetch 3 cache lines as we need to look deep into packet */
+ CLIB_PREFETCH (p2->data, 3 * CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (p3->data, 3 * CLIB_CACHE_LINE_BYTES, STORE);
+ }
+
+ pi0 = to_next[0] = from[0];
+ pi1 = to_next[1] = from[1];
+ from += 2;
+ n_left_from -= 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ p1 = vlib_get_buffer (vm, pi1);
+
+ ip0 = vlib_buffer_get_current (p0);
+ ip1 = vlib_buffer_get_current (p1);
+
+ hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
+ hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
+
+ udp_ping_local_analyse (p0, ip0, hbh0, &next0);
+ udp_ping_local_analyse (p1, ip1, hbh1, &next1);
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
+ {
+ if (p0->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ udp_ping_trace_t *t0 =
+ vlib_add_trace (vm, node, p0, sizeof (*t0));
+ udp_ping_t *udp0;
+
+ /* Check for udp ping packet */
+ udp0 =
+ (udp_ping_t *) ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
+ t0->src = ip0->src_address;
+ t0->dst = ip0->dst_address;
+ t0->src_port = clib_net_to_host_u16 (udp0->udp.src_port);
+ t0->dst_port = clib_net_to_host_u16 (udp0->udp.dst_port);
+ t0->handle =
+ clib_net_to_host_u16 (udp0->ping_data.sender_handle);
+ t0->msg_type = udp0->ping_data.msg_type;
+ t0->next_index = next0;
+ }
+ if (p1->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ udp_ping_trace_t *t1 =
+ vlib_add_trace (vm, node, p1, sizeof (*t1));
+ udp_ping_t *udp1;
+
+ /* Check for udp ping packet */
+ udp1 =
+ (udp_ping_t *) ((u8 *) hbh1 + ((hbh1->length + 1) << 3));
+ t1->src = ip1->src_address;
+ t1->dst = ip1->dst_address;
+ t1->src_port = clib_net_to_host_u16 (udp1->udp.src_port);
+ t1->dst_port = clib_net_to_host_u16 (udp1->udp.dst_port);
+ t1->handle =
+ clib_net_to_host_u16 (udp1->ping_data.sender_handle);
+ t1->msg_type = udp1->ping_data.msg_type;
+ t1->next_index = next1;
+ }
+ }
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ pi0, pi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vlib_buffer_t *p0;
+ ip6_header_t *ip0;
+ ip6_hop_by_hop_header_t *hbh0;
+ u16 next0;
+ u32 pi0;
+
+ pi0 = from[0];
+ to_next[0] = pi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ ip0 = vlib_buffer_get_current (p0);
+ hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
+
+ udp_ping_local_analyse (p0, ip0, hbh0, &next0);
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
+ {
+ if (p0->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ udp_ping_trace_t *t0 =
+ vlib_add_trace (vm, node, p0, sizeof (*t0));
+ udp_ping_t *udp0;
+
+ /* Check for udp ping packet */
+ udp0 =
+ (udp_ping_t *) ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
+ t0->src = ip0->src_address;
+ t0->dst = ip0->dst_address;
+ t0->src_port = clib_net_to_host_u16 (udp0->udp.src_port);
+ t0->dst_port = clib_net_to_host_u16 (udp0->udp.dst_port);
+ t0->handle =
+ clib_net_to_host_u16 (udp0->ping_data.sender_handle);
+ t0->msg_type = udp0->ping_data.msg_type;
+ t0->next_index = next0;
+ }
+ }
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ pi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+/*
+ * Node for udp-ping-local
+ */
+VLIB_REGISTER_NODE (udp_ping_local, static) =
+{
+ .function = udp_ping_local_node_fn,
+ .name = "udp-ping-local",
+ .vector_size = sizeof (u32),
+ .format_trace = format_udp_ping_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_next_nodes = UDP_PING_N_NEXT,
+ .next_nodes =
+ {
+ [UDP_PING_NEXT_DROP] = "error-drop",
+ [UDP_PING_NEXT_PUNT] = "error-punt",
+ [UDP_PING_NEXT_UDP_LOOKUP] = "ip6-udp-lookup",
+ [UDP_PING_NEXT_ICMP] = "ip6-icmp-input",
+ [UDP_PING_NEXT_IP6_LOOKUP] = "ip6-lookup",
+ [UDP_PING_NEXT_IP6_DROP] = "ip6-drop",
+ },
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+udp_ping_init (vlib_main_t * vm)
+{
+ clib_error_t *error = 0;
+
+ udp_ping_main.vlib_main = vm;
+ udp_ping_main.vnet_main = vnet_get_main ();
+ udp_ping_main.timer_interval = 1e9;
+
+ if ((error = vlib_call_init_function (vm, ip_main_init)))
+ return (error);
+
+ ip6_register_protocol (IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS,
+ udp_ping_local.index);
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (udp_ping_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_packet.h b/src/plugins/ioam/udp-ping/udp_ping_packet.h
new file mode 100644
index 00000000..09dcb1c2
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_packet.h
@@ -0,0 +1,154 @@
+/*
+ * 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_UDP_PING_UDP_PING_PACKET_H_
+#define PLUGINS_IOAM_PLUGIN_IOAM_UDP_PING_UDP_PING_PACKET_H_
+
+#include <vppinfra/clib.h>
+#include <vnet/ip/ip6_hop_by_hop_packet.h>
+#include <vnet/udp/udp_packet.h>
+
+#define UDP_PING_PROBE 1
+#define UDP_PING_REPLY 2
+
+#define UDP_PING_PROBE_MARKER1 0xDEAD
+#define UDP_PING_PROBE_MARKER2 0xBEEF
+
+/*
+ * Refer to:
+ * https://tools.ietf.org/html/draft-lapukhov-dataplane-probe-01
+ * 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Probe Marker (1) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Probe Marker (2) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Version | Message Type | Flags |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Telemetry Request Vector |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Hop Limit | Hop Count | Must Be Zero |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Maximum Length | Current Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Sender's Handle | Sequence Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * (1) The "Probe Marker" fields are arbitrary 32-bit values generally
+ used by the network elements to identify the packet as a probe
+ packet. These fields should be interpreted as unsigned integer
+ values, stored in network byte order. For example, a network
+ element may be configured to recognize a UDP packet destined to
+ port 31337 and having 0xDEAD 0xBEEF as the values in "Probe
+ Marker" field as an active probe, and treat it respectively.
+
+ (2) "Version Number" is currently set to 1.
+
+ (3) The "Message Type" field value could be either "1" - "Probe" or
+ "2" - "Probe Reply"
+
+ (4) The "Flags" field is 8 bits, and defines the following flags:
+
+ (5)
+ (1) "Overflow" (O-bit) (least significant bit). This bit is
+ set by the network element if the number of records on the
+ packet is at the maximum limit as specified by the packet:
+ i.e. the packet is already "full" of telemetry
+ information.
+
+ (6) "Telemetry Request Vector" is a 32-bit long field that requests
+ well-known inband telemetry information from the network
+ elements on the path. A bit set in this vector translates to a
+ request of a particular type of information. The following
+ types/bits are currently defined, starting with the least
+ significant bit first:
+
+ (1) Bit 0: Device identifier.
+
+ (2) Bit 1: Timestamp.
+
+ (3) Bit 2: Queueing delay.
+
+ (4) Bit 3: Ingress/Egress port identifiers.
+
+ (5) Bit 31: Opaque state snapshot request.
+
+ (7) "Hop Limit" is defined only for "Message Type" of "1"
+ ("Probe"). For "Probe Reply" the "Hop Limit" field must be set
+ to zero. This field is treated as an integer value
+ representing the number of network elements. See the Section 4
+ section on the intended use of the field.
+
+ (8) The "Hop Count" field specifies the current number of hops of
+ capable network elements the packet has transit through. It
+ begins with zero and must be incremented by one for every
+ network element that adds a telemetry record. Combined with a
+ push mechanism, this simplifies the work for the subsequent
+ network element and the packet receiver. The subsequent
+ network element just needs to parse the template and then
+ insert new record(s) immediately after the template.
+
+ (9) The "Max Length" field specifies the maximum length of the
+ telemetry payload in bytes. Given that the sender knows the
+ minimum path MTU, the sender can set the maximum of payload
+ bytes allowed before exceeding the MTU. Thus, a simple
+ comparison between "Current Length" and "Max Length" allows to
+ decide whether or not data could be added.
+
+ (10) The "Current Length" field specifies the current length of data
+ stored in the probe. This field is incremented by eacn network
+ element by the number of bytes it has added with the telemetry
+ data frame.
+
+ (11) The "Sender's Handle" field is set by the sender to allow the
+ receiver to identify a particular originator of probe packets.
+ Along with "Sequence Number" it allows for tracking of packet
+ order and loss within the network.
+
+ *
+ */
+typedef struct
+{
+ u32 probe_marker1;
+ u32 probe_marker2;
+ u8 version;
+ u8 msg_type;
+ u16 flags;
+ u32 tel_req_vec;
+ u8 hop_limit;
+ u8 hop_count;
+ u16 reserve;
+ u16 max_len;
+ u16 cur_len;
+ u16 sender_handle;
+ u16 seq_no;
+} udp_ping_data;
+
+typedef struct
+{
+ udp_header_t udp;
+ udp_ping_data ping_data;
+} udp_ping_t;
+
+#endif /* PLUGINS_IOAM_PLUGIN_IOAM_UDP_PING_UDP_PING_PACKET_H_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_test.c b/src/plugins/ioam/udp-ping/udp_ping_test.c
new file mode 100644
index 00000000..3128f274
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_test.c
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+/*
+ *------------------------------------------------------------------
+ * udp_ping_test.c - test harness for udp ping plugin
+ *------------------------------------------------------------------
+ */
+
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibsocket/api.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/ip.h>
+
+/* Declare message IDs */
+#include <ioam/udp-ping/udp_ping_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_typedefs
+
+/* declare message handlers for each api */
+
+#define vl_endianfun /* define message structures */
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_printfun
+
+/* Get the API version number. */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <ioam/udp-ping/udp_ping_all_api_h.h>
+#undef vl_api_version
+
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+ vat_main_t *vat_main;
+} udp_ping_test_main_t;
+
+udp_ping_test_main_t udp_ping_test_main;
+
+#define foreach_standard_reply_retval_handler \
+_(udp_ping_add_del_reply) \
+_(udp_ping_export_reply)
+
+#define _(n) \
+ static void vl_api_##n##_t_handler \
+ (vl_api_##n##_t * mp) \
+ { \
+ vat_main_t * vam = udp_ping_test_main.vat_main; \
+ i32 retval = ntohl(mp->retval); \
+ if (vam->async_mode) { \
+ vam->async_errors += (retval < 0); \
+ } else { \
+ vam->retval = retval; \
+ vam->result_ready = 1; \
+ } \
+ }
+foreach_standard_reply_retval_handler;
+#undef _
+
+/*
+ * Table of message reply handlers, must include boilerplate handlers
+ * we just generated
+ */
+#define foreach_vpe_api_reply_msg \
+_(UDP_PING_ADD_DEL_REPLY, udp_ping_add_del_reply) \
+_(UDP_PING_EXPORT_REPLY, udp_ping_export_reply) \
+
+
+/* M: construct, but don't yet send a message */
+
+#define M(T,t) \
+do { \
+ vam->result_ready = 0; \
+ mp = vl_msg_api_alloc(sizeof(*mp)); \
+ memset (mp, 0, sizeof (*mp)); \
+ mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \
+ mp->client_index = vam->my_client_index; \
+} while(0);
+
+/* S: send a message */
+#define S (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp))
+
+/* W: wait for results, with timeout */
+#define W \
+do { \
+ timeout = vat_time_now (vam) + 5.0; \
+ \
+ while (vat_time_now (vam) < timeout) { \
+ if (vam->result_ready == 1) { \
+ return (vam->retval); \
+ } \
+ } \
+ return -99; \
+} while(0);
+
+static int
+api_udp_ping_add_del_req (vat_main_t * vam)
+{
+ udp_ping_test_main_t *sm = &udp_ping_test_main;
+ unformat_input_t *input = vam->input;
+ vl_api_udp_ping_add_del_req_t *mp;
+ int rv = 0;
+ ip6_address_t dst, src;
+ u32 start_src_port, end_src_port;
+ u32 start_dst_port, end_dst_port;
+ u32 interval;
+ u8 is_disable = 0;
+ f64 timeout;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "src %U", unformat_ip6_address, &src))
+ ;
+ else if (unformat (input, "start-src-port %d", &start_src_port))
+ ;
+ else if (unformat (input, "end-src-port %d", &end_src_port))
+ ;
+ else if (unformat (input, "start-dst-port %d", &start_dst_port))
+ ;
+ else if (unformat (input, "end-dst-port %d", &end_dst_port))
+ ;
+ else if (unformat (input, "dst %U", unformat_ip6_address, &dst))
+ ;
+ else if (unformat (input, "interval %d", &interval))
+ ;
+ else if (unformat (input, "disable"))
+ is_disable = 1;
+ else
+ break;
+ }
+
+ M (UDP_PING_ADD_DEL_REQ, udp_ping_add);
+
+ clib_memcpy (mp->src_ip_address, &src, 16);
+ clib_memcpy (mp->dst_ip_address, &dst, 16);
+ mp->start_src_port = (u16) start_src_port;
+ mp->end_src_port = (u16) end_src_port;
+ mp->start_dst_port = (u16) start_dst_port;
+ mp->end_dst_port = (u16) end_dst_port;
+ mp->interval = (u16) interval;
+ mp->is_ipv4 = 0;
+ mp->dis = is_disable;
+
+ S;
+ W;
+
+ return (rv);
+}
+
+static int
+api_udp_ping_export_req (vat_main_t * vam)
+{
+ udp_ping_test_main_t *sm = &udp_ping_test_main;
+ unformat_input_t *input = vam->input;
+ vl_api_udp_ping_export_req_t *mp;
+ int rv = 0;
+ int is_add = 1;
+ f64 timeout;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "export"))
+ is_add = 1;
+ else if (unformat (input, "disable"))
+ is_add = 0;
+ else
+ break;
+ }
+
+ M (UDP_PING_EXPORT_REQ, udp_ping_export);
+
+ mp->enable = is_add;
+
+ S;
+ W;
+
+ return (rv);
+}
+
+/*
+ * List of messages that the api test plugin sends,
+ * and that the data plane plugin processes
+ */
+#define foreach_vpe_api_msg \
+_(udp_ping_add_del_req, "src <local IPv6 address> start-src-port <first local port> "\
+ "end-src-port <last local port> " \
+ "dst <remote IPv6 address> start-dst-port <first destination port> "\
+ "end-dst-port <last destination port> "\
+ "interval <time interval in sec for which ping packet will be sent> "\
+ "[disable]") \
+_(udp_ping_export_req, "export [disable]") \
+
+
+void
+vat_api_hookup (vat_main_t * vam)
+{
+ udp_ping_test_main_t *sm = &udp_ping_test_main;
+ /* Hook up handlers for replies from the data plane plug-in */
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \
+ #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_vpe_api_reply_msg;
+#undef _
+
+ /* API messages we can send */
+#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
+ foreach_vpe_api_msg;
+#undef _
+
+ /* Help strings */
+#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
+ foreach_vpe_api_msg;
+#undef _
+}
+
+clib_error_t *
+vat_plugin_register (vat_main_t * vam)
+{
+ udp_ping_test_main_t *sm = &udp_ping_test_main;
+ u8 *name;
+
+ sm->vat_main = vam;
+
+ name = format (0, "udp_ping_%08x%c", api_version, 0);
+ sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
+
+ if (sm->msg_id_base != (u16) ~ 0)
+ vat_api_hookup (vam);
+
+ vec_free (name);
+
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_util.c b/src/plugins/ioam/udp-ping/udp_ping_util.c
new file mode 100644
index 00000000..55f48ea4
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_util.c
@@ -0,0 +1,330 @@
+/*
+ * 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/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/ip6_hop_by_hop.h>
+#include <ioam/encap/ip6_ioam_e2e.h>
+#include <ioam/encap/ip6_ioam_trace.h>
+#include <ioam/udp-ping/udp_ping_packet.h>
+#include <ioam/udp-ping/udp_ping.h>
+
+#define UDP_PING_REWRITE_LEN 1000
+
+u16
+udp_ping_fill_udp_data (udp_ping_t * udp_ping,
+ u16 src_port, u16 dst_port, u8 msg_type, u16 ctx)
+{
+ /* Populate udp ping header */
+ udp_ping->udp.src_port = clib_host_to_net_u16 (src_port);
+ udp_ping->udp.dst_port = clib_host_to_net_u16 (dst_port);
+ udp_ping->udp.length = clib_host_to_net_u16 (sizeof (udp_ping_t));
+ udp_ping->udp.checksum = 0;
+ udp_ping->ping_data.probe_marker1 =
+ clib_host_to_net_u32 (UDP_PING_PROBE_MARKER1);
+ udp_ping->ping_data.probe_marker2 =
+ clib_host_to_net_u32 (UDP_PING_PROBE_MARKER2);
+ udp_ping->ping_data.version = 1;
+ udp_ping->ping_data.msg_type = msg_type;
+ udp_ping->ping_data.flags = clib_host_to_net_u16 (0);
+ udp_ping->ping_data.tel_req_vec = clib_host_to_net_u16 (0);
+ udp_ping->ping_data.hop_limit = 254;
+ udp_ping->ping_data.hop_count = 0;
+ udp_ping->ping_data.reserve = clib_host_to_net_u16 (0);
+ udp_ping->ping_data.max_len =
+ udp_ping->ping_data.cur_len = clib_host_to_net_u16 (0);
+ udp_ping->ping_data.sender_handle = clib_host_to_net_u16 (ctx);
+ udp_ping->ping_data.seq_no = clib_host_to_net_u16 (0);
+
+ return (sizeof (udp_ping_t));
+}
+
+/**
+ * @brief Frame IPv6 udp-ping probe packet.
+ *
+ * Creates IPv6 UDP-Ping probe packet along with iOAM headers.
+ *
+ */
+int
+udp_ping_create_ip6_pak (u8 * buf, /*u16 len, */
+ ip6_address_t src, ip6_address_t dst,
+ u16 src_port, u16 dst_port, u8 msg_type, u16 ctx)
+{
+ ip6_header_t *ip0;
+ ip6_hop_by_hop_header_t *hbh0;
+ //trace_profile *profile = NULL;
+ u16 hbh_len = 0, rnd_size = 0, ip0_len = 0, udp_len = 0;
+ u16 trace_len = 0, trace_data_size = 0;
+ u16 e2e_len = sizeof (ioam_e2e_option_t) - sizeof (ip6_hop_by_hop_option_t);
+ u8 *current = NULL;
+ ioam_trace_option_t *trace_option;
+ ioam_e2e_option_t *e2e;
+
+ ip0 = (ip6_header_t *) buf;
+
+ ip0->ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 (0x6 << 28);
+
+ ip0->protocol = IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS;
+ ip0->hop_limit = 255;
+
+ ip0->src_address = src;
+ ip0->dst_address = dst;
+
+ hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
+
+ /* Calculate hbh header len */
+ //profile = trace_profile_find();
+ trace_data_size = fetch_trace_data_size (TRACE_TYPE_IF_TS_APP);
+ /* We need 2 times data for trace as packet traverse back to source */
+ trace_len = sizeof (ioam_trace_option_t) +
+ (5 * trace_data_size * 2) - sizeof (ip6_hop_by_hop_option_t);
+ //(profile->num_elts * trace_data_size * 2);
+ hbh_len = e2e_len + trace_len + sizeof (ip6_hop_by_hop_header_t);
+ rnd_size = (hbh_len + 7) & ~7;
+
+ /* Length of header in 8 octet units, not incl first 8 octets */
+ hbh0->length = (rnd_size >> 3) - 1;
+ hbh0->protocol = IP_PROTOCOL_UDP;
+
+ /* Populate hbh header */
+ current = (u8 *) (hbh0 + 1);
+
+ /* Populate trace */
+ trace_option = (ioam_trace_option_t *) current;
+ trace_option->hdr.type = HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST |
+ HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE;
+ trace_option->hdr.length = trace_len;
+ trace_option->trace_hdr.ioam_trace_type =
+ TRACE_TYPE_IF_TS_APP & TRACE_TYPE_MASK;
+
+ trace_option->trace_hdr.data_list_elts_left = 5 * 2;
+ //profile->num_elts * 2;
+
+ current += trace_option->hdr.length + sizeof (ip6_hop_by_hop_option_t);
+
+ /* Populate e2e */
+ e2e = (ioam_e2e_option_t *) current;
+ e2e->hdr.type = HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE;
+ e2e->hdr.length = e2e_len;
+
+ /* Move past hbh header */
+ current = ((u8 *) hbh0) + ((hbh0->length + 1) << 3);
+
+ /* Populate udp ping header */
+ udp_len = udp_ping_fill_udp_data ((udp_ping_t *) current,
+ src_port, dst_port, msg_type, ctx);
+
+ /* Calculate total length and set it in ip6 header */
+ ip0_len = ((hbh0->length + 1) << 3) + udp_len;
+ //ip0_len = (len > ip0_len) ? len : ip0_len;
+ ip0->payload_length = clib_host_to_net_u16 (ip0_len);
+
+ return (ip0_len + sizeof (ip6_header_t));
+}
+
+int
+udp_ping_compare_flow (ip46_address_t src, ip46_address_t dst,
+ u16 start_src_port, u16 end_src_port,
+ u16 start_dst_port, u16 end_dst_port,
+ ip46_udp_ping_flow * flow)
+{
+ if ((0 == ip46_address_cmp (&flow->src, &src)) &&
+ (0 == ip46_address_cmp (&flow->dst, &dst)) &&
+ (flow->udp_data.start_src_port == start_src_port) &&
+ (flow->udp_data.end_src_port == end_src_port) &&
+ (flow->udp_data.start_dst_port == start_dst_port) &&
+ (flow->udp_data.end_dst_port == end_dst_port))
+ {
+ return 0;
+ }
+
+ return -1;
+}
+
+void
+udp_ping_populate_flow (ip46_address_t src, ip46_address_t dst,
+ u16 start_src_port, u16 end_src_port,
+ u16 start_dst_port, u16 end_dst_port,
+ u16 interval, u8 fault_det, ip46_udp_ping_flow * flow)
+{
+ flow->src = src;
+ flow->dst = dst;
+ flow->udp_data.start_src_port = start_src_port;
+ flow->udp_data.end_src_port = end_src_port;
+ flow->udp_data.start_dst_port = start_dst_port;
+ flow->udp_data.end_dst_port = end_dst_port;
+ flow->udp_data.interval = interval;
+ flow->udp_data.next_send_time = 0;
+ flow->fault_det = fault_det;
+}
+
+void
+udp_ping_create_rewrite (ip46_udp_ping_flow * flow, u16 ctx)
+{
+ u16 src_port;
+ u16 dst_port;
+ u16 no_flows;
+ int i;
+ udp_ping_flow_data *stats;
+
+ no_flows =
+ (flow->udp_data.end_dst_port - flow->udp_data.start_dst_port) + 1;
+ no_flows *=
+ ((flow->udp_data.end_src_port - flow->udp_data.start_src_port) + 1);
+
+ vec_validate_aligned (flow->udp_data.stats,
+ no_flows - 1, CLIB_CACHE_LINE_BYTES);
+
+ i = 0;
+ for (src_port = flow->udp_data.start_src_port;
+ src_port <= flow->udp_data.end_src_port; src_port++)
+ {
+ for (dst_port = flow->udp_data.start_dst_port;
+ dst_port <= flow->udp_data.end_dst_port; dst_port++)
+ {
+ u8 *rewrite = NULL;
+
+ stats = flow->udp_data.stats + i;
+ ioam_analyse_init_data (&stats->analyse_data);
+ stats->analyse_data.is_free = 0;
+
+ vec_validate (rewrite, UDP_PING_REWRITE_LEN - 1);
+ stats->ping_rewrite = rewrite;
+ stats->rewrite_len =
+ udp_ping_create_ip6_pak (rewrite,
+ flow->src.ip6, flow->dst.ip6,
+ src_port, dst_port, UDP_PING_PROBE, ctx);
+ /* For each flow we need to create ioam e2e flow */
+ stats->flow_ctx = ioam_flow_add (1, (u8 *) "udp_ping"); //FIXME
+ i++;
+ }
+ }
+}
+
+void
+udp_ping_free_flow_data (ip46_udp_ping_flow * flow)
+{
+ int i;
+ udp_ping_flow_data *stats;
+
+ for (i = 0; i < vec_len (flow->udp_data.stats); i++)
+ {
+ stats = flow->udp_data.stats + i;
+ vec_free (stats->ping_rewrite);
+ stats->ping_rewrite = NULL;
+ stats->rewrite_len = 0;
+ }
+
+ vec_free (flow->udp_data.stats);
+ flow->udp_data.stats = NULL;
+}
+
+/**
+ * @brief Create and send ipv6 udp-ping probe packet.
+ *
+ */
+void
+udp_ping_send_ip6_pak (vlib_main_t * vm, ip46_udp_ping_flow * flow)
+{
+ u16 no_pak;
+ u32 *buffers = NULL;
+ int i;
+ vlib_buffer_t *b0;
+ udp_ping_flow_data *stats;
+ vlib_frame_t *nf = 0;
+ u32 *to_next;
+ vlib_node_t *next_node;
+
+ next_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup");
+ nf = vlib_get_frame_to_node (vm, next_node->index);
+ nf->n_vectors = 0;
+ to_next = vlib_frame_vector_args (nf);
+
+ no_pak = vec_len (flow->udp_data.stats);
+ vec_validate (buffers, (no_pak - 1));
+ if (vlib_buffer_alloc (vm, buffers, vec_len (buffers)) != no_pak)
+ {
+ //Error
+ return;
+ }
+
+ for (i = 0; i < no_pak; i++)
+ {
+ int bogus;
+ b0 = vlib_get_buffer (vm, buffers[i]);
+ stats = flow->udp_data.stats + i;
+ clib_memcpy (b0->data, stats->ping_rewrite, stats->rewrite_len);
+ b0->current_data = 0;
+ b0->current_length = stats->rewrite_len;
+ b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
+
+ /* If session is going down, then set path down */
+ if ((stats->retry != 0) && ((stats->retry % MAX_PING_RETRIES) == 0))
+ ip6_ioam_analyse_set_paths_down (&stats->analyse_data);
+
+ stats->retry++;
+ stats->analyse_data.pkt_sent++;
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
+ vnet_buffer (b0)->l2_classify.opaque_index = stats->flow_ctx;
+
+ ip6_header_t *ip6 = vlib_buffer_get_current (b0);
+ ip6_hop_by_hop_header_t *hbh = (ip6_hop_by_hop_header_t *) (ip6 + 1);
+ udp_header_t *udp =
+ (udp_header_t *) ((u8 *) hbh + ((hbh->length + 1) << 3));
+
+ /* If session is down, then set loopback flag in probe.
+ * This is for fault isolation.
+ */
+ if (flow->fault_det && (stats->retry > MAX_PING_RETRIES))
+ {
+ ioam_trace_option_t *opt = (ioam_trace_option_t *)
+ ip6_hbh_get_option (hbh, HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
+ ip6_hbh_ioam_trace_set_bit (opt, BIT_LOOPBACK);
+ }
+
+ /* Checksum not pre-computed as we intend to vary packet length for every
+ * probe. its isnt done yet, but to be taken up later.
+ */
+ udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip6, &bogus);
+ ASSERT (bogus == 0);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+
+ if (nf->n_vectors == VLIB_FRAME_SIZE)
+ {
+ vlib_put_frame_to_node (vm, next_node->index, nf);
+ nf = vlib_get_frame_to_node (vm, next_node->index);
+ nf->n_vectors = 0;
+ to_next = vlib_frame_vector_args (nf);
+ }
+ *to_next = buffers[i];
+ nf->n_vectors++;
+ to_next++;
+ }
+ vlib_put_frame_to_node (vm, next_node->index, nf);
+
+ flow->udp_data.next_send_time =
+ vlib_time_now (vm) + flow->udp_data.interval;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ioam/udp-ping/udp_ping_util.h b/src/plugins/ioam/udp-ping/udp_ping_util.h
new file mode 100644
index 00000000..fcaf27bd
--- /dev/null
+++ b/src/plugins/ioam/udp-ping/udp_ping_util.h
@@ -0,0 +1,83 @@
+/*
+ * 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_UDP_PING_UDP_PING_UTIL_H_
+#define PLUGINS_IOAM_PLUGIN_IOAM_UDP_PING_UDP_PING_UTIL_H_
+
+int udp_ping_create_ip6_pak (u8 * buf, /*u16 len, */
+ ip6_address_t src, ip6_address_t dst,
+ u16 src_port, u16 dst_port,
+ u8 msg_type, u16 ctx);
+
+int
+udp_ping_compare_flow (ip46_address_t src, ip46_address_t dst,
+ u16 start_src_port, u16 end_src_port,
+ u16 start_dst_port, u16 end_dst_port,
+ ip46_udp_ping_flow * flow);
+
+void
+udp_ping_populate_flow (ip46_address_t src, ip46_address_t dst,
+ u16 start_src_port, u16 end_src_port,
+ u16 start_dst_port, u16 end_dst_port,
+ u16 interval, u8 fault_det,
+ ip46_udp_ping_flow * flow);
+
+void udp_ping_free_flow_data (ip46_udp_ping_flow * flow);
+
+void udp_ping_create_rewrite (ip46_udp_ping_flow * flow, u16 ctx);
+
+void udp_ping_send_ip6_pak (vlib_main_t * vm, ip46_udp_ping_flow * flow);
+
+/**
+ * @brief Create and send ipv6 udp-ping response packet.
+ *
+ */
+always_inline void
+udp_ping_create_reply_from_probe_ip6 (ip6_header_t * ip,
+ ip6_hop_by_hop_header_t * hbh,
+ udp_ping_t * udp)
+{
+ ip6_address_t src;
+ u16 src_port;
+ ioam_trace_option_t *trace;
+
+ src = ip->src_address;
+
+ ip->src_address = ip->dst_address;
+ ip->dst_address = src;
+
+ trace = (ioam_trace_option_t *)
+ ip6_hbh_get_option (hbh, HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST);
+ ip6_hbh_ioam_trace_reset_bit (trace, BIT_LOOPBACK);
+
+ /* No need of endian transform */
+ src_port = udp->udp.src_port;
+
+ udp->udp.src_port = udp->udp.dst_port;
+ udp->udp.dst_port = src_port;
+ udp->udp.checksum = 0; //FIXME
+
+ udp->ping_data.msg_type = UDP_PING_REPLY;
+}
+
+#endif /* PLUGINS_IOAM_PLUGIN_IOAM_UDP_PING_UDP_PING_UTIL_H_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h
index cf40fbb3..535f24c0 100644
--- a/src/vnet/ip/ip6.h
+++ b/src/vnet/ip/ip6.h
@@ -445,11 +445,30 @@ always_inline u32
ip6_compute_flow_hash (const ip6_header_t * ip,
flow_hash_config_t flow_hash_config)
{
- tcp_header_t *tcp = (void *) (ip + 1);
+ tcp_header_t *tcp;
u64 a, b, c;
u64 t1, t2;
- uword is_tcp_udp = (ip->protocol == IP_PROTOCOL_TCP
- || ip->protocol == IP_PROTOCOL_UDP);
+ uword is_tcp_udp = 0;
+ u8 protocol = ip->protocol;
+
+ if (PREDICT_TRUE
+ ((ip->protocol == IP_PROTOCOL_TCP)
+ || (ip->protocol == IP_PROTOCOL_UDP)))
+ {
+ is_tcp_udp = 1;
+ tcp = (void *) (ip + 1);
+ }
+ else if (ip->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)
+ {
+ ip6_hop_by_hop_header_t *hbh = (ip6_hop_by_hop_header_t *) (ip + 1);
+ if ((hbh->protocol == IP_PROTOCOL_TCP) ||
+ (hbh->protocol == IP_PROTOCOL_UDP))
+ {
+ is_tcp_udp = 1;
+ tcp = (tcp_header_t *) ((u8 *) hbh + ((hbh->length + 1) << 3));
+ }
+ protocol = hbh->protocol;
+ }
t1 = (ip->src_address.as_u64[0] ^ ip->src_address.as_u64[1]);
t1 = (flow_hash_config & IP_FLOW_HASH_SRC_ADDR) ? t1 : 0;
@@ -459,7 +478,7 @@ ip6_compute_flow_hash (const ip6_header_t * ip,
a = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ? t2 : t1;
b = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ? t1 : t2;
- b ^= (flow_hash_config & IP_FLOW_HASH_PROTO) ? ip->protocol : 0;
+ b ^= (flow_hash_config & IP_FLOW_HASH_PROTO) ? protocol : 0;
t1 = is_tcp_udp ? tcp->src : 0;
t2 = is_tcp_udp ? tcp->dst : 0;
diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c
index 91a303d4..b80c757a 100644
--- a/src/vnet/ip/ip6_forward.c
+++ b/src/vnet/ip/ip6_forward.c
@@ -1161,7 +1161,8 @@ ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
uword));
}
- /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets) */
+ /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets)
+ * or UDP-Ping packets */
if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
{
u32 skip_bytes;
@@ -1169,7 +1170,8 @@ ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
(ip6_hop_by_hop_ext_t *) data_this_buffer;
/* validate really icmp6 next */
- ASSERT (ext_hdr->next_hdr == IP_PROTOCOL_ICMP6);
+ ASSERT ((ext_hdr->next_hdr == IP_PROTOCOL_ICMP6)
+ || (ext_hdr->next_hdr == IP_PROTOCOL_UDP));
skip_bytes = 8 * (1 + ext_hdr->n_data_u64s);
data_this_buffer = (void *) ((u8 *) data_this_buffer + skip_bytes);
@@ -1317,23 +1319,6 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
len_diff0 = 0;
len_diff1 = 0;
- /* Skip HBH local processing */
- if (PREDICT_FALSE
- (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
- {
- ip6_hop_by_hop_ext_t *ext_hdr =
- (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
- next0 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
- type0 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
- }
- if (PREDICT_FALSE
- (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
- {
- ip6_hop_by_hop_ext_t *ext_hdr =
- (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
- next1 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
- type1 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
- }
if (PREDICT_TRUE (IP_PROTOCOL_UDP == ip6_locate_header (p0, ip0,
IP_PROTOCOL_UDP,
&udp_offset0)))
@@ -1458,15 +1443,6 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
good_l4_checksum0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
len_diff0 = 0;
- /* Skip HBH local processing */
- if (PREDICT_FALSE
- (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
- {
- ip6_hop_by_hop_ext_t *ext_hdr =
- (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
- next0 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
- type0 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
- }
if (PREDICT_TRUE (IP_PROTOCOL_UDP == ip6_locate_header (p0, ip0,
IP_PROTOCOL_UDP,
&udp_offset0)))
diff --git a/src/vnet/ip/ip6_hop_by_hop.h b/src/vnet/ip/ip6_hop_by_hop.h
index 9574f0a5..5f12f647 100644
--- a/src/vnet/ip/ip6_hop_by_hop.h
+++ b/src/vnet/ip/ip6_hop_by_hop.h
@@ -230,6 +230,42 @@ ioam_flow_add (u8 encap, u8 * flow_name)
return (index);
}
+always_inline ip6_hop_by_hop_option_t *
+ip6_hbh_get_option (ip6_hop_by_hop_header_t * hbh0, u8 option_to_search)
+{
+ ip6_hop_by_hop_option_t *opt0, *limit0;
+ u8 type0;
+
+ if (!hbh0)
+ return NULL;
+
+ opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
+ limit0 = (ip6_hop_by_hop_option_t *)
+ ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
+
+ /* Scan the set of h-b-h options, process ones that we understand */
+ 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 (type0 == option_to_search)
+ return opt0;
+ break;
+ }
+ opt0 =
+ (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
+ sizeof (ip6_hop_by_hop_option_t));
+ }
+ return NULL;
+}
+
#endif /* __included_ip6_hop_by_hop_ioam_h__ */
/*