From bb1cde678e3b7639490cbbd68fe312af171c0a41 Mon Sep 17 00:00:00 2001 From: NikitaSkrynnik Date: Thu, 14 Sep 2023 16:00:38 +0700 Subject: [PATCH] ping: Simple binary API for running ping based on events Type: improvement Change-Id: I02846a2420637470cb0f9472c86471b6a3421a75 Signed-off-by: NikitaSkrynnik --- src/plugins/ping/CMakeLists.txt | 5 ++ src/plugins/ping/ping.api | 46 ++++++++++++ src/plugins/ping/ping.c | 76 ++------------------ src/plugins/ping/ping.h | 73 +++++++++++++++++++ src/plugins/ping/ping_api.c | 155 ++++++++++++++++++++++++++++++++++++++++ test/test_ping.py | 34 +++++++++ 6 files changed, 319 insertions(+), 70 deletions(-) create mode 100644 src/plugins/ping/ping.api create mode 100644 src/plugins/ping/ping_api.c diff --git a/src/plugins/ping/CMakeLists.txt b/src/plugins/ping/CMakeLists.txt index 2828f769fcc..d0040ff373a 100644 --- a/src/plugins/ping/CMakeLists.txt +++ b/src/plugins/ping/CMakeLists.txt @@ -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 index 00000000000..4cf043f5c31 --- /dev/null +++ b/src/plugins/ping/ping.api @@ -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: + */ diff --git a/src/plugins/ping/ping.c b/src/plugins/ping/ping.c index 050048211e6..ca2fc1fa055 100644 --- a/src/plugins/ping/ping.c +++ b/src/plugins/ping/ping.c @@ -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; } diff --git a/src/plugins/ping/ping.h b/src/plugins/ping/ping.h index 7826945ea8b..e789300acd6 100644 --- a/src/plugins/ping/ping.h +++ b/src/plugins/ping/ping.h @@ -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 index 00000000000..5578fa560f2 --- /dev/null +++ b/src/plugins/ping/ping_api.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* define message IDs */ +#include +#include + +#define REPLY_MSG_ID_BASE pm->msg_id_base +#include + +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 + +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 diff --git a/test/test_ping.py b/test/test_ping.py index c2eb8b75299..2fb36e5d7b6 100644 --- a/test/test_ping.py +++ b/test/test_ping.py @@ -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") -- 2.16.6