summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS5
-rw-r--r--src/plugins/arping/CMakeLists.txt24
-rw-r--r--src/plugins/arping/FEATURE.yaml9
-rw-r--r--src/plugins/arping/arping.api61
-rw-r--r--src/plugins/arping/arping.c797
-rw-r--r--src/plugins/arping/arping.h74
-rw-r--r--src/plugins/arping/arping_api.c87
-rw-r--r--src/plugins/arping/arping_test.c167
-rw-r--r--src/plugins/arping/test/test_arping.py251
9 files changed, 1475 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 4f0a36891c1..0ee7796b291 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -340,6 +340,11 @@ M: Dave Barach <vpp@barachs.net>
M: Neale Ranns <neale@graphiant.com>
F: src/plugins/arp/
+Plugin - ARPing CLI
+I: arping
+M: Steven Luong <sluong@cisco.com>
+F: src/plugins/arping/
+
Plugin - IP6 Neighbor Discovery
I: ip6-nd
M: Dave Barach <vpp@barachs.net>
diff --git a/src/plugins/arping/CMakeLists.txt b/src/plugins/arping/CMakeLists.txt
new file mode 100644
index 00000000000..afec21e7d8e
--- /dev/null
+++ b/src/plugins/arping/CMakeLists.txt
@@ -0,0 +1,24 @@
+# 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.
+
+add_vpp_plugin(arping
+ SOURCES
+ arping.c
+ arping_api.c
+
+ API_FILES
+ arping.api
+
+ API_TEST_SOURCES
+ arping_test.c
+)
diff --git a/src/plugins/arping/FEATURE.yaml b/src/plugins/arping/FEATURE.yaml
new file mode 100644
index 00000000000..c947b17c6a4
--- /dev/null
+++ b/src/plugins/arping/FEATURE.yaml
@@ -0,0 +1,9 @@
+---
+name: arping command
+maintainer: Steven Luong <sluong@cisco.com>
+features:
+ - arping command to send either gratuitous or ARP request to the remote
+ - support both IPv4 and IPv6
+description: "arping command"
+state: production
+properties: [API, CLI, STATS, MULTITHREAD]
diff --git a/src/plugins/arping/arping.api b/src/plugins/arping/arping.api
new file mode 100644
index 00000000000..f797b8cf3aa
--- /dev/null
+++ b/src/plugins/arping/arping.api
@@ -0,0 +1,61 @@
+/*
+ *------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+ */
+
+option version = "1.0.0";
+import "vnet/interface_types.api";
+import "vnet/ip/ip_types.api";
+
+/** \brief
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param address - address to send arp request or gratuitous arp.
+ @param sw_if_index - interface to send
+ @param repeat - number of packets to send
+ @param interval - if more than 1 packet is sent, the delay between send
+ @param is_garp - is garp or arp request
+*/
+
+define arping
+{
+ u32 client_index;
+ u32 context;
+ vl_api_address_t address;
+ vl_api_interface_index_t sw_if_index;
+ bool is_garp;
+ u32 repeat [default=1];
+ f64 interval [default=1.0];
+ option vat_help = "<address> <interface> [gratuitouss] [repeat <count>] [interval <sec>]";
+};
+
+/** \brief
+ @param context - sender context, to match reply w/ request
+ @param retval - return value for request
+ @reply_count - return value for reply count
+*/
+
+define arping_reply
+{
+ u32 context;
+ i32 retval;
+ u32 reply_count;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
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 <stddef.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <vnet/ip-neighbor/ip4_neighbor.h>
+#include <vnet/ip-neighbor/ip6_neighbor.h>
+#include <arping/arping.h>
+
+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] <addr> "
+ "<intf> [repeat <count>] [interval <secs>]",
+ 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:
+ */
diff --git a/src/plugins/arping/arping.h b/src/plugins/arping/arping.h
new file mode 100644
index 00000000000..d07e7cfb683
--- /dev/null
+++ b/src/plugins/arping/arping.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+#ifndef included_arping_arping_h
+#define included_arping_arping_h
+
+#include <vnet/ip/ip_types.h>
+#include <vnet/ethernet/arp_packet.h>
+
+#define ARPING_DEFAULT_INTERVAL 1.0
+#define ARPING_DEFAULT_REPEAT 1
+
+typedef struct arping6_ip6_reply_t
+{
+ mac_address_t mac;
+ ip6_address_t ip6;
+} arping6_ip6_reply_t;
+
+typedef CLIB_PACKED (union arping46_reply_ {
+ ethernet_arp_ip4_over_ethernet_address_t from4;
+ arping6_ip6_reply_t from6;
+}) arping46_reply_t;
+
+typedef struct arping_intf_t
+{
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+ f64 interval;
+ u32 repeat;
+ ip_address_t address;
+
+ arping46_reply_t recv;
+ u32 reply_count;
+} arping_intf_t;
+
+typedef struct arping_main_t
+{
+ arping_intf_t *arping_interfaces;
+ arping_intf_t **interfaces;
+ u16 msg_id_base;
+} arping_main_t;
+
+typedef struct arping_args_t
+{
+ ip_address_t address;
+ u32 sw_if_index;
+ u32 repeat;
+ f64 interval;
+ u8 is_garp;
+ u8 silence;
+
+ /* reply */
+ i32 rv;
+ u32 reply_count;
+ arping46_reply_t recv;
+ clib_error_t *error;
+} arping_args_t;
+
+extern arping_main_t arping_main;
+
+extern clib_error_t *arping_plugin_api_hookup (vlib_main_t *vm);
+extern void arping_run_command (vlib_main_t *vm, arping_args_t *args);
+
+#endif /* included_arping_arping_h */
diff --git a/src/plugins/arping/arping_api.c b/src/plugins/arping/arping_api.c
new file mode 100644
index 00000000000..015c6148f5e
--- /dev/null
+++ b/src/plugins/arping/arping_api.c
@@ -0,0 +1,87 @@
+/*
+ *------------------------------------------------------------------
+ * 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 <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 <arping/arping.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+/* define message IDs */
+#include <arping/arping.api_enum.h>
+#include <arping/arping.api_types.h>
+
+#include <vlibapi/api_helper_macros.h>
+
+static void
+vl_api_arping_t_handler (vl_api_arping_t *mp)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ arping_main_t *am = &arping_main;
+ vl_api_arping_reply_t *rmp;
+ arping_args_t args = { 0 };
+ int rv;
+
+ if (mp->sw_if_index != ~0)
+ VALIDATE_SW_IF_INDEX (mp);
+
+ ip_address_decode2 (&mp->address, &args.address);
+ args.interval = clib_net_to_host_f64 (mp->interval);
+ args.repeat = ntohl (mp->repeat);
+ args.is_garp = mp->is_garp;
+ args.sw_if_index = ntohl (mp->sw_if_index);
+ args.silence = 1;
+
+ arping_run_command (vm, &args);
+ rv = args.rv;
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO2 (VL_API_ARPING_REPLY + am->msg_id_base,
+ ({ rmp->reply_count = ntohl (args.reply_count); }));
+}
+
+/* set tup the API message handling tables */
+#include <arping/arping.api.c>
+clib_error_t *
+arping_plugin_api_hookup (vlib_main_t *vm)
+{
+ arping_main_t *am = &arping_main;
+ api_main_t *vam = vlibapi_get_main ();
+
+ /* ask for a correctly-sized block of API message decode slots */
+ am->msg_id_base = setup_message_id_table ();
+
+ /* Mark API as mp safe */
+ vam->is_mp_safe[am->msg_id_base + VL_API_ARPING] = 1;
+
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/arping/arping_test.c b/src/plugins/arping/arping_test.c
new file mode 100644
index 00000000000..9001b7098a7
--- /dev/null
+++ b/src/plugins/arping/arping_test.c
@@ -0,0 +1,167 @@
+/*
+ * arping VAT support
+ *
+ * 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 <inttypes.h>
+
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <vppinfra/error.h>
+#include <arping/arping.h>
+
+#define __plugin_msg_base arping_test_main.msg_id_base
+#include <vlibapi/vat_helper_macros.h>
+
+/* declare message IDs */
+#include <vnet/format_fns.h>
+#include <arping/arping.api_enum.h>
+#include <arping/arping.api_types.h>
+#include <vpp/api/vpe.api_types.h>
+#include <vnet/ip/ip_types_api.h>
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+ u32 ping_id;
+ vat_main_t *vat_main;
+} arping_test_main_t;
+
+arping_test_main_t arping_test_main;
+
+/* arping request API */
+static int
+api_arping (vat_main_t *vam)
+{
+ vl_api_arping_t *mp;
+ arping_args_t args = { 0 };
+ int ret;
+ unformat_input_t *input = vam->input;
+ vnet_main_t *vnm = vnet_get_main ();
+ f64 interval = ARPING_DEFAULT_INTERVAL;
+ vl_api_control_ping_t *mp_ping;
+ arping_test_main_t *atm = &arping_test_main;
+
+ args.repeat = ARPING_DEFAULT_REPEAT;
+ args.interval = ARPING_DEFAULT_INTERVAL;
+ args.sw_if_index = ~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
+ {
+ errmsg ("expecting IP4/IP6 address `%U'. Usage: arping [gratuitous] "
+ "<addr> <intf> [repeat <count>] [interval <secs>]",
+ format_unformat_error, input);
+ return -99;
+ }
+
+ if (!unformat_user (input, unformat_vnet_sw_interface, vnm,
+ &args.sw_if_index))
+ {
+ errmsg ("unknown interface `%U'", format_unformat_error, input);
+ return -99;
+ }
+
+ /* parse the rest of the parameters in a cycle */
+ while (!unformat_eof (input, NULL))
+ {
+ if (unformat (input, "interval"))
+ {
+ if (!unformat (input, "%f", &interval))
+ {
+ errmsg ("expecting interval (floating point number) got `%U'",
+ format_unformat_error, input);
+ return -99;
+ }
+ args.interval = interval;
+ }
+ else if (unformat (input, "repeat"))
+ {
+ if (!unformat (input, "%u", &args.repeat))
+ {
+ errmsg ("expecting repeat count but got `%U'",
+ format_unformat_error, input);
+ return -99;
+ }
+ }
+ else
+ {
+ errmsg ("unknown input `%U'", format_unformat_error, input);
+ return -99;
+ }
+ }
+
+ M (ARPING, mp);
+
+ mp->interval = clib_host_to_net_f64 (args.interval);
+ mp->repeat = clib_host_to_net_u32 (args.repeat);
+ mp->is_garp = args.is_garp;
+ mp->sw_if_index = clib_host_to_net_u32 (args.sw_if_index);
+ ip_address_encode2 (&args.address, &mp->address);
+
+ S (mp);
+
+ /* Use a control ping for synchronization */
+ if (!atm->ping_id)
+ atm->ping_id = vl_msg_api_get_msg_index ((u8 *) (VL_API_CONTROL_PING_CRC));
+ mp_ping = vl_msg_api_alloc_as_if_client (sizeof (*mp_ping));
+ mp_ping->_vl_msg_id = htons (atm->ping_id);
+ mp_ping->client_index = vam->my_client_index;
+
+ fformat (vam->ofp, "Sending ping id=%d\n", atm->ping_id);
+
+ vam->result_ready = 0;
+ S (mp_ping);
+
+ W (ret);
+
+ return ret;
+}
+
+/* arping-create reply handler */
+static void
+vl_api_arping_reply_t_handler (vl_api_arping_reply_t *mp)
+{
+ vat_main_t *vam = arping_test_main.vat_main;
+ i32 retval = ntohl (mp->retval);
+
+ if (retval == 0)
+ {
+ fformat (vam->ofp, "arping request reply count = %d\n",
+ ntohl (mp->reply_count));
+ }
+
+ vam->retval = retval;
+ vam->result_ready = 1;
+}
+
+#include <arping/arping.api_test.c>
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/arping/test/test_arping.py b/src/plugins/arping/test/test_arping.py
new file mode 100644
index 00000000000..bd8b6250a54
--- /dev/null
+++ b/src/plugins/arping/test/test_arping.py
@@ -0,0 +1,251 @@
+from scapy.layers.l2 import ARP
+from scapy.layers.inet6 import ICMPv6ND_NS, ICMPv6ND_NA, IPv6
+
+from framework import VppTestCase
+
+""" TestArping is a subclass of VPPTestCase classes.
+
+Basic test for sanity check of arping.
+
+"""
+
+
+class TestArping(VppTestCase):
+ """ Arping Test Case """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestArping, cls).setUpClass()
+ try:
+ cls.create_pg_interfaces(range(2))
+ cls.interfaces = list(cls.pg_interfaces)
+
+ for i in cls.interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.config_ip6()
+ i.disable_ipv6_ra()
+ i.resolve_arp()
+ i.resolve_ndp()
+ except Exception:
+ super(TestArping, cls).tearDownClass()
+ raise
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestArping, cls).tearDownClass()
+
+ def tearDown(self):
+ super(TestArping, self).tearDown()
+
+ def show_commands_at_teardown(self):
+ self.logger.info(self.vapi.cli("show hardware"))
+
+ def verify_arping_request(self, p, src, dst):
+ arp = p[ARP]
+ self.assertEqual(arp.hwtype, 0x0001)
+ self.assertEqual(arp.ptype, 0x0800)
+ self.assertEqual(arp.hwlen, 6)
+ self.assertEqual(arp.op, 1)
+ self.assertEqual(arp.psrc, src)
+ self.assertEqual(arp.pdst, dst)
+
+ def verify_arping_ip6_ns(self, p, src, dst):
+ icmpv6 = p[ICMPv6ND_NS]
+ self.assertEqual(icmpv6.type, 135)
+ self.assertEqual(icmpv6.tgt, dst)
+ ipv6 = p[IPv6]
+ self.assertEqual(src, ipv6.src)
+
+ def verify_arping_ip6_na(self, p, src, dst):
+ icmpv6 = p[ICMPv6ND_NA]
+ self.assertEqual(icmpv6.type, 136)
+ self.assertEqual(icmpv6.tgt, dst)
+ ipv6 = p[IPv6]
+ self.assertEqual(src, ipv6.src)
+
+ def test_arping_ip4_arp_request_cli(self):
+ """ arping IP4 arp request CLI test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ remote_ip4 = self.pg1.remote_ip4
+
+ ping_cmd = "arping " + remote_ip4 + "pg1 repeat 5 interval 0.1"
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ ping_cmd = "arping " + remote_ip4 + "pg1"
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_request(p, self.pg1.local_ip4,
+ self.pg1.remote_ip4)
+ finally:
+ self.vapi.cli("show error")
+
+ def test_arping_ip4_garp_cli(self):
+ """ arping ip4 gratuitous arp CLI test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ ping_cmd = ("arping gratuitous" + self.pg1.local_ip4 +
+ "pg1 repeat 5 interval 0.1")
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ ping_cmd = "arping gratuitous" + self.pg1.local_ip4 + "pg1"
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_request(p, self.pg1.local_ip4,
+ self.pg1.local_ip4)
+ finally:
+ self.vapi.cli("show error")
+
+ def test_arping_ip4_arp_request_api(self):
+ """ arping ip4 arp request API test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ remote_ip4 = self.pg1.remote_ip4
+
+ ret = self.vapi.arping(address=remote_ip4,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=0, repeat=5, interval=0.1)
+ self.logger.info(ret)
+
+ ret = self.vapi.arping(address=remote_ip4,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=0)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_request(p, self.pg1.local_ip4,
+ self.pg1.remote_ip4)
+ finally:
+ self.vapi.cli("show error")
+
+ def test_arping_ip4_garp_api(self):
+ """ arping ip4 gratuitous arp API test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ ret = self.vapi.arping(address=self.pg1.local_ip4,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=1, repeat=5, interval=0.1)
+ self.logger.info(ret)
+
+ ret = self.vapi.arping(address=self.pg1.local_ip4,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=1)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_request(p, self.pg1.local_ip4,
+ self.pg1.local_ip4)
+ finally:
+ self.vapi.cli("show error")
+
+ def test_arping_ip6_ns_cli(self):
+ """ arping IP6 neighbor solicitation CLI test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ remote_ip6 = self.pg1.remote_ip6
+
+ ping_cmd = "arping " + remote_ip6 + "pg1 repeat 5 interval 0.1"
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ ping_cmd = "arping " + remote_ip6 + "pg1"
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_ip6_ns(p, self.pg1.local_ip6,
+ self.pg1.remote_ip6)
+ finally:
+ self.vapi.cli("show error")
+
+ def test_arping_ip6_ns_api(self):
+ """ arping ip6 neighbor solicitation API test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ remote_ip6 = self.pg1.remote_ip6
+
+ ret = self.vapi.arping(address=remote_ip6,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=0, repeat=5, interval=0.1)
+ self.logger.info(ret)
+
+ ret = self.vapi.arping(address=remote_ip6,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=0)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_ip6_ns(p, self.pg1.local_ip6,
+ self.pg1.remote_ip6)
+ finally:
+ self.vapi.cli("show error")
+
+ def test_arping_ip6_na_cli(self):
+ """ arping ip6 neighbor advertisement CLI test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ ping_cmd = ("arping gratuitous" + self.pg1.local_ip6 +
+ "pg1 repeat 5 interval 0.1")
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ ping_cmd = "arping gratuitous" + self.pg1.local_ip6 + "pg1"
+ ret = self.vapi.cli(ping_cmd)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_ip6_na(p, self.pg1.local_ip6,
+ self.pg1.local_ip6)
+ finally:
+ self.vapi.cli("show error")
+
+ def test_arping_ip6_na_api(self):
+ """ arping ip6 neighbor advertisement API test """
+ try:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ ret = self.vapi.arping(address=self.pg1.local_ip6,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=1, repeat=5, interval=0.1)
+ self.logger.info(ret)
+
+ ret = self.vapi.arping(address=self.pg1.local_ip6,
+ sw_if_index=self.pg1.sw_if_index,
+ is_garp=1)
+ self.logger.info(ret)
+
+ out = self.pg1.get_capture(6)
+ for p in out:
+ self.verify_arping_ip6_na(p, self.pg1.local_ip6,
+ self.pg1.local_ip6)
+ finally:
+ self.vapi.cli("show error")
+
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)