From 87d24db65facb89ca524c951b8379ca2ec4dbc7a Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Wed, 4 Dec 2019 17:19:12 -0500 Subject: classify: vpp packet tracer support Configure n-tuple classifier filters which apply to the vpp packet tracer. Update the documentation to reflect the new feature. Add a test vector. Type: feature Signed-off-by: Dave Barach Change-Id: Iefa911716c670fc12e4825b937b62044433fec36 --- docs/gettingstarted/developers/vnet.md | 13 ++- extras/pcapcli/setup.tracefilter | 20 +++++ src/vlib/main.h | 10 +++ src/vlib/trace.c | 33 ++++++- src/vlib/trace_funcs.h | 16 ++++ src/vnet/classify/vnet_classify.c | 151 ++++++++++++++++++++++++++------- test/test_trace_filter.py | 64 ++++++++++++++ 7 files changed, 271 insertions(+), 36 deletions(-) create mode 100644 extras/pcapcli/setup.tracefilter create mode 100644 test/test_trace_filter.py diff --git a/docs/gettingstarted/developers/vnet.md b/docs/gettingstarted/developers/vnet.md index a5cd9e73594..d9bd7b54de7 100644 --- a/docs/gettingstarted/developers/vnet.md +++ b/docs/gettingstarted/developers/vnet.md @@ -526,10 +526,10 @@ These commands have the following optional parameters: ## packet trace capture filtering -The "classify filter pcap | " debug CLI command +The "classify filter pcap | | trace" debug CLI command constructs an arbitrary set of packet classifier tables for use with "pcap rx | tx | drop trace," and with the vpp packet tracer on a -per-interface basis. +per-interface or system-wide basis. Packets which match a rule in the classifier table chain will be traced. The tables are automatically ordered so that matches in the @@ -575,10 +575,17 @@ Note that per-interface capture filters are _always_ applied. pcap trace tx max 100 filter ``` +### Configure a vpp packet tracer filter + +``` + classify filter trace mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10 + trace add dpdk-input 100 filter +``` + ### Clear all current classifier filters ``` - classify filter del + classify filter [pcap | | trace] del ``` ### To inspect the classifier tables diff --git a/extras/pcapcli/setup.tracefilter b/extras/pcapcli/setup.tracefilter new file mode 100644 index 00000000000..e6c86298c84 --- /dev/null +++ b/extras/pcapcli/setup.tracefilter @@ -0,0 +1,20 @@ +set term pag off +loop create + +set int ip address loop0 192.168.1.1/24 +set int state loop0 up + +packet-generator new { + name pg0 + limit 100 + size 300-300 + interface loop0 + node ethernet-input + data { IP4: 1.2.3 -> 4.5.6 + UDP: 192.168.1.10 - 192.168.1.20 -> 192.168.2.10 + UDP: 1234 -> 2345 + incrementing 286 + } +} + +classify filter trace mask l3 ip4 src match l3 ip4 src 192.168.1.15 diff --git a/src/vlib/main.h b/src/vlib/main.h index e230ddf8419..af6539efc75 100644 --- a/src/vlib/main.h +++ b/src/vlib/main.h @@ -73,6 +73,13 @@ typedef struct u32 filter_classify_table_index; } vnet_pcap_t; +typedef struct +{ + u8 trace_filter_enable; + u32 trace_classify_table_index; + u32 trace_filter_set_index; +} vlib_trace_filter_t; + typedef struct vlib_main_t { CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); @@ -165,6 +172,9 @@ typedef struct vlib_main_t /* pcap rx / tx tracing */ vnet_pcap_t pcap; + /* Packet trace capture filter */ + vlib_trace_filter_t trace_filter; + /* Error handling. */ vlib_error_main_t error_main; diff --git a/src/vlib/trace.c b/src/vlib/trace.c index 530598d349d..7ee1b63f07e 100644 --- a/src/vlib/trace.c +++ b/src/vlib/trace.c @@ -351,6 +351,13 @@ VLIB_CLI_COMMAND (show_trace_cli,static) = { }; /* *INDENT-ON* */ +int vlib_enable_disable_pkt_trace_filter (int enable) __attribute__ ((weak)); +int +vlib_enable_disable_pkt_trace_filter (int enable) +{ + return 0; +} + static clib_error_t * cli_add_trace_buffer (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -361,6 +368,7 @@ cli_add_trace_buffer (vlib_main_t * vm, vlib_trace_node_t *tn; u32 node_index, add; u8 verbose = 0; + int filter = 0; clib_error_t *error = 0; if (!unformat_user (input, unformat_line_input, line_input)) @@ -376,6 +384,8 @@ cli_add_trace_buffer (vlib_main_t * vm, ; else if (unformat (line_input, "verbose")) verbose = 1; + else if (unformat (line_input, "filter")) + filter = 1; else { error = clib_error_create ("expected NODE COUNT, got `%U'", @@ -395,6 +405,15 @@ cli_add_trace_buffer (vlib_main_t * vm, goto done; } + if (filter) + { + if (vlib_enable_disable_pkt_trace_filter (1 /* enable */ )) + { + error = clib_error_create ("No packet trace filter configured..."); + goto done; + } + } + /* *INDENT-OFF* */ foreach_vlib_main (( { @@ -421,7 +440,6 @@ VLIB_CLI_COMMAND (add_trace_cli,static) = { }; /* *INDENT-ON* */ - /* * Configure a filter for packet traces. * @@ -523,6 +541,7 @@ static clib_error_t * cli_clear_trace_buffer (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { + vlib_enable_disable_pkt_trace_filter (0 /* enable */ ); clear_trace_buffer (); return 0; } @@ -541,6 +560,18 @@ vlib_trace_cli_reference (void) { } +int +vnet_is_packet_traced (vlib_buffer_t * b, + u32 classify_table_index, int func) +__attribute__ ((weak)); + +int +vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func) +{ + clib_warning ("BUG: STUB called"); + return 1; +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vlib/trace_funcs.h b/src/vlib/trace_funcs.h index 257c3a3a4ff..4261f675aec 100644 --- a/src/vlib/trace_funcs.h +++ b/src/vlib/trace_funcs.h @@ -61,6 +61,9 @@ vlib_add_trace (vlib_main_t * vm, ASSERT (vnet_trace_dummy); + if (PREDICT_FALSE ((b->flags & VLIB_BUFFER_IS_TRACED) == 0)) + return vnet_trace_dummy; + if (PREDICT_FALSE (tm->add_trace_callback != 0)) { return tm->add_trace_callback ((struct vlib_main_t *) vm, @@ -118,6 +121,9 @@ vlib_trace_next_frame (vlib_main_t * vm, } void trace_apply_filter (vlib_main_t * vm); +int vnet_is_packet_traced (vlib_buffer_t * b, + u32 classify_table_index, int func); + /* Mark buffer as traced and allocate trace buffer. */ always_inline void @@ -131,6 +137,16 @@ vlib_trace_buffer (vlib_main_t * vm, if (PREDICT_FALSE (tm->trace_enable == 0)) return; + /* Classifier filter in use? */ + if (PREDICT_FALSE (vlib_global_main.trace_filter.trace_filter_enable)) + { + /* See if we're supposed to trace this packet... */ + if (vnet_is_packet_traced + (b, vlib_global_main.trace_filter.trace_classify_table_index, + 0 /* full classify */ ) != 1) + return; + } + /* * Apply filter to existing traces to keep number of allocated traces low. * Performed each time around the main loop. diff --git a/src/vnet/classify/vnet_classify.c b/src/vnet/classify/vnet_classify.c index 938f583ef57..3d338d15d07 100755 --- a/src/vnet/classify/vnet_classify.c +++ b/src/vnet/classify/vnet_classify.c @@ -19,6 +19,7 @@ #include /* for L2_INPUT_CLASSIFY_NEXT_xxx */ #include #include +#include /** * @file @@ -1692,12 +1693,14 @@ classify_filter_command_fn (vlib_main_t * vm, u32 current_data_flag = 0; int current_data_offset = 0; u32 sw_if_index = ~0; + int pkt_trace = 0; int i; vnet_classify_table_t *t; u8 *mask = 0; vnet_classify_main_t *cm = &vnet_classify_main; int rv = 0; vnet_classify_filter_set_t *set = 0; + u32 set_index = ~0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { @@ -1705,6 +1708,8 @@ classify_filter_command_fn (vlib_main_t * vm, is_add = 0; else if (unformat (input, "pcap %=", &sw_if_index, 0)) ; + else if (unformat (input, "trace")) + pkt_trace = 1; else if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index)) ; @@ -1720,6 +1725,9 @@ classify_filter_command_fn (vlib_main_t * vm, break; } + if (sw_if_index == 0) + return clib_error_return (0, "Local interface not supported..."); + if (is_add && mask == 0 && table_index == ~0) return clib_error_return (0, "Mask required"); @@ -1729,18 +1737,25 @@ classify_filter_command_fn (vlib_main_t * vm, if (is_add && match == ~0 && table_index == ~0) return clib_error_return (0, "match count required"); - if (sw_if_index == ~0) - return clib_error_return (0, "Must specify pcap or interface..."); + if (sw_if_index == ~0 && pkt_trace == 0) + return clib_error_return (0, "Must specify trace, pcap or interface..."); + + if (pkt_trace && sw_if_index != ~0) + return clib_error_return (0, "Packet trace filter is per-system"); if (!is_add) { - u32 set_index = 0; - if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index)) + if (pkt_trace) + set_index = vlib_global_main.trace_filter.trace_filter_set_index; + else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index)) set_index = cm->filter_set_by_sw_if_index[sw_if_index]; - if (set_index == 0) + if (set_index == ~0) { + if (pkt_trace) + return clib_error_return (0, + "No pkt trace classify filter set..."); if (sw_if_index == 0) return clib_error_return (0, "No pcap classify filter set..."); else @@ -1759,27 +1774,36 @@ classify_filter_command_fn (vlib_main_t * vm, table_index = set->table_indices[0]; vec_reset_length (set->table_indices); pool_put (cm->filter_sets, set); - cm->filter_set_by_sw_if_index[sw_if_index] = 0; - if (sw_if_index > 0) + if (pkt_trace) { - vnet_hw_interface_t *hi = - vnet_get_sup_hw_interface (vnm, sw_if_index); - hi->trace_classify_table_index = ~0; + vlib_global_main.trace_filter.trace_filter_set_index = ~0; + vlib_global_main.trace_filter.trace_classify_table_index = ~0; + } + else + { + cm->filter_set_by_sw_if_index[sw_if_index] = ~0; + if (sw_if_index > 0) + { + vnet_hw_interface_t *hi = + vnet_get_sup_hw_interface (vnm, sw_if_index); + hi->trace_classify_table_index = ~0; + } } } } if (is_add) { - u32 set_index = 0; - - if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index)) + if (pkt_trace) + set_index = vlib_global_main.trace_filter.trace_filter_set_index; + else if (sw_if_index < vec_len (cm->filter_set_by_sw_if_index)) set_index = cm->filter_set_by_sw_if_index[sw_if_index]; /* Do we have a filter set for this intfc / pcap yet? */ - if (set_index == 0) + if (set_index == ~0) { pool_get (cm->filter_sets, set); + set_index = set - cm->filter_sets; set->refcnt = 1; } else @@ -1826,11 +1850,18 @@ classify_filter_command_fn (vlib_main_t * vm, /* Remember the table */ vec_add1 (set->table_indices, table_index); - vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index, 0); - cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets; + + if (pkt_trace) + vlib_global_main.trace_filter.trace_filter_set_index = set_index; + else + { + vec_validate_init_empty (cm->filter_set_by_sw_if_index, sw_if_index, + ~0); + cm->filter_set_by_sw_if_index[sw_if_index] = set - cm->filter_sets; + } /* Put top table index where device drivers can find them */ - if (sw_if_index > 0) + if (sw_if_index > 0 && pkt_trace == 0) { vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index); ASSERT (vec_len (set->table_indices) > 0); @@ -1877,10 +1908,34 @@ found_table: return 0; } +/** Enable / disable packet trace filter */ +int +vlib_enable_disable_pkt_trace_filter (int enable) +{ + if (enable) + { + vnet_classify_main_t *cm = &vnet_classify_main; + vnet_classify_filter_set_t *set; + u32 set_index = vlib_global_main.trace_filter.trace_filter_set_index; + + if (set_index == ~0) + return -1; + + set = pool_elt_at_index (cm->filter_sets, set_index); + vlib_global_main.trace_filter.trace_classify_table_index = + set->table_indices[0]; + vlib_global_main.trace_filter.trace_filter_enable = 1; + } + else + { + vlib_global_main.trace_filter.trace_filter_enable = 0; + } + return 0; +} + /*? * Construct an arbitrary set of packet classifier tables for use with - * "pcap rx | tx trace," and (eventually) with the vpp packet - * tracer + * "pcap rx | tx trace," and with the vpp packet tracer * * Packets which match a rule in the classifier table chain * will be traced. The tables are automatically ordered so that @@ -1925,16 +1980,24 @@ found_table: * * Configure a simple classify filter, and configure pcap rx trace to use it: * - * classify filter mask l3 ip4 src match l3 ip4 src 192.168.1.11"
+ * classify filter rx mask l3 ip4 src match l3 ip4 src 192.168.1.11"
* pcap rx trace on max 100 filter * * Configure another fairly simple filter * * classify filter mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10" * - * Clear all current classifier filters * - * classify filter del + * Configure a filter for use with the vpp packet tracer: + * classify filter trace mask l3 ip4 src dst match l3 ip4 src 192.168.1.10 dst 192.168.2.10" + * trace add dpdk-input 100 filter + * + * Clear classifier filters + * + * classify filter [trace | rx | tx | ] del + * + * To display the top-level classifier tables for each use case: + * show classify filter * * To inspect the classifier tables, use * @@ -1947,8 +2010,9 @@ VLIB_CLI_COMMAND (classify_filter, static) = { .path = "classify filter", .short_help = - "classify filter | pcap mask match [del]" - "[buckets ] [memory-size ]", + "classify filter | pcap mask match \n" + " | trace mask match [del]\n" + " [buckets ] [memory-size ]", .function = classify_filter_command_fn, }; /* *INDENT-ON* */ @@ -1966,26 +2030,39 @@ show_classify_filter_command_fn (vlib_main_t * vm, u32 set_index; u32 table_index; int verbose = 0; - int i, j; + int i, j, limit; (void) unformat (input, "verbose %=", &verbose, 1); vlib_cli_output (vm, "%-30s%s", "Filter Used By", " Table(s)"); vlib_cli_output (vm, "%-30s%s", "--------------", " --------"); - for (i = 0; i < vec_len (cm->filter_set_by_sw_if_index); i++) + limit = vec_len (cm->filter_set_by_sw_if_index); + + for (i = -1; i < limit; i++) { - set_index = cm->filter_set_by_sw_if_index[i]; + if (i < 0) + set_index = vlib_global_main.trace_filter.trace_filter_set_index; + else + set_index = cm->filter_set_by_sw_if_index[i]; - if (set_index == 0 && verbose == 0) + if (set_index == ~0) continue; set = pool_elt_at_index (cm->filter_sets, set_index); - if (i == 0) - name = format (0, "pcap rx/tx/drop:"); - else - name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i); + switch (i) + { + case -1: + name = format (0, "packet tracer:"); + break; + case 0: + name = format (0, "pcap rx/tx/drop:"); + break; + default: + name = format (0, "%U:", format_vnet_sw_if_index_name, vnm, i); + break; + } if (verbose) { @@ -2875,12 +2952,22 @@ vnet_classify_init (vlib_main_t * vm) set->table_indices[0] = ~0; /* Initialize the pcap filter set */ vec_validate (cm->filter_set_by_sw_if_index, 0); + cm->filter_set_by_sw_if_index[0] = ~0; + /* Initialize the packet tracer filter set */ + vlib_global_main.trace_filter.trace_filter_set_index = ~0; return 0; } VLIB_INIT_FUNCTION (vnet_classify_init); +int +vnet_is_packet_traced (vlib_buffer_t * b, u32 classify_table_index, int func) +{ + return vnet_is_packet_traced_inline (b, classify_table_index, func); +} + + #define TEST_CODE 0 #if TEST_CODE > 0 diff --git a/test/test_trace_filter.py b/test/test_trace_filter.py new file mode 100644 index 00000000000..bde732d289f --- /dev/null +++ b/test/test_trace_filter.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +import unittest + +from framework import VppTestCase, VppTestRunner, running_extended_tests +from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath + + +class TestTracefilter(VppTestCase): + """ Packet Tracer Filter Test """ + + @classmethod + def setUpClass(cls): + super(TestTracefilter, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestTracefilter, cls).tearDownClass() + + def setUp(self): + super(TestTracefilter, self).setUp() + + def tearDown(self): + super(TestTracefilter, self).tearDown() + + def test_mactime_unitTest(self): + """ Packet Tracer Filter Test """ + cmds = ["loopback create", + "set int ip address loop0 192.168.1.1/24", + "set int state loop0 up", + "packet-generator new {\n" + " name classifyme\n" + " limit 100\n" + " size 300-300\n" + " interface loop0\n" + " node ethernet-input\n" + " data { \n" + " IP4: 1.2.3 -> 4.5.6\n" + " UDP: 192.168.1.10 - 192.168.1.20 -> 192.168.2.10\n" + " UDP: 1234 -> 2345\n" + " incrementing 286\n" + " }\n" + "}\n", + "classify filter trace mask l3 ip4 src\n" + " match l3 ip4 src 192.168.1.15", + "trace add pg-input 100 filter", + "pa en"] + + for cmd in cmds: + r = self.vapi.cli_return_response(cmd) + if r.retval != 0: + if hasattr(r, 'reply'): + self.logger.info(cmd + " FAIL reply " + r.reply) + else: + self.logger.info(cmd + " FAIL retval " + str(r.retval)) + + # Check for 9 classifier hits, which is the right answer + r = self.vapi.cli_return_response("show classify table verbose 2") + self.assertTrue(r.retval == 0) + self.assertTrue(hasattr(r, 'reply')) + self.assertTrue(r.reply.find("hits 9") != -1) + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) -- cgit 1.2.3-korg