misc: classifier-based packet trace filter
See .../src/vnet/classify/trace_classify.h for the business end of the scheme. It would be best to hash pkts, prefetch buckets, and do the primary table lookups two at a time. The inline as given works, but perf tuning will be required. "At least it works..." Add "classify filter" debug cli, for example: classify filter mask l3 ip4 src dst \ match l3 ip4 dst 192.168.2.10 src 192.168.1.10 Add "pcap rx | tx trace ... filter" to use the current classify filter chain Patch includes sphinx documentation and doxygen tags. Next step: device-driver integration Type: feature Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: I05b1358a769f61e6d32470e0c87058f640486b26 (cherry picked from commit 9137e5400699bed9f7c0095187839a8b38273100)
This commit is contained in:
Dave Barach
committed by
Damjan Marion
parent
2abe699d10
commit
1b696ac9b3
@ -469,3 +469,111 @@ metadata changes, header checksum changes, and so forth.
|
||||
This should be of significant value when developing new vpp graph
|
||||
nodes. If new code mispositions b->current_data, it will be completely
|
||||
obvious from looking at the dispatch trace in wireshark.
|
||||
|
||||
## pcap rx and tx tracing
|
||||
|
||||
vpp also supports rx and tx packet capture in pcap format, through the
|
||||
"pcap rx trace" and "pcap tx trace" debug CLI commands
|
||||
|
||||
This command is used to start or stop a packet capture, or show
|
||||
the status of packet capture. Note that both "pcap rx trace" and
|
||||
"pcap tx trace" are implemented. The command syntax is identical,
|
||||
simply substitute rx for tx as needed.
|
||||
|
||||
These commands have the following optional parameters:
|
||||
|
||||
on|off- Used to start or stop a packet capture.
|
||||
|
||||
- <b>max _nnnn_</b> - file size, number of packet captures. Once
|
||||
<nnnn> packets have been received, the trace buffer buffer is flushed
|
||||
to the indicated file. Defaults to 1000. Can only be updated if packet
|
||||
capture is off.
|
||||
|
||||
- <b>intfc _interface_ | _any_</b> - Used to specify a given interface,
|
||||
or use '<em>any</em>' to run packet capture on all interfaces.
|
||||
'<em>any</em>' is the default if not provided. Settings from a previous
|
||||
packet capture are preserved, so '<em>any</em>' can be used to reset
|
||||
the interface setting.
|
||||
|
||||
- <b>file _filename_</b> - Used to specify the output filename. The
|
||||
file will be placed in the '<em>/tmp</em>' directory. If _filename_
|
||||
already exists, file will be overwritten. If no filename is
|
||||
provided, '<em>/tmp/rx.pcap or tx.pcap</em>' will be used, depending
|
||||
on capture direction. Can only be updated when pcap capture is off.
|
||||
|
||||
- <b>status</b> - Displays the current status and configured
|
||||
attributes associated with a packet capture. If packet capture is in
|
||||
progress, '<em>status</em>' also will return the number of packets
|
||||
currently in the buffer. Any additional attributes entered on
|
||||
command line with a '<em>status</em>' request will be ignored.
|
||||
|
||||
- <b>filter</b> - Capture packets which match the current packet
|
||||
trace filter set. See next section. Configure the capture filter
|
||||
first.
|
||||
|
||||
## packet trace capture filtering
|
||||
|
||||
The "classify filter" debug CLI command constructs an arbitrary set of
|
||||
packet classifier tables for use with "pcap rx | tx trace," and
|
||||
(eventually) 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 matches in the
|
||||
most specific table are tried first.
|
||||
|
||||
It's reasonably likely that folks will configure a single table with
|
||||
one or two matches. As a result, we configure 8 hash buckets and 128K
|
||||
of match rule space by default. One can override the defaults by
|
||||
specifiying "buckets <nnn>" and "memory-size <xxx>" as desired.
|
||||
|
||||
To build up complex filter chains, repeatedly issue the classify
|
||||
filter debug CLI command. Each command must specify the desired mask
|
||||
and match values. If a classifier table with a suitable mask already
|
||||
exists, the CLI command adds a match rule to the existing table. If
|
||||
not, the CLI command add a new table and the indicated mask rule
|
||||
|
||||
### Configure a simple classify filter
|
||||
|
||||
```
|
||||
classify filter 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
|
||||
pcap tx trace on max 100 filter
|
||||
```
|
||||
|
||||
### Clear all current classifier filters
|
||||
|
||||
```
|
||||
classify filter del
|
||||
```
|
||||
|
||||
### To inspect the classifier tables
|
||||
|
||||
```
|
||||
show classify table [verbose]
|
||||
```
|
||||
|
||||
The verbose form displays all of the match rules, with hit-counters.
|
||||
|
||||
### Terse description of the "mask <xxx>" syntax:
|
||||
|
||||
```
|
||||
l2 src dst proto tag1 tag2 ignore-tag1 ignore-tag2 cos1 cos2 dot1q dot1ad
|
||||
l3 ip4 <ip4-mask> ip6 <ip6-mask>
|
||||
<ip4-mask> version hdr_length src[/width] dst[/width]
|
||||
tos length fragment_id ttl protocol checksum
|
||||
<ip6-mask> version traffic-class flow-label src dst proto
|
||||
payload_length hop_limit protocol
|
||||
l4 tcp <tcp-mask> udp <udp_mask> src_port dst_port
|
||||
<tcp-mask> src dst # ports
|
||||
<udp-mask> src_port dst_port
|
||||
```
|
||||
|
||||
To construct **matches**, add the values to match after the indicated
|
||||
keywords in the mask syntax. For example: "... mask l3 ip4 src" ->
|
||||
"... match l3 ip4 src 192.168.1.11"
|
||||
|
@ -63,6 +63,7 @@ typedef struct
|
||||
int pcap_enable;
|
||||
u32 pcap_sw_if_index;
|
||||
pcap_main_t pcap_main;
|
||||
u32 filter_classify_table_index;
|
||||
} vnet_pcap_t;
|
||||
|
||||
typedef struct vlib_main_t
|
||||
|
@ -384,6 +384,7 @@ list(APPEND VNET_API_FILES lldp/lldp.api)
|
||||
##############################################################################
|
||||
list(APPEND VNET_SOURCES
|
||||
classify/vnet_classify.c
|
||||
classify/trace_classify.h
|
||||
classify/ip_classify.c
|
||||
classify/in_out_acl.c
|
||||
classify/policer_classify.c
|
||||
@ -400,6 +401,7 @@ list(APPEND VNET_MULTIARCH_SOURCES
|
||||
|
||||
list(APPEND VNET_HEADERS
|
||||
classify/vnet_classify.h
|
||||
classify/trace_classify.h
|
||||
classify/in_out_acl.h
|
||||
classify/policer_classify.h
|
||||
classify/flow_classify.h
|
||||
|
100
src/vnet/classify/trace_classify.h
Normal file
100
src/vnet/classify/trace_classify.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* trace_classify.h - Use the classifier to decide if a packet is traced
|
||||
*
|
||||
* Copyright (c) 2019 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 <vppinfra/error.h>
|
||||
#include <vnet/classify/vnet_classify.h>
|
||||
|
||||
/** @file trace_classify.h
|
||||
* Use the vpp classifier to decide whether to trace packets
|
||||
*/
|
||||
|
||||
/** @brief vnet_is_packet_traced
|
||||
* @param vlib_buffer_t *b - packet to classify
|
||||
* @param int func - 0 => use classifier w/ supplied table index
|
||||
* @param u32 classify_table_index - classifier table index
|
||||
* @return 0 => no trace, 1 => trace, -1 => error
|
||||
*/
|
||||
|
||||
static inline int
|
||||
vnet_is_packet_traced_inline (vlib_buffer_t * b,
|
||||
u32 classify_table_index, int func)
|
||||
{
|
||||
vnet_classify_main_t *vcm = &vnet_classify_main;
|
||||
vnet_classify_table_t *t;
|
||||
vnet_classify_entry_t *e;
|
||||
u64 hash;
|
||||
|
||||
/*$$$ add custom classifiers here, if any */
|
||||
if (func != 0)
|
||||
return -1;
|
||||
|
||||
/* This will happen... */
|
||||
if (pool_is_free_index (vcm->tables, classify_table_index))
|
||||
return -1;
|
||||
|
||||
/* Get the table */
|
||||
t = pool_elt_at_index (vcm->tables, classify_table_index);
|
||||
|
||||
/* Hash the packet */
|
||||
hash = vnet_classify_hash_packet (t, vlib_buffer_get_current (b));
|
||||
|
||||
/* See if there's a matching entry */
|
||||
e = vnet_classify_find_entry (t, vlib_buffer_get_current (b), hash,
|
||||
0 /* time = 0, disables hit-counter */ );
|
||||
/* Hit means trace the packet... */
|
||||
if (e)
|
||||
{
|
||||
/* Manual hit accounting */
|
||||
e->hits++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for a hit in a less-specific table.
|
||||
* Performance hint: for this use-case, don't go there.
|
||||
*/
|
||||
while (1)
|
||||
{
|
||||
/* Most likely, we're done right now */
|
||||
if (PREDICT_TRUE (t->next_table_index == ~0))
|
||||
return 0;
|
||||
t = pool_elt_at_index (vcm->tables, t->next_table_index);
|
||||
|
||||
/* Compute hash for this table */
|
||||
hash = vnet_classify_hash_packet (t, vlib_buffer_get_current (b));
|
||||
|
||||
/* See if there's a matching entry */
|
||||
e = vnet_classify_find_entry (t, vlib_buffer_get_current (b), hash,
|
||||
0 /* time = 0, disables hit-counter */ );
|
||||
if (e)
|
||||
{
|
||||
/* Manual hit accounting */
|
||||
e->hits++;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
@ -44,6 +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>
|
||||
|
||||
#define foreach_ethernet_input_next \
|
||||
_ (PUNT, "error-punt") \
|
||||
@ -993,15 +994,29 @@ ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
|
||||
if (PREDICT_FALSE (vlib_global_main.pcap[VLIB_RX].pcap_enable))
|
||||
{
|
||||
u32 bi0;
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
|
||||
from = vlib_frame_vector_args (from_frame);
|
||||
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 (vec_len (vnm->classify_filter_table_indices))
|
||||
{
|
||||
classify_filter_result =
|
||||
vnet_is_packet_traced_inline
|
||||
(b0, vnm->classify_filter_table_indices[0],
|
||||
0 /* full classify */ );
|
||||
if (classify_filter_result)
|
||||
pcap_add_buffer (&vlib_global_main.pcap[VLIB_RX].pcap_main,
|
||||
vm, bi0, 512);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vlib_global_main.pcap[VLIB_RX].pcap_sw_if_index == 0 ||
|
||||
vlib_global_main.pcap[VLIB_RX].pcap_sw_if_index
|
||||
@ -1010,7 +1025,6 @@ ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
|
||||
pcap_add_buffer (&vlib_global_main.pcap[VLIB_RX].pcap_main, vm,
|
||||
bi0, 512);
|
||||
}
|
||||
n_left--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -901,6 +901,7 @@ typedef struct
|
||||
u32 packets_to_capture;
|
||||
vlib_rx_or_tx_t rxtx;
|
||||
u32 sw_if_index;
|
||||
int filter;
|
||||
} vnet_pcap_dispatch_trace_args_t;
|
||||
|
||||
int vnet_pcap_dispatch_trace_configure (vnet_pcap_dispatch_trace_args_t *);
|
||||
|
@ -1699,6 +1699,7 @@ int
|
||||
vnet_pcap_dispatch_trace_configure (vnet_pcap_dispatch_trace_args_t * a)
|
||||
{
|
||||
vlib_main_t *vm = vlib_get_main ();
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
vlib_rx_or_tx_t rxtx = a->rxtx;
|
||||
vnet_pcap_t *pp = &vm->pcap[rxtx];
|
||||
pcap_main_t *pm = &pp->pcap_main;
|
||||
@ -1734,6 +1735,10 @@ vnet_pcap_dispatch_trace_configure (vnet_pcap_dispatch_trace_args_t * a)
|
||||
&& (pm->n_packets_to_capture != a->packets_to_capture))
|
||||
return VNET_API_ERROR_INVALID_VALUE_2;
|
||||
|
||||
if (a->enable && a->filter
|
||||
&& (vec_len (vnm->classify_filter_table_indices) == 0))
|
||||
return VNET_API_ERROR_NO_SUCH_LABEL;
|
||||
|
||||
if (a->enable)
|
||||
{
|
||||
/* Clean up from previous run, if any */
|
||||
@ -1754,6 +1759,11 @@ vnet_pcap_dispatch_trace_configure (vnet_pcap_dispatch_trace_args_t * a)
|
||||
pm->packet_type = PCAP_PACKET_TYPE_ethernet;
|
||||
pm->n_packets_to_capture = a->packets_to_capture;
|
||||
pp->pcap_sw_if_index = a->sw_if_index;
|
||||
if (a->filter)
|
||||
pp->filter_classify_table_index =
|
||||
vnm->classify_filter_table_indices[0];
|
||||
else
|
||||
pp->filter_classify_table_index = ~0;
|
||||
pp->pcap_enable = 1;
|
||||
}
|
||||
else
|
||||
@ -1796,6 +1806,7 @@ pcap_trace_command_internal (vlib_main_t * vm,
|
||||
int rv;
|
||||
int enable = 0;
|
||||
int status = 0;
|
||||
int filter = 0;
|
||||
u32 sw_if_index = 0;
|
||||
|
||||
/* Get a line of input. */
|
||||
@ -1826,6 +1837,8 @@ pcap_trace_command_internal (vlib_main_t * vm,
|
||||
;
|
||||
else if (unformat (line_input, "intfc any"))
|
||||
sw_if_index = 0;
|
||||
else if (unformat (line_input, "filter"))
|
||||
filter = 1;
|
||||
else
|
||||
{
|
||||
return clib_error_return (0, "unknown input `%U'",
|
||||
@ -1842,6 +1855,7 @@ pcap_trace_command_internal (vlib_main_t * vm,
|
||||
a->packets_to_capture = max;
|
||||
a->rxtx = rxtx;
|
||||
a->sw_if_index = sw_if_index;
|
||||
a->filter = filter;
|
||||
|
||||
rv = vnet_pcap_dispatch_trace_configure (a);
|
||||
|
||||
@ -1866,6 +1880,10 @@ pcap_trace_command_internal (vlib_main_t * vm,
|
||||
case VNET_API_ERROR_NO_SUCH_ENTRY:
|
||||
return clib_error_return (0, "No packets captured...");
|
||||
|
||||
case VNET_API_ERROR_NO_SUCH_LABEL:
|
||||
return clib_error_return
|
||||
(0, "No classify filter configured, see 'classify filter...'");
|
||||
|
||||
default:
|
||||
vlib_cli_output (vm, "WARNING: trace configure returned %d", rv);
|
||||
break;
|
||||
@ -1958,7 +1976,6 @@ VLIB_CLI_COMMAND (pcap_rx_trace_command, static) = {
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <vnet/ip/ip6.h>
|
||||
#include <vnet/udp/udp_packet.h>
|
||||
#include <vnet/feature/feature.h>
|
||||
#include <vnet/classify/trace_classify.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -813,13 +814,29 @@ static_always_inline void vnet_interface_pcap_tx_trace
|
||||
else
|
||||
sw_if_index = ~0;
|
||||
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
n_left_from = frame->n_vectors;
|
||||
from = vlib_frame_vector_args (frame);
|
||||
|
||||
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 (vec_len (vnm->classify_filter_table_indices))
|
||||
{
|
||||
classify_filter_result =
|
||||
vnet_is_packet_traced_inline
|
||||
(b0, vnm->classify_filter_table_indices[0],
|
||||
0 /* full classify */ );
|
||||
if (classify_filter_result)
|
||||
pcap_add_buffer (&vlib_global_main.pcap[VLIB_TX].pcap_main, vm,
|
||||
bi0, 512);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sw_if_index_from_buffer)
|
||||
sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
|
||||
@ -828,8 +845,6 @@ static_always_inline void vnet_interface_pcap_tx_trace
|
||||
vlib_global_main.pcap[VLIB_TX].pcap_sw_if_index == sw_if_index)
|
||||
pcap_add_buffer (&vlib_global_main.pcap[VLIB_TX].pcap_main, vm, bi0,
|
||||
512);
|
||||
from++;
|
||||
n_left_from--;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,9 @@ typedef struct vnet_main_t
|
||||
*/
|
||||
vnet_api_error_t api_errno;
|
||||
|
||||
/* pcap rx/tx, packet tracer filter tables */
|
||||
u32 *classify_filter_table_indices;
|
||||
|
||||
vlib_main_t *vlib_main;
|
||||
} vnet_main_t;
|
||||
|
||||
|
Reference in New Issue
Block a user