aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorMathias Raoul <mathias.raoul@gmail.com>2019-07-12 19:11:49 +0200
committerMathias Raoul <mathias.raoul@gmail.com>2019-07-15 23:29:21 +0200
commit9082b43dd06fcd8888eebd95857ef2fb8177d475 (patch)
treecb5e25ca9f2d686c3dc02c0370203842896f46a0 /src/plugins
parent4b0b0d4b53cec5eeb0e12fd7c4ce55cc4052ad0c (diff)
quic: integrate vpp crypto api for quic packets encryption
Type: feature Change-Id: I740f15a5ef959d31e94e59d652aa9f691db1f289 Signed-off-by: Mathias Raoul <mathias.raoul@gmail.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/quic/CMakeLists.txt1
-rw-r--r--src/plugins/quic/quic.c35
-rw-r--r--src/plugins/quic/quic_crypto.c336
-rw-r--r--src/plugins/quic/quic_crypto.h31
4 files changed, 403 insertions, 0 deletions
diff --git a/src/plugins/quic/CMakeLists.txt b/src/plugins/quic/CMakeLists.txt
index ccd49682e8c..70a1243b3b3 100644
--- a/src/plugins/quic/CMakeLists.txt
+++ b/src/plugins/quic/CMakeLists.txt
@@ -31,6 +31,7 @@ if(QUICLY_INCLUDE_DIR AND QUIC_LINK_LIBRARIES)
add_vpp_plugin(quic
SOURCES
quic.c
+ quic_crypto.c
INSTALL_HEADERS
quic.h
diff --git a/src/plugins/quic/quic.c b/src/plugins/quic/quic.c
index b46cdb3f018..6c1a3c37bd5 100644
--- a/src/plugins/quic/quic.c
+++ b/src/plugins/quic/quic.c
@@ -30,6 +30,8 @@
#include <picotls/openssl.h>
#include <picotls/pembase64.h>
+#include <quic/quic_crypto.h>
+
static quic_main_t quic_main;
static void quic_update_timer (quic_ctx_t * ctx);
@@ -2539,7 +2541,40 @@ quic_init (vlib_main_t * vm)
VLIB_INIT_FUNCTION (quic_init);
+static clib_error_t *
+quic_plugin_crypto_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "vpp"))
+ {
+ quic_tlsctx.cipher_suites = vpp_crypto_cipher_suites;
+ return 0;
+ }
+ else if (unformat (input, "picotls"))
+ {
+ quic_tlsctx.cipher_suites = ptls_openssl_cipher_suites;
+ return 0;
+ }
+ else
+ return clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, input);
+ }
+
+ return clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, input);
+}
+
/* *INDENT-OFF* */
+VLIB_CLI_COMMAND(quic_plugin_crypto_command, static)=
+{
+ .path = "quic set crypto api",
+ .short_help = "quic set crypto api [picotls, vpp]",
+ .function = quic_plugin_crypto_command_fn,
+};
+
VLIB_PLUGIN_REGISTER () =
{
.version = VPP_BUILD_VER,
diff --git a/src/plugins/quic/quic_crypto.c b/src/plugins/quic/quic_crypto.c
new file mode 100644
index 00000000000..6c198960c24
--- /dev/null
+++ b/src/plugins/quic/quic_crypto.c
@@ -0,0 +1,336 @@
+/*
+ * 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 <quic/quic_crypto.h>
+#include <quic/quic.h>
+
+#include <vnet/crypto/crypto.h>
+
+#include <picotls/openssl.h>
+#include <quicly.h>
+
+typedef void (*quicly_do_transform_fn) (ptls_cipher_context_t *, void *,
+ const void *, size_t);
+
+struct cipher_context_t
+{
+ ptls_cipher_context_t super;
+ vnet_crypto_op_t op;
+ u32 key_index;
+};
+
+struct aead_crypto_context_t
+{
+ ptls_aead_context_t super;
+ vnet_crypto_op_t op;
+ u32 key_index;
+};
+
+vnet_crypto_main_t *cm = &crypto_main;
+
+static void
+vpp_crypto_cipher_do_init (ptls_cipher_context_t * _ctx, const void *iv)
+{
+ struct cipher_context_t *ctx = (struct cipher_context_t *) _ctx;
+
+ vnet_crypto_op_id_t id;
+ if (!strcmp (ctx->super.algo->name, "AES128-CTR"))
+ {
+ id = VNET_CRYPTO_OP_AES_128_CTR_ENC;
+ }
+ else if (!strcmp (ctx->super.algo->name, "AES256-CTR"))
+ {
+ id = VNET_CRYPTO_OP_AES_256_CTR_ENC;
+ }
+ else
+ {
+ QUIC_DBG (1, "%s, Invalid crypto cipher : ", __FUNCTION__,
+ _ctx->algo->name);
+ assert (0);
+ }
+
+ vnet_crypto_op_init (&ctx->op, id);
+ ctx->op.iv = (u8 *) iv;
+ ctx->op.key_index = ctx->key_index;
+}
+
+static void
+vpp_crypto_cipher_dispose (ptls_cipher_context_t * _ctx)
+{
+ /* Do nothing */
+}
+
+static void
+vpp_crypto_cipher_encrypt (ptls_cipher_context_t * _ctx, void *output,
+ const void *input, size_t _len)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ struct cipher_context_t *ctx = (struct cipher_context_t *) _ctx;
+
+ ctx->op.src = (u8 *) input;
+ ctx->op.dst = output;
+ ctx->op.len = _len;
+
+ vnet_crypto_process_ops (vm, &ctx->op, 1);
+}
+
+static int
+vpp_crypto_cipher_setup_crypto (ptls_cipher_context_t * _ctx, int is_enc,
+ const void *key, const EVP_CIPHER * cipher,
+ quicly_do_transform_fn do_transform)
+{
+ struct cipher_context_t *ctx = (struct cipher_context_t *) _ctx;
+
+ ctx->super.do_dispose = vpp_crypto_cipher_dispose;
+ ctx->super.do_init = vpp_crypto_cipher_do_init;
+ ctx->super.do_transform = do_transform;
+
+ vlib_main_t *vm = vlib_get_main ();
+ vnet_crypto_alg_t algo;
+ if (!strcmp (ctx->super.algo->name, "AES128-CTR"))
+ {
+ algo = VNET_CRYPTO_ALG_AES_128_CTR;
+ }
+ else if (!strcmp (ctx->super.algo->name, "AES256-CTR"))
+ {
+ algo = VNET_CRYPTO_ALG_AES_256_CTR;
+ }
+ else
+ {
+ QUIC_DBG (1, "%s, Invalid crypto cipher : ", __FUNCTION__,
+ _ctx->algo->name);
+ assert (0);
+ }
+
+ ctx->key_index = vnet_crypto_key_add (vm, algo,
+ (u8 *) key, _ctx->algo->key_size);
+
+ return 0;
+}
+
+static int
+aes128ctr_setup_crypto (ptls_cipher_context_t * ctx, int is_enc,
+ const void *key)
+{
+ return vpp_crypto_cipher_setup_crypto (ctx, 1, key, EVP_aes_128_ctr (),
+ vpp_crypto_cipher_encrypt);
+}
+
+static int
+aes256ctr_setup_crypto (ptls_cipher_context_t * ctx, int is_enc,
+ const void *key)
+{
+ return vpp_crypto_cipher_setup_crypto (ctx, 1, key, EVP_aes_256_ctr (),
+ vpp_crypto_cipher_encrypt);
+}
+
+size_t
+vpp_crypto_aead_encrypt (ptls_aead_context_t * _ctx, void *output,
+ const void *input, size_t inlen, uint64_t seq,
+ const void *iv, const void *aad, size_t aadlen)
+{
+ QUIC_DBG (1, "[quic] %s", __FUNCTION__);
+
+ vlib_main_t *vm = vlib_get_main ();
+ struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *) _ctx;
+
+ vnet_crypto_op_id_t id;
+ if (!strcmp (ctx->super.algo->name, "AES128-GCM"))
+ {
+ id = VNET_CRYPTO_OP_AES_128_GCM_ENC;
+ }
+ else if (!strcmp (ctx->super.algo->name, "AES256-GCM"))
+ {
+ id = VNET_CRYPTO_OP_AES_256_GCM_ENC;
+ }
+ else
+ {
+ assert (0);
+ }
+
+ vnet_crypto_op_init (&ctx->op, id);
+ ctx->op.aad = (u8 *) aad;
+ ctx->op.aad_len = aadlen;
+ ctx->op.iv = (u8 *) iv;
+
+ ctx->op.src = (u8 *) input;
+ ctx->op.dst = output;
+ ctx->op.key_index = ctx->key_index;
+ ctx->op.len = inlen;
+
+ ctx->op.tag_len = ctx->super.algo->tag_size;
+ ctx->op.tag = ctx->op.src + inlen;
+
+ vnet_crypto_process_ops (vm, &ctx->op, 1);
+
+ return ctx->op.len + ctx->op.tag_len;
+}
+
+size_t
+vpp_crypto_aead_decrypt (ptls_aead_context_t * _ctx, void *_output,
+ const void *input, size_t inlen, const void *iv,
+ const void *aad, size_t aadlen)
+{
+ QUIC_DBG (1, "[quic] %s", __FUNCTION__);
+
+ vlib_main_t *vm = vlib_get_main ();
+ struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *) _ctx;
+
+ vnet_crypto_op_id_t id;
+ if (!strcmp (ctx->super.algo->name, "AES128-GCM"))
+ {
+ id = VNET_CRYPTO_OP_AES_128_GCM_DEC;
+ }
+ else if (!strcmp (ctx->super.algo->name, "AES256-GCM"))
+ {
+ id = VNET_CRYPTO_OP_AES_256_GCM_DEC;
+ }
+ else
+ {
+ assert (0);
+ }
+
+ vnet_crypto_op_init (&ctx->op, id);
+ ctx->op.aad = (u8 *) aad;
+ ctx->op.aad_len = aadlen;
+ ctx->op.iv = (u8 *) iv;
+
+ ctx->op.src = (u8 *) input;
+ ctx->op.dst = _output;
+ ctx->op.key_index = ctx->key_index;
+ ctx->op.len = inlen - ctx->super.algo->tag_size;
+
+ ctx->op.tag_len = ctx->super.algo->tag_size;
+ ctx->op.tag = ctx->op.src + ctx->op.len;
+
+ vnet_crypto_process_ops (vm, &ctx->op, 1);
+
+ return ctx->op.len;
+}
+
+static void
+vpp_crypto_aead_dispose_crypto (ptls_aead_context_t * _ctx)
+{
+ QUIC_DBG (1, "[quic] %s", __FUNCTION__);
+}
+
+static int
+vpp_crypto_aead_setup_crypto (ptls_aead_context_t * _ctx, int is_enc,
+ const void *key, const EVP_CIPHER * cipher)
+{
+ QUIC_DBG (1, "%s, algo : ", __FUNCTION__, _ctx->algo->name);
+
+ vlib_main_t *vm = vlib_get_main ();
+ struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *) _ctx;
+
+ vnet_crypto_alg_t algo;
+ if (!strcmp (ctx->super.algo->name, "AES128-GCM"))
+ {
+ algo = VNET_CRYPTO_ALG_AES_128_GCM;
+ }
+ else if (!strcmp (ctx->super.algo->name, "AES256-GCM"))
+ {
+ algo = VNET_CRYPTO_ALG_AES_256_GCM;
+ }
+ else
+ {
+ QUIC_DBG (1, "%s, algo : ", __FUNCTION__, _ctx->algo->name);
+ assert (0);
+ }
+
+ ctx->super.do_decrypt = vpp_crypto_aead_decrypt;
+ ctx->super.do_encrypt = vpp_crypto_aead_encrypt;
+ ctx->super.dispose_crypto = vpp_crypto_aead_dispose_crypto;
+
+ ctx->key_index = vnet_crypto_key_add (vm, algo,
+ (u8 *) key, _ctx->algo->key_size);
+
+ return 0;
+}
+
+static int
+vpp_crypto_aead_aes128gcm_setup_crypto (ptls_aead_context_t * ctx, int is_enc,
+ const void *key)
+{
+ return vpp_crypto_aead_setup_crypto (ctx, is_enc, key, EVP_aes_128_gcm ());
+}
+
+static int
+vpp_crypto_aead_aes256gcm_setup_crypto (ptls_aead_context_t * ctx, int is_enc,
+ const void *key)
+{
+ return vpp_crypto_aead_setup_crypto (ctx, is_enc, key, EVP_aes_256_gcm ());
+}
+
+ptls_cipher_algorithm_t vpp_crypto_aes128ctr = { "AES128-CTR",
+ PTLS_AES128_KEY_SIZE,
+ 1, PTLS_AES_IV_SIZE,
+ sizeof (struct cipher_context_t),
+ aes128ctr_setup_crypto
+};
+
+ptls_cipher_algorithm_t vpp_crypto_aes256ctr = { "AES256-CTR",
+ PTLS_AES256_KEY_SIZE,
+ 1 /* block size */ ,
+ PTLS_AES_IV_SIZE,
+ sizeof (struct cipher_context_t),
+ aes256ctr_setup_crypto
+};
+
+ptls_aead_algorithm_t vpp_crypto_aes128gcm = { "AES128-GCM",
+ &vpp_crypto_aes128ctr,
+ NULL,
+ PTLS_AES128_KEY_SIZE,
+ PTLS_AESGCM_IV_SIZE,
+ PTLS_AESGCM_TAG_SIZE,
+ sizeof (struct aead_crypto_context_t),
+ vpp_crypto_aead_aes128gcm_setup_crypto
+};
+
+ptls_aead_algorithm_t vpp_crypto_aes256gcm = { "AES256-GCM",
+ &vpp_crypto_aes256ctr,
+ NULL,
+ PTLS_AES256_KEY_SIZE,
+ PTLS_AESGCM_IV_SIZE,
+ PTLS_AESGCM_TAG_SIZE,
+ sizeof (struct aead_crypto_context_t),
+ vpp_crypto_aead_aes256gcm_setup_crypto
+};
+
+ptls_cipher_suite_t vpp_crypto_aes128gcmsha256 =
+ { PTLS_CIPHER_SUITE_AES_128_GCM_SHA256,
+ &vpp_crypto_aes128gcm,
+ &ptls_openssl_sha256
+};
+
+ptls_cipher_suite_t vpp_crypto_aes256gcmsha384 =
+ { PTLS_CIPHER_SUITE_AES_256_GCM_SHA384,
+ &vpp_crypto_aes256gcm,
+ &ptls_openssl_sha384
+};
+
+ptls_cipher_suite_t *vpp_crypto_cipher_suites[] =
+ { &vpp_crypto_aes256gcmsha384,
+ &vpp_crypto_aes128gcmsha256,
+ NULL
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/quic/quic_crypto.h b/src/plugins/quic/quic_crypto.h
new file mode 100644
index 00000000000..8e4bbf92834
--- /dev/null
+++ b/src/plugins/quic/quic_crypto.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef __included_vpp_quic_crypto_h__
+#define __included_vpp_quic_crypto_h__
+
+#include <quicly.h>
+
+extern ptls_cipher_suite_t *vpp_crypto_cipher_suites[];
+
+#endif /* __included_vpp_quic_crypto_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
ss="n">TestClassifier): """Classifier-based L2 input and output ACL Test Case""" # traffic types IP = 0 ICMP = 1 # IP version IPRANDOM = -1 IPV4 = 0 IPV6 = 1 # rule types DENY = 0 PERMIT = 1 # supported protocols proto = [[6, 17], [1, 58]] proto_map = {1: "ICMP", 58: "ICMPv6EchoRequest", 6: "TCP", 17: "UDP"} ICMPv4 = 0 ICMPv6 = 1 TCP = 0 UDP = 1 PROTO_ALL = 0 # port ranges PORTS_ALL = -1 PORTS_RANGE = 0 PORTS_RANGE_2 = 1 udp_sport_from = 10 udp_sport_to = udp_sport_from + 5 udp_dport_from = 20000 udp_dport_to = udp_dport_from + 5000 tcp_sport_from = 30 tcp_sport_to = tcp_sport_from + 5 tcp_dport_from = 40000 tcp_dport_to = tcp_dport_from + 5000 udp_sport_from_2 = 90 udp_sport_to_2 = udp_sport_from_2 + 5 udp_dport_from_2 = 30000 udp_dport_to_2 = udp_dport_from_2 + 5000 tcp_sport_from_2 = 130 tcp_sport_to_2 = tcp_sport_from_2 + 5 tcp_dport_from_2 = 20000 tcp_dport_to_2 = tcp_dport_from_2 + 5000 icmp4_type = 8 # echo request icmp4_code = 3 icmp6_type = 128 # echo request icmp6_code = 3 icmp4_type_2 = 8 icmp4_code_from_2 = 5 icmp4_code_to_2 = 20 icmp6_type_2 = 128 icmp6_code_from_2 = 8 icmp6_code_to_2 = 42 # Test variables bd_id = 1 @classmethod def setUpClass(cls): """ Perform standard class setup (defined by class method setUpClass in class VppTestCase) before running the test case, set test case related variables and configure VPP. """ super(TestClassifyAcl, cls).setUpClass() cls.af = None try: # Create 2 pg interfaces cls.create_pg_interfaces(range(2)) # Packet flows mapping pg0 -> pg1, pg2 etc. cls.flows = dict() cls.flows[cls.pg0] = [cls.pg1] # Packet sizes cls.pg_if_packet_sizes = [64, 512, 1518, 9018] # Create BD with MAC learning and unknown unicast flooding disabled # and put interfaces to this BD cls.vapi.bridge_domain_add_del_v2( bd_id=cls.bd_id, uu_flood=1, learn=1, flood=1, forward=1, is_add=1 ) for pg_if in cls.pg_interfaces: cls.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=pg_if.sw_if_index, bd_id=cls.bd_id ) # Set up all interfaces for i in cls.pg_interfaces: i.admin_up() # Mapping between packet-generator index and lists of test hosts cls.hosts_by_pg_idx = dict() for pg_if in cls.pg_interfaces: cls.hosts_by_pg_idx[pg_if.sw_if_index] = [] # Create list of deleted hosts cls.deleted_hosts_by_pg_idx = dict() for pg_if in cls.pg_interfaces: cls.deleted_hosts_by_pg_idx[pg_if.sw_if_index] = [] # warm-up the mac address tables # self.warmup_test() # Holder of the active classify table key cls.acl_active_table = "" except Exception: super(TestClassifyAcl, cls).tearDownClass() raise @classmethod def tearDownClass(cls): super(TestClassifyAcl, cls).tearDownClass() def setUp(self): super(TestClassifyAcl, self).setUp() self.acl_tbl_idx = {} def tearDown(self): """ Show various debug prints after each test. """ if not self.vpp_dead: if self.acl_active_table == "mac_inout": self.output_acl_set_interface( self.pg1, self.acl_tbl_idx.get(self.acl_active_table), 0 ) self.input_acl_set_interface( self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0 ) self.acl_active_table = "" elif self.acl_active_table == "mac_out": self.output_acl_set_interface( self.pg1, self.acl_tbl_idx.get(self.acl_active_table), 0 ) self.acl_active_table = "" elif self.acl_active_table == "mac_in": self.input_acl_set_interface( self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0 ) self.acl_active_table = "" super(TestClassifyAcl, self).tearDown() def create_classify_session( self, intf, table_index, match, hit_next_index=0xFFFFFFFF, is_add=1 ): """Create Classify Session :param VppInterface intf: Interface to apply classify session. :param int table_index: table index to identify classify table. :param str match: matched value for interested traffic. :param int is_add: option to configure classify session. - create(1) or delete(0) """ mask_match, mask_match_len = self._resolve_mask_match(match) r = self.vapi.classify_add_del_session( is_add=is_add, table_index=table_index, match=mask_match, match_len=mask_match_len, hit_next_index=hit_next_index, ) self.assertIsNotNone(r, "No response msg for add_del_session") def create_hosts(self, count, start=0): """ Create required number of host MAC addresses and distribute them among interfaces. Create host IPv4 address for every host MAC address. :param int count: Number of hosts to create MAC/IPv4 addresses for. :param int start: Number to start numbering from. """ n_int = len(self.pg_interfaces) macs_per_if = count // n_int i = -1 for pg_if in self.pg_interfaces: i += 1 start_nr = macs_per_if * i + start end_nr = ( count + start if i == (n_int - 1) else macs_per_if * (i + 1) + start ) hosts = self.hosts_by_pg_idx[pg_if.sw_if_index] for j in range(start_nr, end_nr): host = Host( "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j), "172.17.1%02x.%u" % (pg_if.sw_if_index, j), "2017:dead:%02x::%u" % (pg_if.sw_if_index, j), ) hosts.append(host) def create_upper_layer(self, packet_index, proto, ports=0): p = self.proto_map[proto] if p == "UDP": if ports == 0: return UDP( sport=random.randint(self.udp_sport_from, self.udp_sport_to), dport=random.randint(self.udp_dport_from, self.udp_dport_to), ) else: return UDP(sport=ports, dport=ports) elif p == "TCP": if ports == 0: return TCP( sport=random.randint(self.tcp_sport_from, self.tcp_sport_to), dport=random.randint(self.tcp_dport_from, self.tcp_dport_to), ) else: return TCP(sport=ports, dport=ports) return "" def create_stream( self, src_if, packet_sizes, traffic_type=0, ipv6=0, proto=-1, ports=0, fragments=False, pkt_raw=True, etype=-1, ): """ Create input packet stream for defined interface using hosts or deleted_hosts list. :param object src_if: Interface to create packet stream for. :param list packet_sizes: List of required packet sizes. :param traffic_type: 1: ICMP packet, 2: IPv6 with EH, 0: otherwise. :return: Stream of packets. """ pkts = [] if self.flows.__contains__(src_if): src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index] for dst_if in self.flows[src_if]: dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index] n_int = len(dst_hosts) * len(src_hosts) for i in range(0, n_int): dst_host = dst_hosts[i // len(src_hosts)] src_host = src_hosts[i % len(src_hosts)] pkt_info = self.create_packet_info(src_if, dst_if) if ipv6 == 1: pkt_info.ip = 1 elif ipv6 == 0: pkt_info.ip = 0 else: pkt_info.ip = random.choice([0, 1]) if proto == -1: pkt_info.proto = random.choice(self.proto[self.IP]) else: pkt_info.proto = proto payload = self.info_to_payload(pkt_info) p = Ether(dst=dst_host.mac, src=src_host.mac) if etype > 0: p = Ether(dst=dst_host.mac, src=src_host.mac, type=etype) if pkt_info.ip: p /= IPv6(dst=dst_host.ip6, src=src_host.ip6) if fragments: p /= IPv6ExtHdrFragment(offset=64, m=1) else: if fragments: p /= IP( src=src_host.ip4, dst=dst_host.ip4, flags=1, frag=64 ) else: p /= IP(src=src_host.ip4, dst=dst_host.ip4) if traffic_type == self.ICMP: if pkt_info.ip: p /= ICMPv6EchoRequest( type=self.icmp6_type, code=self.icmp6_code ) else: p /= ICMP(type=self.icmp4_type, code=self.icmp4_code) else: p /= self.create_upper_layer(i, pkt_info.proto, ports) if pkt_raw: p /= Raw(payload) pkt_info.data = p.copy() if pkt_raw: size = random.choice(packet_sizes) self.extend_packet(p, size) pkts.append(p) return pkts def verify_capture(self, pg_if, capture, traffic_type=0, ip_type=0, etype=-1): """ Verify captured input packet stream for defined interface. :param object pg_if: Interface to verify captured packet stream for. :param list capture: Captured packet stream. :param traffic_type: 1: ICMP packet, 2: IPv6 with EH, 0: otherwise. """ last_info = dict() for i in self.pg_interfaces: last_info[i.sw_if_index] = None dst_sw_if_index = pg_if.sw_if_index for packet in capture: if etype > 0: if packet[Ether].type != etype: self.logger.error(ppp("Unexpected ethertype in packet:", packet)) else: continue try: # Raw data for ICMPv6 are stored in ICMPv6EchoRequest.data if traffic_type == self.ICMP and ip_type == self.IPV6: payload_info = self.payload_to_info(packet[ICMPv6EchoRequest].data) payload = packet[ICMPv6EchoRequest] else: payload_info = self.payload_to_info(packet[Raw]) payload = packet[self.proto_map[payload_info.proto]] except: self.logger.error( ppp("Unexpected or invalid packet (outside network):", packet) ) raise if ip_type != 0: self.assertEqual(payload_info.ip, ip_type) if traffic_type == self.ICMP: try: if payload_info.ip == 0: self.assertEqual(payload.type, self.icmp4_type) self.assertEqual(payload.code, self.icmp4_code) else: self.assertEqual(payload.type, self.icmp6_type) self.assertEqual(payload.code, self.icmp6_code) except: self.logger.error( ppp("Unexpected or invalid packet (outside network):", packet) ) raise else: try: ip_version = IPv6 if payload_info.ip == 1 else IP ip = packet[ip_version] packet_index = payload_info.index self.assertEqual(payload_info.dst, dst_sw_if_index) self.logger.debug( "Got packet on port %s: src=%u (id=%u)" % (pg_if.name, payload_info.src, packet_index) ) next_info = self.get_next_packet_info_for_interface2( payload_info.src, dst_sw_if_index, last_info[payload_info.src] ) last_info[payload_info.src] = next_info self.assertTrue(next_info is not None) self.assertEqual(packet_index, next_info.index) saved_packet = next_info.data # Check standard fields self.assertEqual(ip.src, saved_packet[ip_version].src) self.assertEqual(ip.dst, saved_packet[ip_version].dst) p = self.proto_map[payload_info.proto] if p == "TCP": tcp = packet[TCP] self.assertEqual(tcp.sport, saved_packet[TCP].sport) self.assertEqual(tcp.dport, saved_packet[TCP].dport) elif p == "UDP": udp = packet[UDP] self.assertEqual(udp.sport, saved_packet[UDP].sport) self.assertEqual(udp.dport, saved_packet[UDP].dport) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise for i in self.pg_interfaces: remaining_packet = self.get_next_packet_info_for_interface2( i, dst_sw_if_index, last_info[i.sw_if_index] ) self.assertTrue( remaining_packet is None, "Port %u: Packet expected from source %u didn't arrive" % (dst_sw_if_index, i.sw_if_index), ) def run_traffic_no_check(self): # Test # Create incoming packet streams for packet-generator interfaces for i in self.pg_interfaces: if self.flows.__contains__(i): pkts = self.create_stream(i, self.pg_if_packet_sizes) if len(pkts) > 0: i.add_stream(pkts) # Enable packet capture and start packet sending self.pg_enable_capture(self.pg_interfaces) self.pg_start() def run_verify_test( self, traffic_type=0, ip_type=0, proto=-1, ports=0, frags=False, pkt_raw=True, etype=-1, ): # Test # Create incoming packet streams for packet-generator interfaces pkts_cnt = 0 for i in self.pg_interfaces: if self.flows.__contains__(i): pkts = self.create_stream( i, self.pg_if_packet_sizes, traffic_type, ip_type, proto, ports, frags, pkt_raw, etype, ) if len(pkts) > 0: i.add_stream(pkts) pkts_cnt += len(pkts) # Enable packet capture and start packet sendingself.IPV self.pg_enable_capture(self.pg_interfaces) self.pg_start() # Verify # Verify outgoing packet streams per packet-generator interface for src_if in self.pg_interfaces: if self.flows.__contains__(src_if): for dst_if in self.flows[src_if]: capture = dst_if.get_capture(pkts_cnt) self.logger.info("Verifying capture on interface %s" % dst_if.name) self.verify_capture(dst_if, capture, traffic_type, ip_type, etype) def run_verify_negat_test( self, traffic_type=0, ip_type=0, proto=-1, ports=0, frags=False, etype=-1 ): # Test self.reset_packet_infos() for i in self.pg_interfaces: if self.flows.__contains__(i): pkts = self.create_stream( i, self.pg_if_packet_sizes, traffic_type, ip_type, proto, ports, frags, True, etype, ) if len(pkts) > 0: i.add_stream(pkts) # Enable packet capture and start packet sending self.pg_enable_capture(self.pg_interfaces) self.pg_start() # Verify # Verify outgoing packet streams per packet-generator interface for src_if in self.pg_interfaces: if self.flows.__contains__(src_if): for dst_if in self.flows[src_if]: self.logger.info("Verifying capture on interface %s" % dst_if.name) capture = dst_if.get_capture(0) self.assertEqual(len(capture), 0) def build_classify_table( self, src_mac="", dst_mac="", ether_type="", etype="", key="mac", hit_next_index=0xFFFFFFFF, ): # Basic ACL testing a_mask = self.build_mac_mask( src_mac=src_mac, dst_mac=dst_mac, ether_type=ether_type ) self.create_classify_table(key, a_mask) for host in self.hosts_by_pg_idx[self.pg0.sw_if_index]: s_mac = host.mac if src_mac else "" if dst_mac: for dst_if in self.flows[self.pg0]: for dst_host in self.hosts_by_pg_idx[dst_if.sw_if_index]: self.create_classify_session( self.pg0, self.acl_tbl_idx.get(key), self.build_mac_match( src_mac=s_mac, dst_mac=dst_host.mac, ether_type=etype ), hit_next_index=hit_next_index, ) else: self.create_classify_session( self.pg0, self.acl_tbl_idx.get(key), self.build_mac_match(src_mac=s_mac, dst_mac="", ether_type=etype), hit_next_index=hit_next_index, ) def test_0000_warmup_test(self): """Learn the MAC addresses""" self.create_hosts(2) self.run_traffic_no_check() def test_0010_inacl_permit_src_mac(self): """Input L2 ACL test - permit source MAC Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACL with source MAC address. - Send and verify received packets on pg1 interface. """ key = "mac_in" self.build_classify_table(src_mac="ffffffffffff", key=key) self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_test(self.IP, self.IPV4, -1) def test_0011_inacl_permit_dst_mac(self): """Input L2 ACL test - permit destination MAC Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACL with destination MAC address. - Send and verify received packets on pg1 interface. """ key = "mac_in" self.build_classify_table(dst_mac="ffffffffffff", key=key) self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_test(self.IP, self.IPV4, -1) def test_0012_inacl_permit_src_dst_mac(self): """Input L2 ACL test - permit source and destination MAC Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACL with source and destination MAC addresses. - Send and verify received packets on pg1 interface. """ key = "mac_in" self.build_classify_table( src_mac="ffffffffffff", dst_mac="ffffffffffff", key=key ) self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_test(self.IP, self.IPV4, -1) def test_0013_inacl_permit_ether_type(self): """Input L2 ACL test - permit ether_type Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACL with destination MAC address. - Send and verify received packets on pg1 interface. """ key = "mac_in" self.build_classify_table(ether_type="ffff", etype=hex(ETH_P_IP)[2:], key=key) self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_test(self.IP, self.IPV4, -1) def test_0015_inacl_deny(self): """Input L2 ACL test - deny Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACL with source MAC address. - Send and verify no received packets on pg1 interface. """ key = "mac_in" self.build_classify_table(src_mac="ffffffffffff", hit_next_index=0, key=key) self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_negat_test(self.IP, self.IPV4, -1) def test_0020_outacl_permit(self): """Output L2 ACL test - permit Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACL with source MAC address. - Send and verify received packets on pg1 interface. """ key = "mac_out" self.build_classify_table(src_mac="ffffffffffff", key=key) self.output_acl_set_interface(self.pg1, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_test(self.IP, self.IPV4, -1) def test_0025_outacl_deny(self): """Output L2 ACL test - deny Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACL with source MAC address. - Send and verify no received packets on pg1 interface. """ key = "mac_out" self.build_classify_table(src_mac="ffffffffffff", hit_next_index=0, key=key) self.output_acl_set_interface(self.pg1, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_negat_test(self.IP, self.IPV4, -1) def test_0030_inoutacl_permit(self): """Input+Output L2 ACL test - permit Test scenario for basic IP ACL with source IP - Create IPv4 stream for pg0 -> pg1 interface. - Create ACLs with source MAC address. - Send and verify received packets on pg1 interface. """ key = "mac_inout" self.build_classify_table(src_mac="ffffffffffff", key=key) self.output_acl_set_interface(self.pg1, self.acl_tbl_idx.get(key)) self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key)) self.acl_active_table = key self.run_verify_test(self.IP, self.IPV4, -1) if __name__ == "__main__": unittest.main(testRunner=VppTestRunner)