From a77ae4708906b2a7894f7ac694bf55d5f0558d5f Mon Sep 17 00:00:00 2001 From: Steven Luong Date: Sun, 14 Feb 2021 11:37:02 -0800 Subject: arping: add arping command Add linux similar arping command to VPP. syntax: arping [gratuitous]
[repeat ] [interval ] Type: feature Signed-off-by: Steven Luong Change-Id: I9267c054235207b8fae8e3f159246777eb0340dd --- src/plugins/arping/arping.c | 797 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 797 insertions(+) create mode 100644 src/plugins/arping/arping.c (limited to 'src/plugins/arping/arping.c') diff --git a/src/plugins/arping/arping.c b/src/plugins/arping/arping.c new file mode 100644 index 00000000000..6c5836b9b9b --- /dev/null +++ b/src/plugins/arping/arping.c @@ -0,0 +1,797 @@ +/* + * Copyright (c) 2021 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 + +arping_main_t arping_main; + +#define foreach_arping_error _ (NONE, "no error") + +typedef enum +{ +#define _(f, s) ARPING_ERROR_##f, + foreach_arping_error +#undef _ + ARPING_N_ERROR, +} arping__error_t; + +static char *arping_error_strings[] = { +#define _(n, s) s, + foreach_arping_error +#undef _ +}; + +#define foreach_arping \ + _ (DROP, "error-drop") \ + _ (IO, "interface-output") + +typedef enum +{ +#define _(sym, str) ARPING_NEXT_##sym, + foreach_arping +#undef _ + ARPING_N_NEXT, +} arping_next_t; + +typedef struct arping_trace_t_ +{ + u32 sw_if_index; + u16 arp_opcode; + ethernet_arp_ip4_over_ethernet_address_t reply; +} arping_trace_t; + +typedef enum +{ +#define _(sym, str) ARPING6_NEXT_##sym, + foreach_arping +#undef _ + ARPING6_N_NEXT, +} arping6_next_t; + +typedef CLIB_PACKED (struct { + mac_address_t mac; + ip6_address_t ip6; +}) ethernet_arp_ip6_over_ethernet_address_t; + +typedef struct arping6_trace_t_ +{ + u32 sw_if_index; + u8 type; + ethernet_arp_ip6_over_ethernet_address_t reply; +} arping6_trace_t; + +/* packet trace format function */ +static u8 * +format_arping_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 *); + arping_trace_t *t = va_arg (*args, arping_trace_t *); + + s = format (s, "sw-if-index: %u, opcode: %U, from %U (%U)", t->sw_if_index, + format_ethernet_arp_opcode, t->arp_opcode, format_mac_address, + &t->reply.mac, format_ip4_address, &t->reply.ip4); + + return s; +} + +static u8 * +format_arping6_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 *); + arping6_trace_t *t = va_arg (*args, arping6_trace_t *); + + s = format (s, "sw-if-index: %u, type: %u, from %U (%U)", t->sw_if_index, + t->type, format_mac_address, &t->reply.mac, format_ip6_address, + &t->reply.ip6); + + return s; +} + +VLIB_NODE_FN (arping_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + u32 n_left_from, *from, *to_next, n_left_to_next; + arping_next_t next_index; + arping_main_t *am = &arping_main; + + next_index = node->cached_next_index; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 2 && n_left_to_next >= 2) + { + u32 next0, next1, bi0, bi1; + vlib_buffer_t *b0, *b1; + ethernet_arp_header_t *arp0, *arp1; + u32 sw_if_index0, sw_if_index1; + arping_intf_t *aif0, *aif1; + + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + next0 = next1 = ARPING_NEXT_DROP; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + arp0 = vlib_buffer_get_current (b0); + arp1 = vlib_buffer_get_current (b1); + + vnet_feature_next (&next0, b0); + vnet_feature_next (&next1, b1); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + if (PREDICT_TRUE (arp0->opcode == + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))) + { + aif0 = am->interfaces[sw_if_index0]; + if (PREDICT_TRUE (aif0->address.ip.ip4.as_u32 == + arp0->ip4_over_ethernet[0].ip4.as_u32)) + { + aif0->recv.from4.ip4.as_u32 = + arp0->ip4_over_ethernet[0].ip4.as_u32; + clib_memcpy_fast (&aif0->recv.from4.mac, + &arp0->ip4_over_ethernet[0].mac, 6); + aif0->reply_count++; + } + } + if (PREDICT_TRUE (arp1->opcode == + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))) + { + aif1 = am->interfaces[sw_if_index1]; + if (PREDICT_TRUE (aif1->address.ip.ip4.as_u32 == + arp1->ip4_over_ethernet[0].ip4.as_u32)) + { + aif1->recv.from4.ip4.as_u32 = + arp1->ip4_over_ethernet[0].ip4.as_u32; + clib_memcpy_fast (&aif1->recv.from4.mac, + &arp0->ip4_over_ethernet[0].mac, 6); + aif1->reply_count++; + } + } + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + arping_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->arp_opcode = clib_host_to_net_u16 (arp0->opcode); + t->reply.ip4.as_u32 = arp0->ip4_over_ethernet[0].ip4.as_u32; + clib_memcpy_fast (&t->reply.mac, &arp0->ip4_over_ethernet[0].mac, + 6); + } + if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED))) + { + arping_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + t->arp_opcode = clib_host_to_net_u16 (arp1->opcode); + t->reply.ip4.as_u32 = arp1->ip4_over_ethernet[0].ip4.as_u32; + clib_memcpy_fast (&t->reply.mac, &arp1->ip4_over_ethernet[0].mac, + 6); + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next, + n_left_to_next, bi0, bi1, next0, + next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 next0, bi0; + vlib_buffer_t *b0; + ethernet_arp_header_t *arp0; + arping_intf_t *aif0; + u32 sw_if_index0; + + bi0 = to_next[0] = from[0]; + + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + next0 = ARPING_NEXT_DROP; + + b0 = vlib_get_buffer (vm, bi0); + arp0 = vlib_buffer_get_current (b0); + + vnet_feature_next (&next0, b0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + if (PREDICT_TRUE (arp0->opcode == + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))) + { + aif0 = am->interfaces[sw_if_index0]; + if (PREDICT_TRUE (aif0->address.ip.ip4.as_u32 == + arp0->ip4_over_ethernet[0].ip4.as_u32)) + { + aif0->recv.from4.ip4.as_u32 = + arp0->ip4_over_ethernet[0].ip4.as_u32; + clib_memcpy_fast (&aif0->recv.from4.mac, + &arp0->ip4_over_ethernet[0].mac, 6); + aif0->reply_count++; + } + } + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + arping_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->arp_opcode = clib_host_to_net_u16 (arp0->opcode); + t->reply.ip4.as_u32 = arp0->ip4_over_ethernet[0].ip4.as_u32; + clib_memcpy_fast (&t->reply.mac, &arp0->ip4_over_ethernet[0].mac, + 6); + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (arping_input_node) = +{ + .name = "arping-input",.vector_size = sizeof (u32),.format_trace = + format_arping_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors = + ARPING_N_ERROR,.error_strings = arping_error_strings,.n_next_nodes = + ARPING_N_NEXT,.next_nodes = + { + [ARPING_NEXT_DROP] = "error-drop",[ARPING_NEXT_IO] = "interface-output",} +,}; + +VNET_FEATURE_INIT (arping_feat_node, static) = { + .arc_name = "arp", + .node_name = "arping-input", + .runs_before = VNET_FEATURES ("arp-reply"), +}; + +VLIB_NODE_FN (arping6_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + u32 n_left_from, *from, *to_next, n_left_to_next; + arping_next_t next_index; + arping_main_t *am = &arping_main; + + next_index = node->cached_next_index; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 2 && n_left_to_next >= 2) + { + u32 next0, next1, bi0, bi1; + vlib_buffer_t *b0, *b1; + ip6_header_t *ip60, *ip61; + u32 sw_if_index0, sw_if_index1; + arping_intf_t *aif0, *aif1; + icmp6_neighbor_solicitation_or_advertisement_header_t *sol_adv0, + *sol_adv1; + icmp6_neighbor_discovery_ethernet_link_layer_address_option_t + *lladdr0, + *lladdr1; + + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + next0 = next1 = ARPING6_NEXT_DROP; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + ip60 = vlib_buffer_get_current (b0); + ip61 = vlib_buffer_get_current (b1); + + vnet_feature_next (&next0, b0); + vnet_feature_next (&next1, b1); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + sol_adv0 = ip6_next_header (ip60); + lladdr0 = + (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t + *) (sol_adv0 + 1); + + if (PREDICT_TRUE (sol_adv0->icmp.type == + ICMP6_neighbor_advertisement)) + { + aif0 = am->interfaces[sw_if_index0]; + if (PREDICT_TRUE (clib_memcmp (&aif0->address.ip.ip6, + &sol_adv0->target_address, + sizeof (aif0->address.ip.ip6)) == + 0)) + { + clib_memcpy_fast (&aif0->recv.from6.ip6, + &sol_adv0->target_address, + sizeof (aif0->recv.from6.ip6)); + clib_memcpy_fast (&aif0->recv.from6.mac, + lladdr0->ethernet_address, 6); + aif0->reply_count++; + } + } + + sol_adv1 = ip6_next_header (ip61); + lladdr1 = + (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t + *) (sol_adv1 + 1); + + if (PREDICT_TRUE (sol_adv1->icmp.type == + ICMP6_neighbor_advertisement)) + { + aif1 = am->interfaces[sw_if_index1]; + if (PREDICT_TRUE (clib_memcmp (&aif1->address.ip.ip6, + &sol_adv1->target_address, + sizeof (aif1->address.ip.ip6)) == + 0)) + { + clib_memcpy_fast (&aif1->recv.from6.ip6, + &sol_adv1->target_address, + sizeof (aif1->recv.from6.ip6)); + clib_memcpy_fast (&aif1->recv.from6.mac, + lladdr1->ethernet_address, 6); + aif1->reply_count++; + } + } + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + arping6_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->type = sol_adv0->icmp.type; + clib_memcpy_fast (&t->reply.ip6, &sol_adv0->target_address, + sizeof (t->reply.ip6)); + clib_memcpy_fast (&t->reply.mac, lladdr0->ethernet_address, 6); + } + if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED))) + { + arping6_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + t->type = sol_adv1->icmp.type; + clib_memcpy_fast (&t->reply.ip6, &sol_adv1->target_address, + sizeof (t->reply.ip6)); + clib_memcpy_fast (&t->reply.mac, lladdr1->ethernet_address, 6); + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next, + n_left_to_next, bi0, bi1, next0, + next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 next0, bi0; + vlib_buffer_t *b0; + arping_intf_t *aif0; + u32 sw_if_index0; + ip6_header_t *ip60; + icmp6_neighbor_solicitation_or_advertisement_header_t *sol_adv0; + icmp6_neighbor_discovery_ethernet_link_layer_address_option_t + *lladdr0; + + bi0 = to_next[0] = from[0]; + + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + next0 = ARPING_NEXT_DROP; + + b0 = vlib_get_buffer (vm, bi0); + ip60 = vlib_buffer_get_current (b0); + + vnet_feature_next (&next0, b0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + sol_adv0 = ip6_next_header (ip60); + lladdr0 = + (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t + *) (sol_adv0 + 1); + if (PREDICT_TRUE (sol_adv0->icmp.type == + ICMP6_neighbor_advertisement)) + { + aif0 = am->interfaces[sw_if_index0]; + if (PREDICT_TRUE (clib_memcmp (&aif0->address.ip.ip6, + &sol_adv0->target_address, + sizeof (aif0->address.ip.ip6)) == + 0)) + { + clib_memcpy_fast (&aif0->recv.from6.ip6, + &sol_adv0->target_address, + sizeof (aif0->recv.from6.ip6)); + clib_memcpy_fast (&aif0->recv.from6.mac, + lladdr0->ethernet_address, 6); + aif0->reply_count++; + } + } + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + arping6_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->type = sol_adv0->icmp.type; + clib_memcpy_fast (&t->reply.ip6, &sol_adv0->target_address, + sizeof (t->reply.ip6)); + clib_memcpy_fast (&t->reply.mac, lladdr0->ethernet_address, 6); + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (arping6_input_node) = +{ + .name = "arping6-input",.vector_size = sizeof (u32),.format_trace = + format_arping6_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors = + ARPING_N_ERROR,.error_strings = arping_error_strings,.n_next_nodes = + ARPING_N_NEXT,.next_nodes = + { + [ARPING6_NEXT_DROP] = "error-drop",[ARPING6_NEXT_IO] = "interface-output",} +,}; + +VNET_FEATURE_INIT (arping6_feat_node, static) = { + .arc_name = "ip6-local", + .node_name = "arping6-input", + .runs_before = VNET_FEATURES ("ip6-local-end-of-arc"), +}; + +static clib_error_t * +arping_neighbor_advertisement (vlib_main_t *vm, arping_args_t *args) +{ + vnet_main_t *vnm = vnet_get_main (); + u32 send_count = 0; + + while (args->repeat > 0) + { + send_count++; + if (args->address.version == AF_IP4) + { + if (args->silence == 0) + vlib_cli_output (vm, "Sending %u GARP to %U", send_count, + format_ip4_address, &args->address.ip.ip4); + ip4_neighbor_advertise (vm, vnm, args->sw_if_index, + &args->address.ip.ip4); + } + else + { + if (args->silence == 0) + vlib_cli_output (vm, "Sending %u Neighbor Advertisement to %U", + send_count, format_ip6_address, + &args->address.ip.ip6); + ip6_neighbor_advertise (vm, vnm, args->sw_if_index, + &args->address.ip.ip6); + } + args->repeat--; + if ((args->interval > 0.0) && (args->repeat > 0)) + vlib_process_suspend (vm, args->interval); + } + + return 0; +} + +static void +arping_vnet_feature_enable_disable (vlib_main_t *vm, const char *arc_name, + const char *node_name, u32 sw_if_index, + int enable_disable, void *feature_config, + u32 n_feature_config_bytes) +{ + vlib_worker_thread_barrier_sync (vm); + vnet_feature_enable_disable (arc_name, node_name, sw_if_index, + enable_disable, feature_config, + n_feature_config_bytes); + vlib_worker_thread_barrier_release (vm); +} + +static void +arping_vec_validate (vlib_main_t *vm, u32 sw_if_index) +{ + arping_main_t *am = &arping_main; + + if (sw_if_index >= vec_len (am->interfaces)) + { + vlib_worker_thread_barrier_sync (vm); + vec_validate (am->interfaces, sw_if_index); + vlib_worker_thread_barrier_release (vm); + } +} + +static clib_error_t * +arping_neighbor_probe_dst (vlib_main_t *vm, arping_args_t *args) +{ + arping_main_t *am = &arping_main; + u32 send_count = 0; + clib_error_t *error; + arping_intf_t aif; + + /* Disallow multiple sends on the same interface for now. Who needs it? */ + if (am->interfaces && (am->interfaces[args->sw_if_index] != 0)) + { + error = clib_error_return ( + 0, "arping command is in progress for the same interface. " + "Please try again later."); + args->rv = VNET_API_ERROR_INVALID_VALUE; + return error; + } + + arping_vec_validate (vm, args->sw_if_index); + clib_memset (&aif, 0, sizeof (aif)); + aif.interval = args->interval; + aif.repeat = args->repeat; + aif.reply_count = 0; + am->interfaces[args->sw_if_index] = &aif; + + clib_memcpy (&aif.address, &args->address, sizeof (aif.address)); + if (args->address.version == AF_IP4) + arping_vnet_feature_enable_disable (vm, "arp", "arping-input", + args->sw_if_index, 1, 0, 0); + else + arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input", + args->sw_if_index, 1, 0, 0); + + while (args->repeat > 0) + { + send_count++; + if (args->address.version == AF_IP4) + { + if (args->silence == 0) + vlib_cli_output (vm, "Sending %u ARP Request to %U", send_count, + format_ip4_address, &args->address.ip.ip4); + ip4_neighbor_probe_dst (args->sw_if_index, &args->address.ip.ip4); + } + else + { + if (args->silence == 0) + vlib_cli_output (vm, "Sending %u Neighbor Solicitation to %U", + send_count, format_ip6_address, + &args->address.ip.ip6); + ip6_neighbor_probe_dst (args->sw_if_index, &args->address.ip.ip6); + } + args->repeat--; + if ((args->interval > 0.0) && (args->repeat > 0)) + vlib_process_suspend (vm, args->interval); + } + + /* wait for a second on the reply */ + u32 wait_count = 0; + while ((aif.reply_count < send_count) && (wait_count < 10)) + { + vlib_process_suspend (vm, 0.1); + wait_count++; + } + + if (args->address.version == AF_IP4) + { + clib_memcpy (&args->recv.from4, &aif.recv.from4, + sizeof (args->recv.from4)); + arping_vnet_feature_enable_disable (vm, "arp", "arping-input", + args->sw_if_index, 0, 0, 0); + } + else + { + clib_memcpy (&args->recv.from6, &aif.recv.from6, + sizeof (args->recv.from6)); + arping_vnet_feature_enable_disable (vm, "ip6-local", "arping6-input", + args->sw_if_index, 0, 0, 0); + } + args->reply_count = aif.reply_count; + + am->interfaces[args->sw_if_index] = 0; + + return 0; +} + +void +arping_run_command (vlib_main_t *vm, arping_args_t *args) +{ + if (args->is_garp) + args->error = arping_neighbor_advertisement (vm, args); + else + args->error = arping_neighbor_probe_dst (vm, args); +} + +static clib_error_t * +arping_ip_address (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + clib_error_t *error = 0; + vnet_main_t *vnm = vnet_get_main (); + arping_args_t args = { 0 }; + f64 interval = ARPING_DEFAULT_INTERVAL; + + args.repeat = ARPING_DEFAULT_REPEAT; + args.interval = ARPING_DEFAULT_INTERVAL; + args.sw_if_index = ~0; + args.silence = 0; + + if (unformat (input, "gratuitous")) + args.is_garp = 1; + + if (unformat (input, "%U", unformat_ip4_address, &args.address.ip.ip4)) + args.address.version = AF_IP4; + else if (unformat (input, "%U", unformat_ip6_address, &args.address.ip.ip6)) + args.address.version = AF_IP6; + else + { + error = clib_error_return ( + 0, + "expecting IP4/IP6 address `%U'. Usage: arping [gratuitous] " + " [repeat ] [interval ]", + format_unformat_error, input); + goto done; + } + + if (!unformat_user (input, unformat_vnet_sw_interface, vnm, + &args.sw_if_index)) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, input); + goto done; + } + + /* parse the rest of the parameters in a cycle */ + while (!unformat_eof (input, NULL)) + { + if (unformat (input, "interval")) + { + if (!unformat (input, "%f", &interval)) + { + error = clib_error_return ( + 0, "expecting interval (floating point number) got `%U'", + format_unformat_error, input); + goto done; + } + args.interval = interval; + } + else if (unformat (input, "repeat")) + { + if (!unformat (input, "%u", &args.repeat)) + { + error = + clib_error_return (0, "expecting repeat count but got `%U'", + format_unformat_error, input); + goto done; + } + } + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + goto done; + } + } + + arping_run_command (vm, &args); + + if (args.reply_count) + { + if (args.address.version == AF_IP4) + vlib_cli_output (vm, "Received %u ARP Replies from %U (%U)", + args.reply_count, format_mac_address, + &args.recv.from4.mac, format_ip4_address, + &args.recv.from4.ip4); + else + vlib_cli_output ( + vm, "Received %u ICMP6 neighbor advertisements from %U (%U)", + args.reply_count, format_mac_address, &args.recv.from6.mac, + format_ip6_address, &args.recv.from6.ip6); + } + else if (args.is_garp == 0) + vlib_cli_output (vm, "Received 0 Reply"); + + error = args.error; +done: + return error; +} +// clang-format off +/*? + * This command sends an ARP REQUEST or gratuitous ARP to network hosts. The + * address can be an IPv4 or IPv6 address. + * + * @cliexpar + * @parblock + * Example of how to send an IPv4 ARP REQUEST + * @cliexstart{arping 100.1.1.10 VirtualEthernet0/0/0 repeat 3 interval 1} + * Sending 1 ARP Request to 100.1.1.10 + * Sending 2 ARP Request to 100.1.1.10 + * Sending 3 ARP Request to 100.1.1.10 + * Received 3 ARP Replies from 52:53:00:00:04:01 (100.1.1.10) + * @cliexend + * + * Example of how to send an IPv6 Neighbor Solicitation + * @cliexstart{arping 2001:192::2 VirtualEthernet0/0/0 repeat 3 interval 1} + * Sending 1 Neighbor Solicitation to 2001:192::2 + * Sending 2 Neighbor Solicitation to 2001:192::2 + * Sending 3 Neighbor Solicitation to 2001:192::2 + * Received 3 ICMP6 neighbor advertisements from 52:53:00:00:04:01 (2001:192::2) + * @cliexend + * + * Example of how to send an IPv4 gratuitous ARP + * @cliexstart{arping gratuitous 100.1.1.100 VirtualEthernet0/0/0 repeat 2} + * Sending 1 GARP to 100.1.1.100 + * Sending 2 GARP to 100.1.1.100 + * @cliexend + * @endparblock + * +?*/ +// clang-format on +VLIB_CLI_COMMAND (arping_command, static) = { + .path = "arping", + .function = arping_ip_address, + .short_help = "arping [gratuitous] {addr} {interface}" + " [interval {sec}] [repeat {cnt}]", + .is_mp_safe = 1, +}; + +static clib_error_t * +arping_cli_init (vlib_main_t *vm) +{ + /* initialize binary API */ + arping_plugin_api_hookup (vm); + + return 0; +} + +VLIB_INIT_FUNCTION (arping_cli_init); + +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Arping (arping)", +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg