aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Peim <mpeim@cisco.com>2023-07-11 09:45:56 +0200
committerBeno�t Ganne <bganne@cisco.com>2023-09-04 07:57:44 +0000
commit77812045e720d1434dd36a1db90c35daad0c8e00 (patch)
tree4588ff65542af1edec3474010c5a8120efb04705
parent2ceb818f8eca4b8aebd0a4f2b36a641c832d8819 (diff)
tracenode: filtering feature
In order to be able to filter on encapsulated packet, a new node has been added to the ip4/6-unicast arcs. Type: feature Change-Id: I1e8ee05bc6d0fce20cadd8319c81bab260c17d21 Signed-off-by: Maxime Peim <mpeim@cisco.com>
-rw-r--r--MAINTAINERS5
-rw-r--r--docs/spelling_wordlist.txt2
-rw-r--r--src/plugins/tracenode/CMakeLists.txt37
-rw-r--r--src/plugins/tracenode/FEATURE.yaml8
-rw-r--r--src/plugins/tracenode/api.c64
-rw-r--r--src/plugins/tracenode/cli.c72
-rw-r--r--src/plugins/tracenode/node.c145
-rw-r--r--src/plugins/tracenode/plugin.c31
-rw-r--r--src/plugins/tracenode/test.c93
-rw-r--r--src/plugins/tracenode/tracenode.api42
-rw-r--r--src/plugins/tracenode/tracenode.c71
-rw-r--r--src/plugins/tracenode/tracenode.h43
-rw-r--r--test/test_trace_filter.py216
13 files changed, 812 insertions, 17 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index cb09e30ab98..167da0f4c5b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -826,6 +826,11 @@ I: npt66
M: Ole Troan <otroan@employees.org>
F: src/plugins/npt66
+Plugin - Trace node
+I: tracenode
+M: Maxime Peim <mpeim@cisco.com>
+F: src/plugins/tracenode
+
cJSON
I: cjson
M: Ole Troan <ot@cisco.com>
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 4116d1d57af..6a06b3b94df 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -1149,6 +1149,8 @@ tpv
TPv
tracedump
Tracedump
+tracenode
+Tracenode
TRex
Tsou
ttl
diff --git a/src/plugins/tracenode/CMakeLists.txt b/src/plugins/tracenode/CMakeLists.txt
new file mode 100644
index 00000000000..6b6ba2e9865
--- /dev/null
+++ b/src/plugins/tracenode/CMakeLists.txt
@@ -0,0 +1,37 @@
+
+# Copyright (c) 2023 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.
+
+add_vpp_plugin(tracenode
+ SOURCES
+ node.c
+ api.c
+ cli.c
+ plugin.c
+ tracenode.c
+
+ MULTIARCH_SOURCES
+ node.c
+
+ API_FILES
+ tracenode.api
+
+ INSTALL_HEADERS
+ tracenode.h
+
+ API_TEST_SOURCES
+ test.c
+
+ COMPONENT
+ vpp-plugin-devtools
+)
diff --git a/src/plugins/tracenode/FEATURE.yaml b/src/plugins/tracenode/FEATURE.yaml
new file mode 100644
index 00000000000..c405dd11d59
--- /dev/null
+++ b/src/plugins/tracenode/FEATURE.yaml
@@ -0,0 +1,8 @@
+---
+name: Trace node
+maintainer: Maxime Peim <mpeim@cisco.com>
+features:
+ - allow trace filtering on encapsulated (inner) packets
+description: "Allow tracing on IP feature arc. Encapsulated packets can then be traced and filtered."
+state: experimental
+properties: [CLI, API]
diff --git a/src/plugins/tracenode/api.c b/src/plugins/tracenode/api.c
new file mode 100644
index 00000000000..0b01ad8b9f5
--- /dev/null
+++ b/src/plugins/tracenode/api.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2023 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 <tracenode/tracenode.h>
+#include <vlibmemory/api.h>
+
+/* define message IDs */
+#include <tracenode/tracenode.api_enum.h>
+#include <tracenode/tracenode.api_types.h>
+
+#define REPLY_MSG_ID_BASE (tnm->msg_id_base)
+#include <vlibapi/api_helper_macros.h>
+
+static void
+vl_api_tracenode_enable_disable_t_handler (
+ vl_api_tracenode_enable_disable_t *mp)
+{
+ tracenode_main_t *tnm = &tracenode_main;
+ vl_api_tracenode_enable_disable_reply_t *rmp;
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ rv = tracenode_feature_enable_disable (ntohl (mp->sw_if_index), mp->is_pcap,
+ mp->enable);
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (VL_API_TRACENODE_ENABLE_DISABLE_REPLY);
+}
+
+#include <tracenode/tracenode.api.c>
+
+clib_error_t *
+tracenode_plugin_api_hookup (vlib_main_t *vm)
+{
+ tracenode_main_t *tnm = &tracenode_main;
+
+ /* ask for a correctly-sized block of API message decode slots */
+ tnm->msg_id_base = setup_message_id_table ();
+
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */ \ No newline at end of file
diff --git a/src/plugins/tracenode/cli.c b/src/plugins/tracenode/cli.c
new file mode 100644
index 00000000000..8d0ed4176d6
--- /dev/null
+++ b/src/plugins/tracenode/cli.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023 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 <tracenode/tracenode.h>
+
+static clib_error_t *
+tracenode_feature_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u32 sw_if_index = ~0;
+ int enable = 1, is_pcap = 0;
+ int rv;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "disable"))
+ enable = 0;
+ else if (unformat (line_input, "pcap"))
+ is_pcap = 1;
+ else if (unformat (line_input, "%U", unformat_vnet_sw_interface,
+ vnet_get_main (), &sw_if_index))
+ {
+ if (sw_if_index == 0)
+ return clib_error_return (0, "Local interface not supported...");
+ }
+
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0)
+ return clib_error_return (0, "Software interface required");
+
+ if ((rv = tracenode_feature_enable_disable (sw_if_index, is_pcap, enable)) !=
+ 0)
+ return clib_error_return (
+ 0, "vnet_enable_disable_tracenode_feature returned %d", rv);
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (tracenode_feature, static) = {
+ .path = "tracenode feature",
+ .short_help = "tracenode feature <intfc> [disable] [pcap]",
+ .function = tracenode_feature_cmd_fn,
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/tracenode/node.c b/src/plugins/tracenode/node.c
new file mode 100644
index 00000000000..444d93f1708
--- /dev/null
+++ b/src/plugins/tracenode/node.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2023 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/feature/feature.h>
+#include <vnet/classify/pcap_classify.h>
+
+typedef struct
+{
+ u32 sw_if_index;
+} tracenode_trace_t;
+
+static u8 *
+format_tracenode_trace (u8 *s, va_list *args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ vnet_main_t *vnm = vnet_get_main ();
+ tracenode_trace_t *t = va_arg (*args, tracenode_trace_t *);
+
+ s = format (s, "Packet traced from interface %U added",
+ format_vnet_sw_if_index_name, vnm, t->sw_if_index);
+ return s;
+}
+
+static_always_inline u32
+tracenode_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, int is_pcap)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_pcap_t *pp = &vnm->pcap;
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
+ u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
+ u32 *from = vlib_frame_vector_args (frame), *from0 = from;
+ const u32 n_tot = frame->n_vectors;
+ u32 n_left = n_tot;
+
+ vlib_get_buffers (vm, from, b, n_tot);
+
+ while (n_left > 0)
+ {
+ /* TODO: dual/quad loop */
+
+ /* enqueue b0 to the current next frame */
+ vnet_feature_next_u16 (next, b[0]);
+
+ /* buffer already traced */
+ if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
+ goto skip;
+
+ if (is_pcap && vnet_is_packet_pcaped (pp, b[0], ~0))
+ {
+ pcap_add_buffer (&pp->pcap_main, vm, from0[0],
+ pp->max_bytes_per_pkt);
+ }
+ else if (!is_pcap && vlib_trace_buffer (vm, node, next[0], b[0],
+ 1 /* follow_chain */))
+ {
+ tracenode_trace_t *tr = vlib_add_trace (vm, node, b[0], sizeof *tr);
+ tr->sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
+ }
+
+ skip:
+ b++;
+ from0++;
+ next++;
+ n_left--;
+ }
+
+ vlib_buffer_enqueue_to_next (vm, node, from, nexts, n_tot);
+ return n_tot;
+}
+
+VLIB_NODE_FN (trace_filtering_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return tracenode_inline (vm, node, frame, 0 /* is_pcap */);
+}
+
+VLIB_NODE_FN (pcap_filtering_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return tracenode_inline (vm, node, frame, 1 /* is_pcap */);
+}
+
+VLIB_REGISTER_NODE (trace_filtering_node) = {
+ .name = "trace-filtering",
+ .vector_size = sizeof (u32),
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .format_trace = format_tracenode_trace,
+};
+
+VLIB_REGISTER_NODE (pcap_filtering_node) = {
+ .name = "pcap-filtering",
+ .vector_size = sizeof (u32),
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .format_trace = format_tracenode_trace,
+};
+
+VNET_FEATURE_INIT (trace_filtering4, static) = {
+ .arc_name = "ip4-unicast",
+ .node_name = "trace-filtering",
+ .runs_after = VNET_FEATURES ("ip4-full-reassembly-feature",
+ "ip4-sv-reassembly-feature"),
+};
+
+VNET_FEATURE_INIT (trace_filtering6, static) = {
+ .arc_name = "ip6-unicast",
+ .node_name = "trace-filtering",
+ .runs_after = VNET_FEATURES ("ip6-full-reassembly-feature",
+ "ip6-sv-reassembly-feature"),
+};
+
+VNET_FEATURE_INIT (pcap_filtering4, static) = {
+ .arc_name = "ip4-unicast",
+ .node_name = "pcap-filtering",
+ .runs_after = VNET_FEATURES ("ip4-full-reassembly-feature",
+ "ip4-sv-reassembly-feature"),
+};
+
+VNET_FEATURE_INIT (pcap_filtering6, static) = {
+ .arc_name = "ip6-unicast",
+ .node_name = "pcap-filtering",
+ .runs_after = VNET_FEATURES ("ip6-full-reassembly-feature",
+ "ip6-sv-reassembly-feature"),
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/tracenode/plugin.c b/src/plugins/tracenode/plugin.c
new file mode 100644
index 00000000000..19ce6ba5610
--- /dev/null
+++ b/src/plugins/tracenode/plugin.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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/plugin/plugin.h>
+#include <vpp/app/version.h>
+
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Tracing packet node",
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/tracenode/test.c b/src/plugins/tracenode/test.c
new file mode 100644
index 00000000000..a409fd2a59a
--- /dev/null
+++ b/src/plugins/tracenode/test.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023 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 <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+#include <vnet/api_errno.h>
+#include <stdbool.h>
+
+#define __plugin_msg_base tracenode_test_main.msg_id_base
+#include <vlibapi/vat_helper_macros.h>
+
+/* Declare message IDs */
+#include <tracenode/tracenode.api_enum.h>
+#include <tracenode/tracenode.api_types.h>
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+ vat_main_t *vat_main;
+} tracenode_test_main_t;
+
+tracenode_test_main_t tracenode_test_main;
+
+int
+api_tracenode_enable_disable (vat_main_t *vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_tracenode_enable_disable_t *mp;
+ u32 sw_if_index;
+ bool is_pcap, enable;
+
+ sw_if_index = ~0;
+ is_pcap = false;
+ enable = true;
+
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "disable"))
+ enable = 0;
+ else if (unformat (i, "pcap"))
+ is_pcap = 1;
+ else if (unformat (i, "%U", unformat_vnet_sw_interface, vnet_get_main (),
+ &sw_if_index))
+ {
+ if (sw_if_index == 0)
+ {
+ clib_warning ("Local interface not supported...");
+ return -99;
+ }
+ }
+
+ else
+ {
+ clib_warning ("Unknown input: %U\n", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ M (TRACENODE_ENABLE_DISABLE, mp);
+ mp->sw_if_index = htonl (sw_if_index);
+ mp->is_pcap = is_pcap;
+ mp->enable = enable;
+
+ int ret = 0;
+ S (mp);
+ W (ret);
+
+ return ret;
+}
+
+#include <tracenode/tracenode.api_test.c>
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/tracenode/tracenode.api b/src/plugins/tracenode/tracenode.api
new file mode 100644
index 00000000000..198f8218b55
--- /dev/null
+++ b/src/plugins/tracenode/tracenode.api
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+option version = "0.1.0";
+
+import "vnet/interface_types.api";
+
+/** \brief Enable/disable trace filtering feature
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - interface on which to enable/disable trace filtering feature
+ @param is_pcap - if non-zero enable the feature for pcap capture, else for trace
+ @param enable - if non-zero then enable the feature, else disable it
+*/
+autoreply define tracenode_enable_disable
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+ bool is_pcap [default=false];
+ bool enable [default=true];
+
+ option vat_help = "tracenode_enable_disable <intfc> [disable] [pcap]";
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/tracenode/tracenode.c b/src/plugins/tracenode/tracenode.c
new file mode 100644
index 00000000000..e292c7da95c
--- /dev/null
+++ b/src/plugins/tracenode/tracenode.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023 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 <tracenode/tracenode.h>
+
+tracenode_main_t tracenode_main;
+
+int
+tracenode_feature_enable_disable (u32 sw_if_index, bool is_pcap, bool enable)
+{
+ tracenode_main_t *tnm = &tracenode_main;
+ char *node_name = is_pcap ? "pcap-filtering" : "trace-filtering";
+ int rv = 0;
+
+ if (pool_is_free_index (tnm->vnet_main->interface_main.sw_interfaces,
+ sw_if_index))
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ if (clib_bitmap_get (tnm->feature_enabled_by_sw_if, sw_if_index) == enable)
+ return 0;
+
+ if ((rv = vnet_feature_enable_disable ("ip4-unicast", node_name, sw_if_index,
+ enable, 0, 0)) != 0)
+ return rv;
+
+ if ((rv = vnet_feature_enable_disable ("ip6-unicast", node_name, sw_if_index,
+ enable, 0, 0)) != 0)
+ return rv;
+
+ tnm->feature_enabled_by_sw_if =
+ clib_bitmap_set (tnm->feature_enabled_by_sw_if, sw_if_index, enable);
+
+ return 0;
+}
+
+static clib_error_t *
+tracenode_init (vlib_main_t *vm)
+{
+ tracenode_main_t *tnm = &tracenode_main;
+ clib_error_t *error = 0;
+
+ memset (tnm, 0, sizeof (*tnm));
+
+ tnm->vnet_main = vnet_get_main ();
+
+ error = tracenode_plugin_api_hookup (vm);
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (tracenode_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/tracenode/tracenode.h b/src/plugins/tracenode/tracenode.h
new file mode 100644
index 00000000000..7af60aa20b1
--- /dev/null
+++ b/src/plugins/tracenode/tracenode.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+#ifndef _TRACENODE_H_
+#define _TRACENODE_H_
+#include <vlib/vlib.h>
+#include <vnet/feature/feature.h>
+#include <stdbool.h>
+
+typedef struct
+{
+ vnet_main_t *vnet_main;
+ uword *feature_enabled_by_sw_if;
+ u16 msg_id_base;
+} tracenode_main_t;
+
+extern tracenode_main_t tracenode_main;
+
+clib_error_t *tracenode_plugin_api_hookup (vlib_main_t *vm);
+
+int tracenode_feature_enable_disable (u32 sw_if_index, bool is_pcap,
+ bool enable);
+
+#endif /* _TRACENODE_H_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/test/test_trace_filter.py b/test/test_trace_filter.py
index 3c1f778a0f7..b716b794f96 100644
--- a/test/test_trace_filter.py
+++ b/test/test_trace_filter.py
@@ -1,32 +1,36 @@
#!/usr/bin/env python3
import unittest
+import secrets
+import socket
from framework import VppTestCase, VppTestRunner
-from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
+from vpp_ipip_tun_interface import VppIpIpTunInterface
+from vpp_papi import VppEnum
+from vpp_ipsec import VppIpsecSA, VppIpsecSpd, VppIpsecSpdItfBinding, VppIpsecSpdEntry
+from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
from scapy.contrib.geneve import GENEVE
from scapy.packet import Raw
from scapy.layers.l2 import Ether
-from scapy.layers.inet import IP, UDP
+from scapy.layers.inet import IP, UDP, TCP
from scapy.layers.vxlan import VXLAN
+from scapy.layers.ipsec import ESP, SecurityAssociation
from scapy.compat import raw
from scapy.utils import rdpcap
-class TestTracefilter(VppTestCase):
- """Packet Tracer Filter Test"""
-
+class TemplateTraceFilter(VppTestCase):
@classmethod
def setUpClass(cls):
- super(TestTracefilter, cls).setUpClass()
+ super().setUpClass()
@classmethod
def tearDownClass(cls):
- super(TestTracefilter, cls).tearDownClass()
+ super().tearDownClass()
def setUp(self):
- super(TestTracefilter, self).setUp()
+ super().setUp()
self.create_pg_interfaces(range(2))
self.pg0.generate_remote_hosts(11)
for i in self.pg_interfaces:
@@ -35,7 +39,7 @@ class TestTracefilter(VppTestCase):
i.resolve_arp()
def tearDown(self):
- super(TestTracefilter, self).tearDown()
+ super().tearDown()
for i in self.pg_interfaces:
i.unconfig()
i.admin_down()
@@ -56,9 +60,12 @@ class TestTracefilter(VppTestCase):
r = self.cli("show classify table verbose")
self.assertTrue(r.reply.find("hits %i" % n) != -1)
+ def clear(self):
+ self.cli("clear trace")
+
def add_trace_filter(self, mask, match):
self.cli("classify filter trace mask %s match %s" % (mask, match))
- self.cli("clear trace")
+ self.clear()
self.cli("trace add pg-input 1000 filter")
def del_trace_filters(self):
@@ -73,6 +80,17 @@ class TestTracefilter(VppTestCase):
s = "pcap rx/tx/drop: first table none"
self.assertTrue(r.reply.find(s) != -1)
+ # install a classify rule, inject traffic and check for hits
+ def assert_classify(self, mask, match, packets, n=None):
+ 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_trace_filters()
+
+
+class TestTracefilter(TemplateTraceFilter):
+ """Packet Tracer Filter Test"""
+
def test_basic(self):
"""Packet Tracer Filter Test"""
self.add_trace_filter(
@@ -111,13 +129,6 @@ class TestTracefilter(VppTestCase):
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_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_trace_filters()
-
def test_encap(self):
"""Packet Tracer Filter Test with encap"""
@@ -281,5 +292,176 @@ class TestTracefilter(VppTestCase):
self.assertEqual(len(pcap), 17)
+class TestTraceFilterInner(TemplateTraceFilter):
+ """Packet Tracer Filter Inner Test"""
+
+ extra_vpp_plugin_config = [
+ "plugin tracenode_plugin.so {enable}",
+ ]
+
+ def add_trace_filter(self, mask, match, tn_feature_intfc_index=None):
+ if tn_feature_intfc_index is not None:
+ self.logger.info("fffff")
+ self.vapi.tracenode_enable_disable(sw_if_index=tn_feature_intfc_index)
+ super().add_trace_filter(mask, match)
+
+ def del_trace_filters(self, tn_feature_intfc_index=None):
+ if tn_feature_intfc_index is not None:
+ self.vapi.tracenode_enable_disable(
+ sw_if_index=tn_feature_intfc_index, enable=False
+ )
+ super().del_trace_filters()
+
+ def __add_sa(self, id_, tun_src, tun_dst):
+ # AES-CTR-128 / SHA2-256
+ crypto_key_length = 16
+ salt_length = 4
+ integ_key_lenght = 16
+ crypto_key = secrets.token_bytes(crypto_key_length)
+ salt = secrets.randbits(salt_length * 8)
+ integ_key = secrets.token_bytes(integ_key_lenght)
+
+ flags = VppEnum.vl_api_ipsec_sad_flags_t.IPSEC_API_SAD_FLAG_UDP_ENCAP
+
+ vpp_sa_in = VppIpsecSA(
+ test=self,
+ id=id_,
+ spi=id_,
+ integ_alg=VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_SHA_256_128,
+ integ_key=integ_key,
+ crypto_alg=VppEnum.vl_api_ipsec_crypto_alg_t.IPSEC_API_CRYPTO_ALG_AES_CTR_128,
+ crypto_key=crypto_key,
+ proto=VppEnum.vl_api_ipsec_proto_t.IPSEC_API_PROTO_ESP,
+ flags=flags,
+ salt=salt,
+ tun_src=tun_src,
+ tun_dst=tun_dst,
+ udp_src=4500,
+ udp_dst=4500,
+ )
+ vpp_sa_in.add_vpp_config()
+
+ scapy_sa_in = SecurityAssociation(
+ ESP,
+ spi=id_,
+ crypt_algo="AES-CTR",
+ crypt_key=crypto_key + salt.to_bytes(salt_length, "big"),
+ auth_algo="SHA2-256-128",
+ auth_key=integ_key,
+ tunnel_header=IP(src=tun_src, dst=tun_dst),
+ nat_t_header=UDP(sport=4500, dport=4500),
+ )
+
+ id_ += 1
+
+ vpp_sa_out = VppIpsecSA(
+ test=self,
+ id=id_,
+ spi=id_,
+ integ_alg=VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_SHA_256_128,
+ integ_key=integ_key,
+ crypto_alg=VppEnum.vl_api_ipsec_crypto_alg_t.IPSEC_API_CRYPTO_ALG_AES_CTR_128,
+ crypto_key=crypto_key,
+ proto=VppEnum.vl_api_ipsec_proto_t.IPSEC_API_PROTO_ESP,
+ flags=flags,
+ salt=salt,
+ tun_src=tun_dst,
+ tun_dst=tun_src,
+ udp_src=4500,
+ udp_dst=4500,
+ )
+ vpp_sa_out.add_vpp_config()
+
+ scapy_sa_out = SecurityAssociation(
+ ESP,
+ spi=id_,
+ crypt_algo="AES-CTR",
+ crypt_key=crypto_key + salt.to_bytes(salt_length, "big"),
+ auth_algo="SHA2-256-128",
+ auth_key=integ_key,
+ tunnel_header=IP(src=tun_dst, dst=tun_src),
+ nat_t_header=UDP(sport=4500, dport=4500),
+ )
+
+ return vpp_sa_in, scapy_sa_in, vpp_sa_out, scapy_sa_out
+
+ def __gen_encrypt_pkt(self, scapy_sa, pkt):
+ return Ether(
+ src=self.pg0.local_mac, dst=self.pg0.remote_mac
+ ) / scapy_sa.encrypt(pkt)
+
+ def test_encrypted_encap(self):
+ """Packet Tracer Filter Test with encrypted encap"""
+
+ vpp_sa_in, scapy_sa_in, vpp_sa_out, _ = self.__add_sa(
+ 1, self.pg0.local_ip4, self.pg0.remote_ip4
+ )
+
+ spd = VppIpsecSpd(self, 1)
+ spd.add_vpp_config()
+
+ spd_binding = VppIpsecSpdItfBinding(self, spd, self.pg0)
+ spd_binding.add_vpp_config()
+
+ spd_entry = VppIpsecSpdEntry(
+ self,
+ spd,
+ 1,
+ self.pg0.local_ip4,
+ self.pg0.local_ip4,
+ self.pg0.remote_ip4,
+ self.pg0.remote_ip4,
+ socket.IPPROTO_ESP,
+ policy=VppEnum.vl_api_ipsec_spd_action_t.IPSEC_API_SPD_ACTION_PROTECT,
+ is_outbound=0,
+ ).add_vpp_config()
+
+ # the inner packet we are trying to match
+ inner_pkt = (
+ IP(src=self.pg1.local_ip4, dst=self.pg1.remote_ip4)
+ / TCP(sport=1234, dport=4321)
+ / Raw(b"\xa5" * 100)
+ )
+ pkt = self.__gen_encrypt_pkt(scapy_sa_in, inner_pkt)
+
+ # self.add_trace_filter("l3 ip4 src", f"l3 ip4 src {self.pg0.local_ip4}")
+
+ self.add_trace_filter(
+ "l2 none l3 ip4 src proto l4 dst_port",
+ f"l2 none l3 ip4 src {self.pg1.local_ip4} proto 6 l4 dst_port 4321",
+ tn_feature_intfc_index=self.pg0.sw_if_index,
+ )
+
+ self.logger.info("Sending packet with matching inner")
+ self.send_and_expect(self.pg0, pkt * 67, self.pg1, trace=False)
+ self.assert_hits(67)
+ self.clear()
+
+ self.logger.info("Sending packet with wrong inner port")
+ inner_pkt[TCP].dport = 1111
+ pkt = self.__gen_encrypt_pkt(scapy_sa_in, inner_pkt)
+ self.send_and_expect(self.pg0, pkt * 67, self.pg1, trace=False)
+ # the classify session should still have the 67 previous hits.
+ # In another way, the delta is 0
+ self.assert_hits(67)
+ self.clear()
+
+ self.logger.info("Sending packet with wrong source address")
+ inner_pkt[IP].src = "1.2.3.4"
+ inner_pkt[TCP].dport = 4321
+ pkt = self.__gen_encrypt_pkt(scapy_sa_in, inner_pkt)
+ self.send_and_expect(self.pg0, pkt * 67, self.pg1, trace=False)
+ self.assert_hits(67)
+ self.clear()
+
+ self.del_trace_filters(tn_feature_intfc_index=self.pg0.sw_if_index)
+
+ spd_entry.remove_vpp_config()
+ spd_binding.remove_vpp_config()
+ spd.remove_vpp_config()
+ vpp_sa_in.remove_vpp_config()
+ vpp_sa_out.remove_vpp_config()
+
+
if __name__ == "__main__":
unittest.main(testRunner=VppTestRunner)