aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vnet/classify/pcap_classify.h59
-rw-r--r--src/vnet/ethernet/node.c31
-rw-r--r--src/vnet/interface_output.c181
-rw-r--r--test/test_trace_filter.py73
4 files changed, 198 insertions, 146 deletions
diff --git a/src/vnet/classify/pcap_classify.h b/src/vnet/classify/pcap_classify.h
new file mode 100644
index 00000000000..1f1c38fd6e6
--- /dev/null
+++ b/src/vnet/classify/pcap_classify.h
@@ -0,0 +1,59 @@
+/*
+ * pcap_classify.h - Use the classifier to decide if a packet is captured
+ *
+ * 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 <vnet/vnet.h>
+#include <vnet/classify/vnet_classify.h>
+#include <vnet/classify/trace_classify.h>
+
+/** @file pcap_classify.h
+ * Use the vpp classifier to decide whether to capture packets
+ */
+
+/** @brief vnet_is_packet_pcaped
+ * @param vlib_buffer_t *b - packet to capture
+ * @return 0 => no capture, 1 => capture
+ */
+
+static_always_inline int
+vnet_is_packet_pcaped (vnet_pcap_t *pp, vlib_buffer_t *b, u32 sw_if_index)
+{
+ const u32 pcap_sw_if_index = pp->pcap_sw_if_index;
+ const u32 filter_classify_table_index = pp->filter_classify_table_index;
+
+ if (pcap_sw_if_index != 0)
+ {
+ if (~0 == sw_if_index)
+ sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
+ if (pcap_sw_if_index != sw_if_index)
+ return 0; /* wrong interface, skip */
+ }
+
+ if (filter_classify_table_index != ~0 &&
+ vnet_is_packet_traced_inline (b, filter_classify_table_index,
+ 0 /* full classify */) != 1)
+ return 0; /* not matching the filter, skip */
+
+ return 1; /* success */
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ethernet/node.c b/src/vnet/ethernet/node.c
index c31e22299b3..88b4a70a28a 100644
--- a/src/vnet/ethernet/node.c
+++ b/src/vnet/ethernet/node.c
@@ -44,7 +44,7 @@
#include <vnet/devices/pipe/pipe.h>
#include <vppinfra/sparse_vec.h>
#include <vnet/l2/l2_bvi.h>
-#include <vnet/classify/trace_classify.h>
+#include <vnet/classify/pcap_classify.h>
#define foreach_ethernet_input_next \
_ (PUNT, "error-punt") \
@@ -1169,38 +1169,13 @@ ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
n_left = from_frame->n_vectors;
while (n_left > 0)
{
- int classify_filter_result;
vlib_buffer_t *b0;
bi0 = from[0];
from++;
n_left--;
b0 = vlib_get_buffer (vm, bi0);
- if (pp->filter_classify_table_index != ~0)
- {
- classify_filter_result =
- vnet_is_packet_traced_inline
- (b0, pp->filter_classify_table_index, 0 /* full classify */ );
- if (classify_filter_result)
- pcap_add_buffer (&pp->pcap_main, vm, bi0,
- pp->max_bytes_per_pkt);
- continue;
- }
-
- if (pp->pcap_sw_if_index == 0 ||
- pp->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX])
- {
- vnet_hw_interface_t *hi =
- vnet_get_sup_hw_interface
- (vnm, vnet_buffer (b0)->sw_if_index[VLIB_RX]);
-
- /* Capture pkt if not filtered, or if filter hits */
- if (hi->trace_classify_table_index == ~0 ||
- vnet_is_packet_traced_inline
- (b0, hi->trace_classify_table_index,
- 0 /* full classify */ ))
- pcap_add_buffer (&pp->pcap_main, vm, bi0,
- pp->max_bytes_per_pkt);
- }
+ if (vnet_is_packet_pcaped (pp, b0, ~0))
+ pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
}
}
}
diff --git a/src/vnet/interface_output.c b/src/vnet/interface_output.c
index fb322604eda..9ae07714485 100644
--- a/src/vnet/interface_output.c
+++ b/src/vnet/interface_output.c
@@ -45,7 +45,7 @@
#include <vnet/ip/ip6.h>
#include <vnet/udp/udp_packet.h>
#include <vnet/feature/feature.h>
-#include <vnet/classify/trace_classify.h>
+#include <vnet/classify/pcap_classify.h>
#include <vnet/interface_output.h>
typedef struct
@@ -309,36 +309,16 @@ static_always_inline void vnet_interface_pcap_tx_trace
while (n_left_from > 0)
{
- int classify_filter_result;
u32 bi0 = from[0];
vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
from++;
n_left_from--;
- if (pp->filter_classify_table_index != ~0)
- {
- classify_filter_result =
- vnet_is_packet_traced_inline
- (b0, pp->filter_classify_table_index, 0 /* full classify */ );
- if (classify_filter_result)
- pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
- continue;
- }
-
if (sw_if_index_from_buffer)
sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
- if (pp->pcap_sw_if_index == 0 || pp->pcap_sw_if_index == sw_if_index)
- {
- vnet_main_t *vnm = vnet_get_main ();
- vnet_hw_interface_t *hi =
- vnet_get_sup_hw_interface (vnm, sw_if_index);
- /* Capture pkt if not filtered, or if filter hits */
- if (hi->trace_classify_table_index == ~0 ||
- vnet_is_packet_traced_inline
- (b0, hi->trace_classify_table_index, 0 /* full classify */ ))
- pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
- }
+ if (vnet_is_packet_pcaped (pp, b0, sw_if_index))
+ pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
}
}
@@ -837,8 +817,6 @@ pcap_drop_trace (vlib_main_t * vm,
i16 save_current_data;
u16 save_current_length;
vlib_error_main_t *em = &vm->error_main;
- int do_trace = 0;
-
from = vlib_frame_vector_args (f);
@@ -860,97 +838,86 @@ pcap_drop_trace (vlib_main_t * vm,
&& hash_get (im->pcap_drop_filter_hash, b0->error))
continue;
- do_trace = (pp->pcap_sw_if_index == 0) ||
- pp->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ if (!vnet_is_packet_pcaped (pp, b0, ~0))
+ continue; /* not matching, skip */
- if (PREDICT_FALSE
- (do_trace == 0 && pp->filter_classify_table_index != ~0))
+ /* Trace all drops, or drops received on a specific interface */
+ save_current_data = b0->current_data;
+ save_current_length = b0->current_length;
+
+ /*
+ * Typically, we'll need to rewind the buffer
+ * if l2_hdr_offset is valid, make sure to rewind to the start of
+ * the L2 header. This may not be the buffer start in case we pop-ed
+ * vlan tags.
+ * Otherwise, rewind to buffer start and hope for the best.
+ */
+ if (b0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
{
- do_trace = vnet_is_packet_traced_inline
- (b0, pp->filter_classify_table_index, 0 /* full classify */ );
+ if (b0->current_data > vnet_buffer (b0)->l2_hdr_offset)
+ vlib_buffer_advance (b0, vnet_buffer (b0)->l2_hdr_offset -
+ b0->current_data);
}
-
- /* Trace all drops, or drops received on a specific interface */
- if (do_trace)
+ else if (b0->current_data > 0)
{
- save_current_data = b0->current_data;
- save_current_length = b0->current_length;
-
- /*
- * Typically, we'll need to rewind the buffer
- * if l2_hdr_offset is valid, make sure to rewind to the start of
- * the L2 header. This may not be the buffer start in case we pop-ed
- * vlan tags.
- * Otherwise, rewind to buffer start and hope for the best.
- */
- if (b0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
- {
- if (b0->current_data > vnet_buffer (b0)->l2_hdr_offset)
- vlib_buffer_advance (b0,
- vnet_buffer (b0)->l2_hdr_offset -
- b0->current_data);
- }
- else if (b0->current_data > 0)
- vlib_buffer_advance (b0, (word) - b0->current_data);
+ vlib_buffer_advance (b0, (word) -b0->current_data);
+ }
+ {
+ vlib_buffer_t *last = b0;
+ u32 error_node_index;
+ int drop_string_len;
+ vlib_node_t *n;
+ /* Length of the error string */
+ int error_string_len =
+ clib_strnlen (em->counters_heap[b0->error].name, 128);
+
+ /* Dig up the drop node */
+ error_node_index = vm->node_main.node_by_error[b0->error];
+ n = vlib_get_node (vm, error_node_index);
+
+ /* Length of full drop string, w/ "nodename: " prepended */
+ drop_string_len = error_string_len + vec_len (n->name) + 2;
+
+ /* Find the last buffer in the chain */
+ while (last->flags & VLIB_BUFFER_NEXT_PRESENT)
+ last = vlib_get_buffer (vm, last->next_buffer);
+
+ /*
+ * Append <nodename>: <error-string> to the capture,
+ * only if we can do that without allocating a new buffer.
+ */
+ if (PREDICT_TRUE ((last->current_data + last->current_length) <
+ (VLIB_BUFFER_DEFAULT_DATA_SIZE - drop_string_len)))
{
- vlib_buffer_t *last = b0;
- u32 error_node_index;
- int drop_string_len;
- vlib_node_t *n;
- /* Length of the error string */
- int error_string_len =
- clib_strnlen (em->counters_heap[b0->error].name, 128);
-
- /* Dig up the drop node */
- error_node_index = vm->node_main.node_by_error[b0->error];
- n = vlib_get_node (vm, error_node_index);
-
- /* Length of full drop string, w/ "nodename: " prepended */
- drop_string_len = error_string_len + vec_len (n->name) + 2;
-
- /* Find the last buffer in the chain */
- while (last->flags & VLIB_BUFFER_NEXT_PRESENT)
- last = vlib_get_buffer (vm, last->next_buffer);
-
- /*
- * Append <nodename>: <error-string> to the capture,
- * only if we can do that without allocating a new buffer.
- */
- if (PREDICT_TRUE ((last->current_data + last->current_length)
- < (VLIB_BUFFER_DEFAULT_DATA_SIZE
- - drop_string_len)))
- {
- clib_memcpy_fast (last->data + last->current_data +
- last->current_length, n->name,
- vec_len (n->name));
- clib_memcpy_fast (last->data + last->current_data +
- last->current_length + vec_len (n->name),
- ": ", 2);
- clib_memcpy_fast (last->data + last->current_data +
- last->current_length + vec_len (n->name) +
- 2, em->counters_heap[b0->error].name,
- error_string_len);
- last->current_length += drop_string_len;
- b0->flags &= ~(VLIB_BUFFER_TOTAL_LENGTH_VALID);
- pcap_add_buffer (&pp->pcap_main, vm, bi0,
- pp->max_bytes_per_pkt);
- last->current_length -= drop_string_len;
- b0->current_data = save_current_data;
- b0->current_length = save_current_length;
- continue;
- }
+ clib_memcpy_fast (last->data + last->current_data +
+ last->current_length,
+ n->name, vec_len (n->name));
+ clib_memcpy_fast (last->data + last->current_data +
+ last->current_length + vec_len (n->name),
+ ": ", 2);
+ clib_memcpy_fast (last->data + last->current_data +
+ last->current_length + vec_len (n->name) + 2,
+ em->counters_heap[b0->error].name,
+ error_string_len);
+ last->current_length += drop_string_len;
+ b0->flags &= ~(VLIB_BUFFER_TOTAL_LENGTH_VALID);
+ pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
+ last->current_length -= drop_string_len;
+ b0->current_data = save_current_data;
+ b0->current_length = save_current_length;
+ continue;
}
+ }
- /*
- * Didn't have space in the last buffer, here's the dropped
- * packet as-is
- */
- pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
+ /*
+ * Didn't have space in the last buffer, here's the dropped
+ * packet as-is
+ */
+ pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
- b0->current_data = save_current_data;
- b0->current_length = save_current_length;
- }
+ b0->current_data = save_current_data;
+ b0->current_length = save_current_length;
}
}
diff --git a/test/test_trace_filter.py b/test/test_trace_filter.py
index 89ab3648169..8869b3255d3 100644
--- a/test/test_trace_filter.py
+++ b/test/test_trace_filter.py
@@ -11,6 +11,7 @@ from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP
from scapy.layers.vxlan import VXLAN
from scapy.compat import raw
+from scapy.utils import rdpcap
class TestTracefilter(VppTestCase):
@@ -49,27 +50,33 @@ class TestTracefilter(VppTestCase):
# check number of hits for classifier
def assert_hits(self, n):
- r = self.cli("show classify table verbose 2")
+ r = self.cli("show classify table verbose")
self.assertTrue(r.reply.find("hits %i" % n) != -1)
- def add_filter(self, mask, match):
- r = self.cli("classify filter trace mask %s match %s" % (mask, match))
- self.vapi.cli("clear trace")
- r = self.cli("trace add pg-input 1000 filter")
+ def add_trace_filter(self, mask, match):
+ self.cli("classify filter trace mask %s match %s" % (mask, match))
+ self.cli("clear trace")
+ self.cli("trace add pg-input 1000 filter")
- def del_all_filters(self):
+ def del_trace_filters(self):
self.cli("classify filter trace del")
r = self.cli("show classify filter")
s = "packet tracer: first table none"
self.assertTrue(r.reply.find(s) != -1)
+ def del_pcap_filters(self):
+ self.cli("classify filter pcap del")
+ r = self.cli("show classify filter")
+ s = "pcap rx/tx/drop: first table none"
+ self.assertTrue(r.reply.find(s) != -1)
+
def test_basic(self):
""" Packet Tracer Filter Test """
- self.add_filter(
+ self.add_trace_filter(
"l3 ip4 src",
"l3 ip4 src %s" %
self.pg0.remote_hosts[5].ip4)
- self.add_filter(
+ self.add_trace_filter(
"l3 ip4 proto l4 src_port",
"l3 ip4 proto 17 l4 src_port 2345")
# the packet we are trying to match
@@ -91,14 +98,14 @@ class TestTracefilter(VppTestCase):
self.assert_hits(9)
self.assert_hits(17)
- self.del_all_filters()
+ self.del_trace_filters()
# install a classify rule, inject traffic and check for hits
def assert_classify(self, mask, match, packets, n=None):
- self.add_filter("hex %s" % mask, "hex %s" % match)
+ self.add_trace_filter("hex %s" % mask, "hex %s" % match)
self.send_and_expect(self.pg0, packets, self.pg1, trace=False)
self.assert_hits(n if n is not None else len(packets))
- self.del_all_filters()
+ self.del_trace_filters()
def test_encap(self):
""" Packet Tracer Filter Test with encap """
@@ -156,6 +163,50 @@ class TestTracefilter(VppTestCase):
match = "".join(("{:02x}".format(o ^ n) for o, n in zip(ori, new)))
self.assert_classify(mask, match, [p] * 17)
+ def test_pcap(self):
+ """ Packet Capture Filter Test """
+ self.cli(
+ "classify filter pcap mask l3 ip4 src match l3 ip4 src %s" %
+ self.pg0.remote_hosts[5].ip4)
+ self.cli(
+ "classify filter pcap "
+ "mask l3 ip4 proto l4 src_port "
+ "match l3 ip4 proto 17 l4 src_port 2345")
+ self.cli(
+ "pcap trace rx tx max 1000 intfc pg0 "
+ "file vpp_test_trace_filter.pcap filter")
+ # the packet we are trying to match
+ p = list()
+ for i in range(100):
+ src = self.pg0.remote_hosts[i % len(self.pg0.remote_hosts)].ip4
+ p.append((Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=src, dst=self.pg1.remote_ip4) /
+ UDP(sport=1234, dport=2345) / Raw('\xa5' * 100)))
+ for i in range(17):
+ p.append((Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_hosts[0].ip4,
+ dst=self.pg1.remote_ip4) /
+ UDP(sport=2345, dport=1234) / Raw('\xa5' * 100)))
+
+ self.send_and_expect(self.pg0, p, self.pg1, trace=False)
+
+ # Check for 9 and 17 classifier hits, which is the right answer
+ self.assert_hits(9)
+ self.assert_hits(17)
+
+ self.cli("pcap trace rx tx off")
+ self.del_pcap_filters()
+
+ # check captured pcap
+ pcap = rdpcap("/tmp/vpp_test_trace_filter.pcap")
+ self.assertEqual(len(pcap), 9 + 17)
+ p_ = str(p[5])
+ for i in range(9):
+ self.assertEqual(str(pcap[i]), p_)
+ p_ = str(p[100])
+ for i in range(9, 9 + 17):
+ self.assertEqual(str(pcap[i]), p_)
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)