arping: add arping command 78/31278/16
authorSteven Luong <sluong@cisco.com>
Sun, 14 Feb 2021 19:37:02 +0000 (11:37 -0800)
committerBeno�t Ganne <bganne@cisco.com>
Fri, 2 Apr 2021 08:44:52 +0000 (08:44 +0000)
Add linux similar arping command to VPP.
syntax: arping [gratuitous] <address> <interface> [repeat <count>] [interval <secs>]

Type: feature

Signed-off-by: Steven Luong <sluong@cisco.com>
Change-Id: I9267c054235207b8fae8e3f159246777eb0340dd

MAINTAINERS
src/plugins/arping/CMakeLists.txt [new file with mode: 0644]
src/plugins/arping/FEATURE.yaml [new file with mode: 0644]
src/plugins/arping/arping.api [new file with mode: 0644]
src/plugins/arping/arping.c [new file with mode: 0644]
src/plugins/arping/arping.h [new file with mode: 0644]
src/plugins/arping/arping_api.c [new file with mode: 0644]
src/plugins/arping/arping_test.c [new file with mode: 0644]
src/plugins/arping/test/test_arping.py [new file with mode: 0644]

index 4f0a368..0ee7796 100644 (file)
@@ -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 (file)
index 0000000..afec21e
--- /dev/null
@@ -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 (file)
index 0000000..c947b17
--- /dev/null
@@ -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 (file)
index 0000000..f797b8c
--- /dev/null
@@ -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 (file)
index 0000000..6c5836b
--- /dev/null
@@ -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 (file)
index 0000000..d07e7cf
--- /dev/null
@@ -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 (file)
index 0000000..015c614
--- /dev/null
@@ -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 (file)
index 0000000..9001b70
--- /dev/null
@@ -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 (file)
index 0000000..bd8b625
--- /dev/null
@@ -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)