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>
This commit is contained in:
Maxime Peim
2023-07-11 09:45:56 +02:00
committed by Beno�t Ganne
parent 2ceb818f8e
commit 77812045e7
13 changed files with 812 additions and 17 deletions

View 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>

View File

@@ -1149,6 +1149,8 @@ tpv
TPv
tracedump
Tracedump
tracenode
Tracenode
TRex
Tsou
ttl

View File

@@ -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
)

View File

@@ -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]

View File

@@ -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:
*/

View File

@@ -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:
*/

View File

@@ -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:
*/

View File

@@ -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:
*/

View File

@@ -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:
*/

View File

@@ -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:
*/

View File

@@ -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:
*/

View File

@@ -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:
*/

View 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)