api: pcap capture api update 18/37918/5
authorMaxime Peim <mpeim@cisco.com>
Fri, 13 Jan 2023 08:04:55 +0000 (08:04 +0000)
committerOle Tr�an <otroan@employees.org>
Wed, 25 Jan 2023 11:04:05 +0000 (11:04 +0000)
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 <mpeim@cisco.com>
Change-Id: I096129c82aecdc82bee5dbfb5e19c76a51d80aab

src/vnet/classify/classify_api.c
src/vnet/interface.api
src/vnet/interface_api.c
src/vnet/interface_cli.c
src/vnet/interface_test.c
test/asf/test_pcap.py

index 9353a64..c59d60f 100644 (file)
@@ -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;
index 172f6af..4d28332 100644 (file)
@@ -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 <nn>] [sw_if_index <sw_if_index>|0 for any] [error <node>.<error>] [filename <name>] [max_bytes_per_packet <nnnn>] [filter] [preallocate_data] [free_data]";
+};
+
+autoreply define pcap_trace_off
+{
+  u32 client_index;
+  u32 context;
+};
+
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
index 288af78..807c908 100644 (file)
@@ -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.
index 3515c39..1c553e2 100644 (file)
@@ -2390,7 +2390,7 @@ pcap_trace_command_fn (vlib_main_t * vm,
  *   any-interface tests fail.
  *
  * - <b>error <node>.<error></b> - 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}
  *
  * - <b>file <name></b> - Used to specify the output filename. The file will
  *   be placed in the '<em>/tmp</em>' directory, so only the filename is
index c3ddcd7..cc406be 100644 (file)
@@ -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 <vnet/interface.api_test.c>
 
 /*
index f44a880..c2ba138 100644 (file)
@@ -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)