ping: Simple binary API for running ping based on events 28/39528/10
authorNikitaSkrynnik <nikita.skrynnik@xored.com>
Thu, 14 Sep 2023 09:00:38 +0000 (16:00 +0700)
committerDave Wallace <dwallacelf@gmail.com>
Tue, 26 Sep 2023 20:45:28 +0000 (20:45 +0000)
Type: improvement
Change-Id: I02846a2420637470cb0f9472c86471b6a3421a75
Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>
src/plugins/ping/CMakeLists.txt
src/plugins/ping/ping.api [new file with mode: 0644]
src/plugins/ping/ping.c
src/plugins/ping/ping.h
src/plugins/ping/ping_api.c [new file with mode: 0644]
test/test_ping.py

index 2828f76..d0040ff 100644 (file)
@@ -14,4 +14,9 @@
 add_vpp_plugin(ping
   SOURCES
   ping.c
+  ping.h
+  ping_api.c
+
+  API_FILES
+  ping.api
 )
diff --git a/src/plugins/ping/ping.api b/src/plugins/ping/ping.api
new file mode 100644 (file)
index 0000000..4cf043f
--- /dev/null
@@ -0,0 +1,46 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * Copyright (c) 2023 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.
+ */
+
+option version = "0.1.0";
+import "vnet/interface_types.api";
+import "vnet/ip/ip_types.api";
+
+autoreply define want_ping_finished_events
+{
+  u32 client_index;
+  u32 context;
+  vl_api_address_t address;
+  u32 repeat [default=1];
+  f64 interval [default=1.0];
+};
+
+define ping_finished_event
+{
+  u32 client_index;
+  u32 request_count;
+  u32 reply_count;
+};
+
+service {
+  rpc want_ping_finished_events returns want_ping_finished_events_reply
+    events ping_finished_event;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 0500482..ca2fc1f 100644 (file)
@@ -99,70 +99,6 @@ format_ip46_ping_result (u8 * s, va_list * args)
  *
  */
 
-
-static_always_inline uword
-get_cli_process_id_by_icmp_id_mt (vlib_main_t * vm, u16 icmp_id)
-{
-  ping_main_t *pm = &ping_main;
-  uword cli_process_id = PING_CLI_UNKNOWN_NODE;
-  ping_run_t *pr;
-
-  clib_spinlock_lock_if_init (&pm->ping_run_check_lock);
-  vec_foreach (pr, pm->active_ping_runs)
-  {
-    if (pr->icmp_id == icmp_id)
-      {
-       cli_process_id = pr->cli_process_id;
-       break;
-      }
-  }
-  clib_spinlock_unlock_if_init (&pm->ping_run_check_lock);
-  return cli_process_id;
-}
-
-
-static_always_inline void
-set_cli_process_id_by_icmp_id_mt (vlib_main_t * vm, u16 icmp_id,
-                                 uword cli_process_id)
-{
-  ping_main_t *pm = &ping_main;
-  ping_run_t *pr;
-
-  clib_spinlock_lock_if_init (&pm->ping_run_check_lock);
-  vec_foreach (pr, pm->active_ping_runs)
-  {
-    if (pr->icmp_id == icmp_id)
-      {
-       pr->cli_process_id = cli_process_id;
-       goto have_found_and_set;
-      }
-  }
-  /* no such key yet - add a new one */
-  ping_run_t new_pr = {.icmp_id = icmp_id,.cli_process_id = cli_process_id };
-  vec_add1 (pm->active_ping_runs, new_pr);
-have_found_and_set:
-  clib_spinlock_unlock_if_init (&pm->ping_run_check_lock);
-}
-
-
-static_always_inline void
-clear_cli_process_id_by_icmp_id_mt (vlib_main_t * vm, u16 icmp_id)
-{
-  ping_main_t *pm = &ping_main;
-  ping_run_t *pr;
-
-  clib_spinlock_lock_if_init (&pm->ping_run_check_lock);
-  vec_foreach (pr, pm->active_ping_runs)
-  {
-    if (pr->icmp_id == icmp_id)
-      {
-       vec_del1 (pm->active_ping_runs, pr - pm->active_ping_runs);
-       break;
-      }
-  }
-  clib_spinlock_unlock_if_init (&pm->ping_run_check_lock);
-}
-
 static_always_inline int
 ip46_get_icmp_id_and_seq (vlib_main_t * vm, vlib_buffer_t * b0,
                          u16 * out_icmp_id, u16 * out_icmp_seq, int is_ip6)
@@ -1229,9 +1165,8 @@ done:
   return err;
 }
 
