In-band OAM active probe (VPP-471) 24/4924/11
authorAkshayaNadahalli <anadahal@cisco.com>
Mon, 23 Jan 2017 16:35:35 +0000 (22:05 +0530)
committerAkshayaNadahalli <anadahal@cisco.com>
Tue, 7 Mar 2017 14:08:22 +0000 (14:08 +0000)
Change-Id: Icf0ddf76ba1c8b588c79387284cd0349ebc6e45f
Signed-off-by: AkshayaNadahalli <anadahal@cisco.com>
23 files changed:
src/plugins/ioam.am
src/plugins/ioam/analyse/ioam_analyse.h
src/plugins/ioam/analyse/ioam_summary_export.c
src/plugins/ioam/analyse/ioam_summary_export.h
src/plugins/ioam/encap/ip6_ioam_e2e.h
src/plugins/ioam/encap/ip6_ioam_seqno.c
src/plugins/ioam/encap/ip6_ioam_trace.c
src/plugins/ioam/encap/ip6_ioam_trace.h
src/plugins/ioam/lib-trace/trace_util.h
src/plugins/ioam/udp-ping/udp_ping.api [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping.h [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_all_api_h.h [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_api.c [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_export.c [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_msg_enum.h [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_node.c [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_packet.h [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_test.c [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_util.c [new file with mode: 0644]
src/plugins/ioam/udp-ping/udp_ping_util.h [new file with mode: 0644]
src/vnet/ip/ip6.h
src/vnet/ip/ip6_forward.c
src/vnet/ip/ip6_hop_by_hop.h

index 4346e3c..d381641 100644 (file)
@@ -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
 
index 3c69d71..b757601 100644 (file)
@@ -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++;
     }
 
index afda309..17fcf7a 100644 (file)
@@ -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;
 
index 9be31d2..b435506 100755 (executable)
@@ -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);
index fcec4a1..fb83403 100644 (file)
@@ -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* */
index 0197712..08bf554 100644 (file)
@@ -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);
index f1eb1bf..e318256 100644 (file)
@@ -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
index 620b70a..4eda611 100644 (file)
@@ -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_ */
 
 /*
index 7065b41..c8b5047 100644 (file)
@@ -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 (file)
index 0000000..8794581
--- /dev/null
@@ -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 (file)
index 0000000..26c4201
--- /dev/null
@@ -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 (file)
index 0000000..1ed2e10
--- /dev/null
@@ -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 (file)
index 0000000..8cb8cc9
--- /dev/null
@@ -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 (file)
index 0000000..ce62d98
--- /dev/null
@@ -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 (file)
index 0000000..dded188
--- /dev/null
@@ -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 (file)
index 0000000..4de8fe2
--- /dev/null
@@ -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 (file)
index 0000000..09dcb1c
--- /dev/null
@@ -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 (file)
index 0000000..3128f27
--- /dev/null
@@ -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 (file)
index 0000000..55f48ea
--- /dev/null
@@ -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 (file)
index 0000000..fcaf27b
--- /dev/null
@@ -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:
+ */
index cf40fbb..535f24c 100644 (file)
@@ -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;
index 91a303d..b80c757 100644 (file)
@@ -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)))
index 9574f0a..5f12f64 100644 (file)
@@ -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__ */
 
 /*