arping: add arping command

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
This commit is contained in:
Steven Luong
2021-02-14 11:37:02 -08:00
committed by Beno�t Ganne
parent 19be32876f
commit a77ae47089
9 changed files with 1475 additions and 0 deletions

View 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>

View File

@ -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
)

View File

@ -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]

View File

@ -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:
*/

797
src/plugins/arping/arping.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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:
*/

View File

@ -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:
*/

View File

@ -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)