-static send_ip46_ping_result_t
-send_ip6_ping (vlib_main_t * vm,
-              u32 table_id, ip6_address_t * pa6,
+send_ip46_ping_result_t
+send_ip6_ping (vlib_main_t *vm, u32 table_id, ip6_address_t *pa6,
               u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len,
               u32 burst, u8 verbose)
 {
@@ -1241,9 +1176,8 @@ send_ip6_ping (vlib_main_t * vm,
                         id_host, data_len, burst, verbose, 1 /* is_ip6 */ );
 }
 
-static send_ip46_ping_result_t
-send_ip4_ping (vlib_main_t * vm,
-              u32 table_id, ip4_address_t * pa4,
+send_ip46_ping_result_t
+send_ip4_ping (vlib_main_t *vm, u32 table_id, ip4_address_t *pa4,
               u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len,
               u32 burst, u8 verbose)
 {
@@ -1678,6 +1612,8 @@ ping_cli_init (vlib_main_t * vm)
   icmp6_register_type (vm, ICMP6_echo_request,
                       ip6_icmp_echo_request_node.index);
 
+  ping_plugin_api_hookup (vm);
+
   return 0;
 }
 
index 7826945..e789300 100644 (file)
@@ -52,6 +52,9 @@ typedef struct ping_run_t
 
 typedef struct ping_main_t
 {
+  /* API message ID base */
+  u16 msg_id_base;
+
   ip6_main_t *ip6_main;
   ip4_main_t *ip4_main;
   /* a vector of current ping runs. */
@@ -88,4 +91,74 @@ typedef enum
   ICMP46_ECHO_REPLY_N_NEXT,
 } icmp46_echo_reply_next_t;
 
+static_always_inline uword
+get_cli_process_id_by_icmp_id_mt (vlib_main_t *vm, u16 icmp_id)
+{
+  ping_main_t *pm = &ping_main;
+  uword cli_process_id = PING_CLI_UNKNOWN_NODE;
+  ping_run_t *pr;
+
+  clib_spinlock_lock_if_init (&pm->ping_run_check_lock);
+  vec_foreach (pr, pm->active_ping_runs)
+    {
+      if (pr->icmp_id == icmp_id)
+       {
+         cli_process_id = pr->cli_process_id;
+         break;
+       }
+    }
+  clib_spinlock_unlock_if_init (&pm->ping_run_check_lock);
+  return cli_process_id;
+}
+
+static_always_inline void
+set_cli_process_id_by_icmp_id_mt (vlib_main_t *vm, u16 icmp_id,
+                                 uword cli_process_id)
+{
+  ping_main_t *pm = &ping_main;
+  ping_run_t *pr;
+
+  clib_spinlock_lock_if_init (&pm->ping_run_check_lock);
+  vec_foreach (pr, pm->active_ping_runs)
+    {
+      if (pr->icmp_id == icmp_id)
+       {
+         pr->cli_process_id = cli_process_id;
+         goto have_found_and_set;
+       }
+    }
+  /* no such key yet - add a new one */
+  ping_run_t new_pr = { .icmp_id = icmp_id, .cli_process_id = cli_process_id };
+  vec_add1 (pm->active_ping_runs, new_pr);
+have_found_and_set:
+  clib_spinlock_unlock_if_init (&pm->ping_run_check_lock);
+}
+
+static_always_inline void
+clear_cli_process_id_by_icmp_id_mt (vlib_main_t *vm, u16 icmp_id)
+{
+  ping_main_t *pm = &ping_main;
+  ping_run_t *pr;
+
+  clib_spinlock_lock_if_init (&pm->ping_run_check_lock);
+  vec_foreach (pr, pm->active_ping_runs)
+    {
+      if (pr->icmp_id == icmp_id)
+       {
+         vec_del1 (pm->active_ping_runs, pr - pm->active_ping_runs);
+         break;
+       }
+    }
+  clib_spinlock_unlock_if_init (&pm->ping_run_check_lock);
+}
+clib_error_t *ping_plugin_api_hookup (vlib_main_t *vm);
+send_ip46_ping_result_t send_ip4_ping (vlib_main_t *vm, u32 table_id,
+                                      ip4_address_t *pa4, u32 sw_if_index,
+                                      u16 seq_host, u16 id_host, u16 data_len,
+                                      u32 burst, u8 verbose);
+send_ip46_ping_result_t send_ip6_ping (vlib_main_t *vm, u32 table_id,
+                                      ip6_address_t *pa6, u32 sw_if_index,
+                                      u16 seq_host, u16 id_host, u16 data_len,
+                                      u32 burst, u8 verbose);
+
 #endif /* included_ping_ping_h */
diff --git a/src/plugins/ping/ping_api.c b/src/plugins/ping/ping_api.c
new file mode 100644 (file)
index 0000000..5578fa5
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vlib/pci/pci.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/format_fns.h>
+#include <vnet/ip/ip_types_api.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <ping/ping.h>
+
+/* define message IDs */
+#include <ping/ping.api_enum.h>
+#include <ping/ping.api_types.h>
+
+#define REPLY_MSG_ID_BASE pm->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+static void
+ping_api_send_ping_event (vl_api_want_ping_finished_events_t *mp,
+                         u32 request_count, u32 reply_count)
+{
+  ping_main_t *pm = &ping_main;
+
+  vl_api_registration_t *rp;
+  rp = vl_api_client_index_to_registration (mp->client_index);
+
+  vl_api_ping_finished_event_t *e = vl_msg_api_alloc (sizeof (*e));
+  clib_memset (e, 0, sizeof (*e));
+
+  e->_vl_msg_id = htons (VL_API_PING_FINISHED_EVENT + pm->msg_id_base);
+  e->request_count = htonl (request_count);
+  e->reply_count = htonl (reply_count);
+
+  vl_api_send_msg (rp, (u8 *) e);
+}
+
+void
+vl_api_want_ping_finished_events_t_handler (
+  vl_api_want_ping_finished_events_t *mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  ping_main_t *pm = &ping_main;
+  vl_api_want_ping_finished_events_reply_t *rmp;
+
+  uword curr_proc = vlib_current_process (vm);
+
+  u16 icmp_id;
+  static u32 rand_seed = 0;
+
+  if (PREDICT_FALSE (!rand_seed))
+    rand_seed = random_default_seed ();
+
+  icmp_id = random_u32 (&rand_seed) & 0xffff;
+
+  while (~0 != get_cli_process_id_by_icmp_id_mt (vm, icmp_id))
+    icmp_id++;
+
+  set_cli_process_id_by_icmp_id_mt (vm, icmp_id, curr_proc);
+
+  int rv = 0;
+  u32 request_count = 0;
+  u32 reply_count = 0;
+
+  u32 table_id = 0;
+  ip_address_t dst_addr = { 0 };
+  u32 sw_if_index = ~0;
+  f64 ping_interval = clib_net_to_host_f64 (mp->interval);
+  u32 ping_repeat = ntohl (mp->repeat);
+  u32 data_len = PING_DEFAULT_DATA_LEN;
+  u32 ping_burst = 1;
+  u32 verbose = 0;
+  ip_address_decode2 (&mp->address, &dst_addr);
+
+  vl_api_registration_t *rp;
+  rp = vl_api_client_index_to_registration (mp->client_index);
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  rmp->_vl_msg_id =
+    htons ((VL_API_WANT_PING_FINISHED_EVENTS_REPLY) + (REPLY_MSG_ID_BASE));
+  rmp->context = mp->context;
+  rmp->retval = ntohl (rv);
+  vl_api_send_msg (rp, (u8 *) rmp);
+
+  int i;
+  send_ip46_ping_result_t res = SEND_PING_OK;
+  for (i = 1; i <= ping_repeat; i++)
+    {
+      f64 sleep_interval;
+      f64 time_ping_sent = vlib_time_now (vm);
+
+      if (dst_addr.version == AF_IP4)
+       res = send_ip4_ping (vm, table_id, &dst_addr.ip.ip4, sw_if_index, i,
+                            icmp_id, data_len, ping_burst, verbose);
+      else
+       res = send_ip6_ping (vm, table_id, &dst_addr.ip.ip6, sw_if_index, i,
+                            icmp_id, data_len, ping_burst, verbose);
+
+      if (SEND_PING_OK == res)
+       request_count += 1;
+      else
+       continue;
+
+      while ((sleep_interval =
+               time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0)
+       {
+         uword event_type;
+         vlib_process_wait_for_event_or_clock (vm, sleep_interval);
+         event_type = vlib_process_get_events (vm, 0);
+
+         if (event_type == ~0)
+           break;
+
+         if (event_type == PING_RESPONSE_IP4 ||
+             event_type == PING_RESPONSE_IP6)
+           reply_count += 1;
+       }
+    }
+
+  ping_api_send_ping_event (mp, request_count, reply_count);
+
+  clear_cli_process_id_by_icmp_id_mt (vm, icmp_id);
+}
+
+/* set tup the API message handling tables */
+#include <ping/ping.api.c>
+
+clib_error_t *
+ping_plugin_api_hookup (vlib_main_t *vm)
+{
+  ping_main_t *pm = &ping_main;
+
+  /* ask for a correctly-sized block of API message decode slots */
+  pm->msg_id_base = setup_message_id_table ();
+
+  return 0;
+}
\ No newline at end of file
index c2eb8b7..2fb36e5 100644 (file)
@@ -179,3 +179,37 @@ class TestPing(VppTestCase):
                 icmp_seq = icmp_seq + 1
         finally:
             self.vapi.cli("show error")
+
+    def test_ping_api(self):
+        """ping api"""
+
+        try:
+            self.pg_enable_capture(self.pg_interfaces)
+            self.pg_start()
+
+            ret = self.vapi.want_ping_finished_events(
+                address=self.pg1.remote_ip4,
+                repeat=4,
+                interval=0.2,
+            )
+            self.logger.info(ret)
+            timeout = 1
+
+            ev = self.vapi.wait_for_event(timeout, "ping_finished_event")
+            self.logger.info(ev)
+            self.assertEqual(ev.request_count, 4)
+
+            out = self.pg1.get_capture(4)
+            icmp_id = None
+            icmp_seq = 1
+            for p in out:
+                icmp = self.verify_ping_request(
+                    p, self.pg1.local_ip4, self.pg1.remote_ip4, icmp_seq
+                )
+                icmp_seq = icmp_seq + 1
+                if icmp_id is None:
+                    icmp_id = icmp.id
+                else:
+                    self.assertEqual(icmp.id, icmp_id)
+        finally:
+            self.vapi.cli("show error")