tracenode: filtering feature 13/39213/15
authorMaxime Peim <mpeim@cisco.com>
Tue, 11 Jul 2023 07:45:56 +0000 (09:45 +0200)
committerBeno�t Ganne <bganne@cisco.com>
Mon, 4 Sep 2023 07:57:44 +0000 (07:57 +0000)
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>
13 files changed:
MAINTAINERS
docs/spelling_wordlist.txt
src/plugins/tracenode/CMakeLists.txt [new file with mode: 0644]
src/plugins/tracenode/FEATURE.yaml [new file with mode: 0644]
src/plugins/tracenode/api.c [new file with mode: 0644]
src/plugins/tracenode/cli.c [new file with mode: 0644]
src/plugins/tracenode/node.c [new file with mode: 0644]
src/plugins/tracenode/plugin.c [new file with mode: 0644]
src/plugins/tracenode/test.c [new file with mode: 0644]
src/plugins/tracenode/tracenode.api [new file with mode: 0644]
src/plugins/tracenode/tracenode.c [new file with mode: 0644]
src/plugins/tracenode/tracenode.h [new file with mode: 0644]
test/test_trace_filter.py

index cb09e30..167da0f 100644 (file)
@@ -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>
index 4116d1d..6a06b3b 100644 (file)
@@ -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 (file)
index 0000000..6b6ba2e
--- /dev/null
@@ -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 (file)
index 0000000..c405dd1
--- /dev/null
@@ -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 (file)
index 0000000..0b01ad8
--- /dev/null
@@ -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 (file)
index 0000000..8d0ed41
--- /dev/null
@@ -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 (file)
index 0000000..444d93f
--- /dev/null
@@ -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 (file)
index 0000000..19ce6ba
--- /dev/null
@@ -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 (file)
index 0000000..a409fd2
--- /dev/null
@@ -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 (file)
index 0000000..198f821
--- /dev/null
@@ -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 (file)
index 0000000..e292c7d
--- /dev/null
@@ -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 (file)
index 0000000..7af60aa
--- /dev/null
@@ -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:
+ */
index 3c1f778..b716b79 100644 (file)
@@ -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)