diff options
author | AkshayaNadahalli <anadahal@cisco.com> | 2017-01-23 22:05:35 +0530 |
---|---|---|
committer | AkshayaNadahalli <anadahal@cisco.com> | 2017-03-07 14:08:22 +0000 |
commit | 1b563527c143903b6e7e79b5978af5310372f605 (patch) | |
tree | 7b673272cb930e03b77dd376f581d903c219a566 | |
parent | 066f034b903bda6e938bec1b12f01edef65ee9c4 (diff) |
In-band OAM active probe (VPP-471)
Change-Id: Icf0ddf76ba1c8b588c79387284cd0349ebc6e45f
Signed-off-by: AkshayaNadahalli <anadahal@cisco.com>
23 files changed, 2742 insertions, 59 deletions
diff --git a/src/plugins/ioam.am b/src/plugins/ioam.am index 4346e3c0ccb..d3816413d9f 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 3c69d71f349..b7576012e56 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 afda3099c9a..17fcf7a5b7e 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 9be31d2e5ed..b435506187d 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 fcec4a1447f..fb83403da8f 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 019771232b4..08bf554bbdd 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 f1eb1bf0957..e31825614c2 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 620b70a8c22..4eda6110d24 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 7065b41e9c9..c8b504701e4 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 00000000000..87945816044 --- /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 00000000000..26c4201900e --- /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 00000000000..1ed2e10b72d --- /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 00000000000..8cb8cc96224 --- /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 00000000000..ce62d98f861 --- /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 00000000000..dded18842d2 --- /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 00000000000..4de8fe2f894 --- /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 00000000000..09dcb1c20ee --- /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 00000000000..3128f274260 --- /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 00000000000..55f48ea4999 --- /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 00000000000..fcaf27bd485 --- /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 cf40fbb37aa..535f24c05d1 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 91a303d4857..b80c757a6d3 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 9574f0a5ecc..5f12f647dba 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__ */ /* |