From ddc16cfcf96ed9193522dd006b9e3edef1107e43 Mon Sep 17 00:00:00 2001 From: Maxime Peim Date: Fri, 13 Jan 2023 08:04:55 +0000 Subject: [PATCH] api: pcap capture api update Allow enabling and disabling pcap capture via the API. A little bug is fixed along the way in vl_api_classify_pcap_set_table_t_handler. Type: improvement Signed-off-by: Maxime Peim Change-Id: I096129c82aecdc82bee5dbfb5e19c76a51d80aab --- src/vnet/classify/classify_api.c | 5 ++- src/vnet/interface.api | 41 ++++++++++++++++++++++ src/vnet/interface_api.c | 59 ++++++++++++++++++++++++++++++++ src/vnet/interface_cli.c | 2 +- src/vnet/interface_test.c | 12 +++++++ test/asf/test_pcap.py | 74 +++++++++++++++++++++++++++++++++++++++- 6 files changed, 188 insertions(+), 5 deletions(-) diff --git a/src/vnet/classify/classify_api.c b/src/vnet/classify/classify_api.c index 9353a647277..c59d60fc6be 100644 --- a/src/vnet/classify/classify_api.c +++ b/src/vnet/classify/classify_api.c @@ -115,9 +115,8 @@ static void vl_api_classify_pcap_set_table_t_handler u32 table_index = ntohl (mp->table_index); u32 sw_if_index = ntohl (mp->sw_if_index); - if (sw_if_index == ~0 - || sw_if_index >= vec_len (cm->classify_table_index_by_sw_if_index) - || (table_index != ~0 && pool_is_free_index (cm->tables, table_index))) + if (sw_if_index == ~0 || + (table_index != ~0 && pool_is_free_index (cm->tables, table_index))) { rv = VNET_API_ERROR_INVALID_VALUE; goto out; diff --git a/src/vnet/interface.api b/src/vnet/interface.api index 172f6afb818..4d28332a639 100644 --- a/src/vnet/interface.api +++ b/src/vnet/interface.api @@ -733,6 +733,47 @@ autoreply define collect_detailed_interface_stats bool enable_disable; }; +/** \brief pcap_trace_on + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param capture_rx - capture received packets + @param capture_tx - capture transmitted packets + @param capture_drop - capture dropped packets + @param filter - is a filter is being used on this capture + @param preallocate_data - preallocate the data buffer + @param free_data - free the data buffer + @param max_packets - depth of local buffer + @param max_bytes_per_packet - maximum number of bytes to capture + for each packet + @param sw_if_index - specify a given interface, or 0 for any + @param error - filter packets based on a specific error. + @param filename - output filename, will be placed in /tmp +*/ +autoreply define pcap_trace_on +{ + u32 client_index; + u32 context; + bool capture_rx; + bool capture_tx; + bool capture_drop; + bool filter; + bool preallocate_data; + bool free_data; + u32 max_packets [default=1000]; + u32 max_bytes_per_packet [default=512]; + vl_api_interface_index_t sw_if_index; + string error[128]; + string filename[64]; + + option vat_help = "pcap_trace_on [capture_rx] [capture_tx] [capture_drop] [max_packets ] [sw_if_index |0 for any] [error .] [filename ] [max_bytes_per_packet ] [filter] [preallocate_data] [free_data]"; +}; + +autoreply define pcap_trace_off +{ + u32 client_index; + u32 context; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c index 288af78b2be..807c908731d 100644 --- a/src/vnet/interface_api.c +++ b/src/vnet/interface_api.c @@ -1604,6 +1604,65 @@ static void REPLY_MACRO (VL_API_SW_INTERFACE_ADDRESS_REPLACE_END_REPLY); } +static void +vl_api_pcap_trace_on_t_handler (vl_api_pcap_trace_on_t *mp) +{ + vl_api_pcap_trace_on_reply_t *rmp; + unformat_input_t filename, drop_err_name; + vnet_pcap_dispatch_trace_args_t capture_args; + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + unformat_init_cstring (&filename, (char *) mp->filename); + if (!unformat_user (&filename, unformat_vlib_tmpfile, + &capture_args.filename)) + { + rv = VNET_API_ERROR_ILLEGAL_NAME; + goto out; + } + + capture_args.rx_enable = mp->capture_rx; + capture_args.tx_enable = mp->capture_tx; + capture_args.preallocate_data = mp->preallocate_data; + capture_args.free_data = mp->free_data; + capture_args.drop_enable = mp->capture_drop; + capture_args.status = 0; + capture_args.packets_to_capture = ntohl (mp->max_packets); + capture_args.sw_if_index = ntohl (mp->sw_if_index); + capture_args.filter = mp->filter; + capture_args.max_bytes_per_pkt = ntohl (mp->max_bytes_per_packet); + capture_args.drop_err = ~0; + + unformat_init_cstring (&drop_err_name, (char *) mp->error); + unformat_user (&drop_err_name, unformat_vlib_error, vlib_get_main (), + &capture_args.drop_err); + + rv = vnet_pcap_dispatch_trace_configure (&capture_args); + + BAD_SW_IF_INDEX_LABEL; + +out: + unformat_free (&filename); + unformat_free (&drop_err_name); + + REPLY_MACRO (VL_API_PCAP_TRACE_ON_REPLY); +} + +static void +vl_api_pcap_trace_off_t_handler (vl_api_pcap_trace_off_t *mp) +{ + vl_api_pcap_trace_off_reply_t *rmp; + vnet_pcap_dispatch_trace_args_t capture_args; + int rv = 0; + + clib_memset (&capture_args, 0, sizeof (capture_args)); + + rv = vnet_pcap_dispatch_trace_configure (&capture_args); + + REPLY_MACRO (VL_API_PCAP_TRACE_OFF_REPLY); +} + /* * vpe_api_hookup * Add vpe's API message handlers to the table. diff --git a/src/vnet/interface_cli.c b/src/vnet/interface_cli.c index 3515c395e53..1c553e25e97 100644 --- a/src/vnet/interface_cli.c +++ b/src/vnet/interface_cli.c @@ -2390,7 +2390,7 @@ pcap_trace_command_fn (vlib_main_t * vm, * any-interface tests fail. * * - error . - filter packets based on a specific error. - * For example: error {ip4-udp-lookup}.{No listener for dst port} + * For example: error {ip4-udp-lookup}.{no_listener} * * - file - Used to specify the output filename. The file will * be placed in the '/tmp' directory, so only the filename is diff --git a/src/vnet/interface_test.c b/src/vnet/interface_test.c index c3ddcd74cc4..cc406bef23d 100644 --- a/src/vnet/interface_test.c +++ b/src/vnet/interface_test.c @@ -1283,6 +1283,18 @@ api_sw_interface_set_interface_name (vat_main_t *vam) return -1; } +static int +api_pcap_trace_on (vat_main_t *vam) +{ + return -1; +} + +static int +api_pcap_trace_off (vat_main_t *vam) +{ + return -1; +} + #include /* diff --git a/test/asf/test_pcap.py b/test/asf/test_pcap.py index f44a880feac..c2ba1384533 100644 --- a/test/asf/test_pcap.py +++ b/test/asf/test_pcap.py @@ -3,8 +3,11 @@ import os import unittest +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP +from scapy.packet import Raw + from asfframework import VppTestCase, VppTestRunner -from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath class TestPcap(VppTestCase): @@ -14,8 +17,17 @@ class TestPcap(VppTestCase): def setUpClass(cls): super(TestPcap, cls).setUpClass() + cls.create_pg_interfaces(range(1)) + for i in cls.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + @classmethod def tearDownClass(cls): + for i in cls.pg_interfaces: + i.admin_down() + super(TestPcap, cls).tearDownClass() def setUp(self): @@ -86,6 +98,66 @@ class TestPcap(VppTestCase): os.remove("/tmp/rxtx.pcap") os.remove("/tmp/filt.pcap") + def test_pcap_trace_api(self): + """PCAP API Tests""" + + pkt = ( + Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + / IP(src=self.pg0.local_ip4, dst=self.pg0.remote_ip4, ttl=2) + / UDP(sport=1234, dport=2345) + / Raw(b"\xa5" * 128) + ) + + self.vapi.pcap_trace_on( + capture_rx=True, + capture_tx=True, + max_packets=1000, + sw_if_index=0, + filename="trace_any.pcap", + ) + self.pg_send(self.pg0, pkt * 10) + self.vapi.pcap_trace_off() + + self.vapi.cli( + f"classify filter pcap mask l3 ip4 src match l3 ip4 src {self.pg0.local_ip4}" + ) + self.vapi.pcap_trace_on( + capture_rx=True, + capture_tx=True, + filter=True, + max_packets=1000, + sw_if_index=0, + filename="trace_any_filter.pcap", + ) + self.pg_send(self.pg0, pkt * 10) + self.vapi.pcap_trace_off() + self.vapi.cli("classify filter pcap del mask l3 ip4 src") + + pkt = ( + Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) + # wrong destination address + / IP(src=self.pg0.local_ip4, dst=self.pg0.local_ip4, ttl=2) + / UDP(sport=1234, dport=2345) + / Raw(b"\xa5" * 128) + ) + + self.vapi.pcap_trace_on( + capture_drop=True, + max_packets=1000, + sw_if_index=0, + error="{ip4-local}.{spoofed_local_packets}", + filename="trace_drop_err.pcap", + ) + self.pg_send(self.pg0, pkt * 10) + self.vapi.pcap_trace_off() + + self.assertTrue(os.path.exists("/tmp/trace_any.pcap")) + self.assertTrue(os.path.exists("/tmp/trace_any_filter.pcap")) + self.assertTrue(os.path.exists("/tmp/trace_drop_err.pcap")) + os.remove("/tmp/trace_any.pcap") + os.remove("/tmp/trace_any_filter.pcap") + os.remove("/tmp/trace_drop_err.pcap") + if __name__ == "__main__": unittest.main(testRunner=VppTestRunner) -- 2.16.6