From 02950406c49a743f631395ed52073921744e1afd Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Fri, 20 Dec 2019 00:54:57 +0000 Subject: [PATCH] ipsec: Targeted unit testing Type: fix 1 - big packets; chained buffers and those without enoguh space to add ESP header 2 - IPv6 extension headers in packets that are encrypted/decrypted 3 - Interface protection with SAs that have null algorithms Signed-off-by: Neale Ranns Change-Id: Ie330861fb06a9b248d9dcd5c730e21326ac8e973 --- src/vnet/ipsec/esp_decrypt.c | 29 +++- src/vnet/ipsec/esp_encrypt.c | 34 +++-- src/vnet/ipsec/ipsec.c | 14 +- src/vnet/ipsec/ipsec.h | 12 +- src/vnet/ipsec/ipsec_tun.c | 70 ++++++--- src/vnet/ipsec/ipsec_tun_in.c | 52 ------- test/template_ipsec.py | 164 +++++++++++++++++++++- test/test_ipsec_esp.py | 7 +- test/test_ipsec_tun_if_esp.py | 320 +++++++++++++++++++++++++++++++++++++++++- 9 files changed, 595 insertions(+), 107 deletions(-) diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c index 16ae3a3d9eb..ee53b018552 100644 --- a/src/vnet/ipsec/esp_decrypt.c +++ b/src/vnet/ipsec/esp_decrypt.c @@ -53,6 +53,7 @@ typedef enum _(OVERSIZED_HEADER, "buffer with oversized header (dropped)") \ _(NO_TAIL_SPACE, "no enough buffer tail space (dropped)") \ _(TUN_NO_PROTO, "no tunnel protocol") \ + _(UNSUP_PAYLOAD, "unsupported payload") \ typedef enum @@ -311,9 +312,10 @@ esp_decrypt_inline (vlib_main_t * vm, b += 1; } - vlib_increment_combined_counter (&ipsec_sa_counters, thread_index, - current_sa_index, current_sa_pkts, - current_sa_bytes); + if (PREDICT_TRUE (~0 != current_sa_index)) + vlib_increment_combined_counter (&ipsec_sa_counters, thread_index, + current_sa_index, current_sa_pkts, + current_sa_bytes); if ((n = vec_len (ptd->integ_ops))) { @@ -513,6 +515,8 @@ esp_decrypt_inline (vlib_main_t * vm, next[0] = ESP_DECRYPT_NEXT_IP6_INPUT; break; default: + b[0]->error = + node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD]; next[0] = ESP_DECRYPT_NEXT_DROP; break; } @@ -520,8 +524,7 @@ esp_decrypt_inline (vlib_main_t * vm, else { next[0] = ESP_DECRYPT_NEXT_DROP; - b[0]->error = - node->errors[ESP_DECRYPT_ERROR_DECRYPTION_FAILED]; + b[0]->error = node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD]; goto trace; } } @@ -530,8 +533,20 @@ esp_decrypt_inline (vlib_main_t * vm, if (ipsec_sa_is_set_IS_PROTECT (sa0)) { /* - * Check that the reveal IP header matches that - * of the tunnel we are protecting + * There are two encap possibilities + * 1) the tunnel and ths SA are prodiving encap, i.e. it's + * MAC | SA-IP | TUN-IP | ESP | PAYLOAD + * implying the SA is in tunnel mode (on a tunnel interface) + * 2) only the tunnel provides encap + * MAC | TUN-IP | ESP | PAYLOAD + * implying the SA is in transport mode. + * + * For 2) we need only strip the tunnel encap and we're good. + * since the tunnel and crypto ecnap (int the tun=protect + * object) are the same and we verified above that these match + * for 1) we need to strip the SA-IP outer headers, to + * reveal the tunnel IP and then check that this matches + * the configured tunnel. */ const ipsec_tun_protect_t *itp; diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c index 4f1bb802bcb..3c2fdf4ec3c 100644 --- a/src/vnet/ipsec/esp_encrypt.c +++ b/src/vnet/ipsec/esp_encrypt.c @@ -176,17 +176,19 @@ ext_hdr_is_pre_esp (u8 nexthdr) } static_always_inline u8 -esp_get_ip6_hdr_len (ip6_header_t * ip6) +esp_get_ip6_hdr_len (ip6_header_t * ip6, ip6_ext_header_t ** ext_hdr) { /* this code assumes that HbH, route and frag headers will be before others, if that is not the case, they will end up encrypted */ - u8 len = sizeof (ip6_header_t); ip6_ext_header_t *p; /* if next packet doesn't have ext header */ if (ext_hdr_is_pre_esp (ip6->protocol) == 0) - return len; + { + *ext_hdr = NULL; + return len; + } p = (void *) (ip6 + 1); len += ip6_ext_header_len (p); @@ -197,6 +199,7 @@ esp_get_ip6_hdr_len (ip6_header_t * ip6) p = ip6_ext_next_header (p); } + *ext_hdr = p; return len; } @@ -401,11 +404,12 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node, else /* transport mode */ { u8 *l2_hdr, l2_len, *ip_hdr, ip_len; + ip6_ext_header_t *ext_hdr; udp_header_t *udp = 0; u8 *old_ip_hdr = vlib_buffer_get_current (b[0]); ip_len = is_ip6 ? - esp_get_ip6_hdr_len ((ip6_header_t *) old_ip_hdr) : + esp_get_ip6_hdr_len ((ip6_header_t *) old_ip_hdr, &ext_hdr) : ip4_header_bytes ((ip4_header_t *) old_ip_hdr); vlib_buffer_advance (b[0], ip_len); @@ -445,21 +449,27 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node, else l2_len = 0; - clib_memcpy_le64 (ip_hdr, old_ip_hdr, ip_len); - if (is_ip6) { - ip6_header_t *ip6 = (ip6_header_t *) (ip_hdr); - *next_hdr_ptr = ip6->protocol; - ip6->protocol = IP_PROTOCOL_IPSEC_ESP; + ip6_header_t *ip6 = (ip6_header_t *) (old_ip_hdr); + if (PREDICT_TRUE (NULL == ext_hdr)) + { + *next_hdr_ptr = ip6->protocol; + ip6->protocol = IP_PROTOCOL_IPSEC_ESP; + } + else + { + *next_hdr_ptr = ext_hdr->next_hdr; + ext_hdr->next_hdr = IP_PROTOCOL_IPSEC_ESP; + } ip6->payload_length = clib_host_to_net_u16 (payload_len + hdr_len - l2_len - - ip_len); + sizeof (ip6_header_t)); } else { u16 len; - ip4_header_t *ip4 = (ip4_header_t *) (ip_hdr); + ip4_header_t *ip4 = (ip4_header_t *) (old_ip_hdr); *next_hdr_ptr = ip4->protocol; len = payload_len + hdr_len - l2_len; if (udp) @@ -471,6 +481,8 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node, esp_update_ip4_hdr (ip4, len, /* is_transport */ 1, 0); } + clib_memcpy_le64 (ip_hdr, old_ip_hdr, ip_len); + if (!is_tun) next[0] = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT; } diff --git a/src/vnet/ipsec/ipsec.c b/src/vnet/ipsec/ipsec.c index 0f4f2820349..1075fe48d84 100644 --- a/src/vnet/ipsec/ipsec.c +++ b/src/vnet/ipsec/ipsec.c @@ -188,9 +188,13 @@ ipsec_register_esp_backend (vlib_main_t * vm, ipsec_main_t * im, &b->esp6_decrypt_node_index, &b->esp6_decrypt_next_index); ipsec_add_feature ("ip4-output", esp4_encrypt_node_tun_name, - &b->esp4_encrypt_tun_feature_index); + &b->esp44_encrypt_tun_feature_index); + ipsec_add_feature ("ip4-output", esp6_encrypt_node_tun_name, + &b->esp46_encrypt_tun_feature_index); ipsec_add_feature ("ip6-output", esp6_encrypt_node_tun_name, - &b->esp6_encrypt_tun_feature_index); + &b->esp66_encrypt_tun_feature_index); + ipsec_add_feature ("ip6-output", esp4_encrypt_node_tun_name, + &b->esp64_encrypt_tun_feature_index); b->check_support_cb = esp_check_support_cb; b->add_del_sa_sess_cb = esp_add_del_sa_sess_cb; @@ -252,8 +256,10 @@ ipsec_select_esp_backend (ipsec_main_t * im, u32 backend_idx) im->esp6_encrypt_next_index = b->esp6_encrypt_next_index; im->esp6_decrypt_next_index = b->esp6_decrypt_next_index; - im->esp4_encrypt_tun_feature_index = b->esp4_encrypt_tun_feature_index; - im->esp6_encrypt_tun_feature_index = b->esp6_encrypt_tun_feature_index; + im->esp44_encrypt_tun_feature_index = b->esp44_encrypt_tun_feature_index; + im->esp64_encrypt_tun_feature_index = b->esp64_encrypt_tun_feature_index; + im->esp46_encrypt_tun_feature_index = b->esp46_encrypt_tun_feature_index; + im->esp66_encrypt_tun_feature_index = b->esp66_encrypt_tun_feature_index; return 0; } diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h index af758418322..65b888e51a2 100644 --- a/src/vnet/ipsec/ipsec.h +++ b/src/vnet/ipsec/ipsec.h @@ -61,8 +61,10 @@ typedef struct u32 esp6_decrypt_node_index; u32 esp6_encrypt_next_index; u32 esp6_decrypt_next_index; - u32 esp4_encrypt_tun_feature_index; - u32 esp6_encrypt_tun_feature_index; + u32 esp44_encrypt_tun_feature_index; + u32 esp46_encrypt_tun_feature_index; + u32 esp66_encrypt_tun_feature_index; + u32 esp64_encrypt_tun_feature_index; } ipsec_esp_backend_t; typedef struct @@ -135,8 +137,10 @@ typedef struct u32 ah6_decrypt_next_index; /* tun encrypt arcs and feature nodes */ - u32 esp4_encrypt_tun_feature_index; - u32 esp6_encrypt_tun_feature_index; + u32 esp44_encrypt_tun_feature_index; + u32 esp64_encrypt_tun_feature_index; + u32 esp46_encrypt_tun_feature_index; + u32 esp66_encrypt_tun_feature_index; /* tun nodes to drop packets when no crypto alg set on outbound SA */ u32 esp4_no_crypto_tun_feature_index; diff --git a/src/vnet/ipsec/ipsec_tun.c b/src/vnet/ipsec/ipsec_tun.c index ca0091b3545..f6b09d65f2c 100644 --- a/src/vnet/ipsec/ipsec_tun.c +++ b/src/vnet/ipsec/ipsec_tun.c @@ -35,35 +35,61 @@ typedef struct ipsec_protect_db_t_ static ipsec_protect_db_t ipsec_protect_db; -static int +static void ipsec_tun_protect_feature_set (ipsec_tun_protect_t * itp, u8 enable) { - u32 sai = itp->itp_out_sa; - int rv; + u32 sai; - const char *enc_node = (ip46_address_is_ip4 (&itp->itp_tun.src) ? - "esp4-encrypt-tun" : "esp6-encrypt-tun"); + sai = itp->itp_out_sa; if (itp->itp_flags & IPSEC_PROTECT_L2) { - rv = vnet_feature_enable_disable ("ethernet-output", - enc_node, - itp->itp_sw_if_index, enable, - &sai, sizeof (sai)); + /* l2-GRE only supported by the vnet ipsec code */ + vnet_feature_enable_disable ("ethernet-output", + (ip46_address_is_ip4 (&itp->itp_tun.src) ? + "esp4-encrypt-tun" : + "esp6-encrypt-tun"), + itp->itp_sw_if_index, enable, + &sai, sizeof (sai)); } else { - rv = vnet_feature_enable_disable ("ip4-output", - enc_node, - itp->itp_sw_if_index, enable, - &sai, sizeof (sai)); - rv = vnet_feature_enable_disable ("ip6-output", - enc_node, - itp->itp_sw_if_index, enable, - &sai, sizeof (sai)); + ipsec_main_t *im; + ipsec_sa_t *sa; + u32 fi4, fi6; + + im = &ipsec_main; + sa = ipsec_sa_get (sai); + + if (sa->crypto_alg == IPSEC_CRYPTO_ALG_NONE && + sa->integ_alg == IPSEC_INTEG_ALG_NONE) + { + fi4 = im->esp4_no_crypto_tun_feature_index; + fi6 = im->esp6_no_crypto_tun_feature_index; + } + else + { + if (ip46_address_is_ip4 (&itp->itp_tun.src)) + { + /* tunnel destination is v4 so we need the Xo4 indexes */ + fi4 = im->esp44_encrypt_tun_feature_index; + fi6 = im->esp64_encrypt_tun_feature_index; + } + else + { + /* tunnel destination is v6 so we need the Xo6 indexes */ + fi4 = im->esp46_encrypt_tun_feature_index; + fi6 = im->esp66_encrypt_tun_feature_index; + } + } + + vnet_feature_enable_disable_with_index + (vnet_get_feature_arc_index ("ip4-output"), + fi4, itp->itp_sw_if_index, enable, &sai, sizeof (sai)); + vnet_feature_enable_disable_with_index + (vnet_get_feature_arc_index ("ip6-output"), + fi6, itp->itp_sw_if_index, enable, &sai, sizeof (sai)); } - ASSERT (!rv); - return (rv); } static void @@ -505,6 +531,12 @@ ipsec_tunnel_protect_init (vlib_main_t * vm) sizeof (u64)); im->tun4_protect_by_key = hash_create (0, sizeof (u64)); + /* set up feature nodes to drop outbound packets with no crypto alg set */ + ipsec_add_feature ("ip4-output", "esp4-no-crypto", + &im->esp4_no_crypto_tun_feature_index); + ipsec_add_feature ("ip6-output", "esp6-no-crypto", + &im->esp6_no_crypto_tun_feature_index); + return 0; } diff --git a/src/vnet/ipsec/ipsec_tun_in.c b/src/vnet/ipsec/ipsec_tun_in.c index f25a76319f1..e6ad67b433a 100644 --- a/src/vnet/ipsec/ipsec_tun_in.c +++ b/src/vnet/ipsec/ipsec_tun_in.c @@ -311,58 +311,6 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, n_bytes = len0; } - /* - * compare the packet's outer IP headers to that of the tunnels - */ - if (is_ip6) - { - if (PREDICT_FALSE - (!ip46_address_is_equal_v6 - (&itp0->itp_crypto.dst, &ip60->src_address) - || !ip46_address_is_equal_v6 (&itp0->itp_crypto.src, - &ip60->dst_address))) - { - b[0]->error = - node->errors - [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH]; - next[0] = IPSEC_INPUT_NEXT_DROP; - goto trace00; - } - } - else - { - if (PREDICT_FALSE - (!ip46_address_is_equal_v4 - (&itp0->itp_crypto.dst, &ip40->src_address) - || !ip46_address_is_equal_v4 (&itp0->itp_crypto.src, - &ip40->dst_address))) - { - b[0]->error = - node->errors - [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH]; - next[0] = IPSEC_INPUT_NEXT_DROP; - goto trace00; - } - } - - /* - * There are two encap possibilities - * 1) the tunnel and ths SA are prodiving encap, i.e. it's - * MAC | SA-IP | TUN-IP | ESP | PAYLOAD - * implying the SA is in tunnel mode (on a tunnel interface) - * 2) only the tunnel provides encap - * MAC | TUN-IP | ESP | PAYLOAD - * implying the SA is in transport mode. - * - * For 2) we need only strip the tunnel encap and we're good. - * since the tunnel and crypto ecnap (int the tun=protect - * object) are the same and we verified above that these match - * for 1) we need to strip the SA-IP outer headers, to - * reveal the tunnel IP and then check that this matches - * the configured tunnel. this we can;t do here since it - * involves a lookup in the per-tunnel-type DB - so ship - * the packet to the tunnel-types provided node to do that - */ next[0] = IPSEC_TUN_PROTECT_NEXT_DECRYPT; } trace00: diff --git a/test/template_ipsec.py b/test/template_ipsec.py index bcfc0d9253a..398a6bb0a23 100644 --- a/test/template_ipsec.py +++ b/test/template_ipsec.py @@ -6,7 +6,9 @@ from scapy.layers.inet import IP, ICMP, TCP, UDP from scapy.layers.ipsec import SecurityAssociation, ESP from scapy.layers.l2 import Ether from scapy.packet import Raw -from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest +from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, IPv6ExtHdrHopByHop, \ + IPv6ExtHdrFragment, IPv6ExtHdrDestOpt + from framework import VppTestCase, VppTestRunner from util import ppp, reassemble4, fragment_rfc791, fragment_rfc8200 @@ -641,6 +643,108 @@ class IpsecTra6(object): self.assert_packet_counter_equal(self.tra6_encrypt_node_name, count) self.assert_packet_counter_equal(self.tra6_decrypt_node_name, count) + def gen_encrypt_pkts_ext_hdrs6(self, sa, sw_intf, src, dst, count=1, + payload_size=54): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IPv6(src=src, dst=dst) / + ICMPv6EchoRequest(id=0, seq=1, + data='X' * payload_size)) + for i in range(count)] + + def gen_pkts_ext_hdrs6(self, sw_intf, src, dst, count=1, payload_size=54): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + IPv6(src=src, dst=dst) / + IPv6ExtHdrHopByHop() / + IPv6ExtHdrFragment(id=2, offset=200) / + Raw(b'\xff' * 200) + for i in range(count)] + + def verify_tra_encrypted6(self, p, sa, rxs): + decrypted = [] + for rx in rxs: + self.assert_packet_checksums_valid(rx) + try: + decrypt_pkt = p.vpp_tra_sa.decrypt(rx[IPv6]) + decrypted.append(decrypt_pkt) + self.assert_equal(decrypt_pkt.src, self.tra_if.local_ip6) + self.assert_equal(decrypt_pkt.dst, self.tra_if.remote_ip6) + except: + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", decrypt_pkt)) + except: + pass + raise + return decrypted + + def verify_tra_66_ext_hdrs(self, p): + count = 63 + + # + # check we can decrypt with options + # + tx = self.gen_encrypt_pkts_ext_hdrs6(p.scapy_tra_sa, self.tra_if, + src=self.tra_if.remote_ip6, + dst=self.tra_if.local_ip6, + count=count) + self.send_and_expect(self.tra_if, tx, self.tra_if) + + # + # injecting a packet from ourselves to be routed of box is a hack + # but it matches an outbout policy, alors je ne regrette rien + # + + # one extension before ESP + tx = (Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / + IPv6(src=self.tra_if.local_ip6, + dst=self.tra_if.remote_ip6) / + IPv6ExtHdrFragment(id=2, offset=200) / + Raw(b'\xff' * 200)) + + rxs = self.send_and_expect(self.pg2, [tx], self.tra_if) + dcs = self.verify_tra_encrypted6(p, p.vpp_tra_sa, rxs) + + for dc in dcs: + # for reasons i'm not going to investigate scapy does not + # created the correct headers after decrypt. but reparsing + # the ipv6 packet fixes it + dc = IPv6(raw(dc[IPv6])) + self.assert_equal(dc[IPv6ExtHdrFragment].id, 2) + + # two extensions before ESP + tx = (Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / + IPv6(src=self.tra_if.local_ip6, + dst=self.tra_if.remote_ip6) / + IPv6ExtHdrHopByHop() / + IPv6ExtHdrFragment(id=2, offset=200) / + Raw(b'\xff' * 200)) + + rxs = self.send_and_expect(self.pg2, [tx], self.tra_if) + dcs = self.verify_tra_encrypted6(p, p.vpp_tra_sa, rxs) + + for dc in dcs: + dc = IPv6(raw(dc[IPv6])) + self.assertTrue(dc[IPv6ExtHdrHopByHop]) + self.assert_equal(dc[IPv6ExtHdrFragment].id, 2) + + # two extensions before ESP, one after + tx = (Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / + IPv6(src=self.tra_if.local_ip6, + dst=self.tra_if.remote_ip6) / + IPv6ExtHdrHopByHop() / + IPv6ExtHdrFragment(id=2, offset=200) / + IPv6ExtHdrDestOpt() / + Raw(b'\xff' * 200)) + + rxs = self.send_and_expect(self.pg2, [tx], self.tra_if) + dcs = self.verify_tra_encrypted6(p, p.vpp_tra_sa, rxs) + + for dc in dcs: + dc = IPv6(raw(dc[IPv6])) + self.assertTrue(dc[IPv6ExtHdrDestOpt]) + self.assertTrue(dc[IPv6ExtHdrHopByHop]) + self.assert_equal(dc[IPv6ExtHdrFragment].id, 2) + class IpsecTra6Tests(IpsecTra6): """ UT test methods for Transport v6 """ @@ -653,6 +757,12 @@ class IpsecTra6Tests(IpsecTra6): self.verify_tra_basic6(count=257) +class IpsecTra6ExtTests(IpsecTra6): + def test_tra_ext_hdrs_66(self): + """ ipsec 6o6 tra extension headers test """ + self.verify_tra_66_ext_hdrs(self.params[socket.AF_INET6]) + + class IpsecTra46Tests(IpsecTra4Tests, IpsecTra6Tests): """ UT test methods for Transport v6 and v4""" pass @@ -715,6 +825,7 @@ class IpsecTun4(object): def verify_tun_44(self, p, count=1, payload_size=64, n_rx=None): self.vapi.cli("clear errors") + self.vapi.cli("clear ipsec counters") if not n_rx: n_rx = count try: @@ -736,8 +847,45 @@ class IpsecTun4(object): self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show ipsec all")) + self.logger.info(self.vapi.ppcli("show ipsec sa 0")) + self.logger.info(self.vapi.ppcli("show ipsec sa 4")) self.verify_counters4(p, count, n_rx) + """ verify methods for Transport v4 """ + def verify_tun_44_bad_packet_sizes(self, p): + # with a buffer size of 2048, 1989 bytes of payload + # means there isn't space to insert the ESP header + N_PKTS = 63 + for p_siz in [1989, 8500]: + send_pkts = self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + src=p.remote_tun_if_host, + dst=self.pg1.remote_ip4, + count=N_PKTS, + payload_size=p_siz) + self.send_and_assert_no_replies(self.tun_if, send_pkts) + send_pkts = self.gen_pkts(self.pg1, src=self.pg1.remote_ip4, + dst=p.remote_tun_if_host, count=N_PKTS, + payload_size=p_siz) + self.send_and_assert_no_replies(self.pg1, send_pkts, + self.tun_if) + + # both large packets on decrpyt count against chained buffers + # the 9000 bytes one does on encrypt + self.assertEqual(2 * N_PKTS, + self.statistics.get_err_counter( + '/err/%s/chained buffers (packet dropped)' % + self.tun4_decrypt_node_name)) + self.assertEqual(N_PKTS, + self.statistics.get_err_counter( + '/err/%s/chained buffers (packet dropped)' % + self.tun4_encrypt_node_name)) + + # on encrypt the 1989 size is no trailer space + self.assertEqual(N_PKTS, + self.statistics.get_err_counter( + '/err/%s/no trailer space (packet dropped)' % + self.tun4_encrypt_node_name)) + def verify_tun_reass_44(self, p): self.vapi.cli("clear errors") self.vapi.ip_reassembly_enable_disable( @@ -828,6 +976,10 @@ class IpsecTun4Tests(IpsecTun4): def test_tun_basic44(self): """ ipsec 4o4 tunnel basic test """ self.verify_tun_44(self.params[socket.AF_INET], count=1) + self.tun_if.admin_down() + self.tun_if.resolve_arp() + self.tun_if.admin_up() + self.verify_tun_44(self.params[socket.AF_INET], count=1) def test_tun_reass_basic44(self): """ ipsec 4o4 tunnel basic reassembly test """ @@ -835,7 +987,13 @@ class IpsecTun4Tests(IpsecTun4): def test_tun_burst44(self): """ ipsec 4o4 tunnel burst test """ - self.verify_tun_44(self.params[socket.AF_INET], count=257) + self.verify_tun_44(self.params[socket.AF_INET], count=127) + + +class IpsecTunEsp4Tests(IpsecTun4): + def test_tun_bad_packet_sizes(self): + """ ipsec v4 tunnel bad packet size """ + self.verify_tun_44_bad_packet_sizes(self.params[socket.AF_INET]) class IpsecTun6(object): @@ -926,7 +1084,7 @@ class IpsecTun6(object): src=p.remote_tun_if_host, dst=self.pg1.remote_ip6, count=1, - payload_size=1900) + payload_size=1850) send_pkts = fragment_rfc8200(send_pkts[0], 1, 1400, self.logger) recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1, n_rx=1) diff --git a/test/test_ipsec_esp.py b/test/test_ipsec_esp.py index 82346d64708..60e5c93ed65 100644 --- a/test/test_ipsec_esp.py +++ b/test/test_ipsec_esp.py @@ -9,7 +9,8 @@ from template_ipsec import IpsecTra46Tests, IpsecTun46Tests, TemplateIpsec, \ IpsecTcpTests, IpsecTun4Tests, IpsecTra4Tests, config_tra_params, \ config_tun_params, IPsecIPv4Params, IPsecIPv6Params, \ IpsecTra4, IpsecTun4, IpsecTra6, IpsecTun6, \ - IpsecTun6HandoffTests, IpsecTun4HandoffTests + IpsecTun6HandoffTests, IpsecTun4HandoffTests, \ + IpsecTra6ExtTests, IpsecTunEsp4Tests from vpp_ipsec import VppIpsecSpd, VppIpsecSpdEntry, VppIpsecSA,\ VppIpsecSpdItfBinding from vpp_ip_route import VppIpRoute, VppRoutePath @@ -286,7 +287,9 @@ class TemplateIpsecEsp(ConfigIpsecESP): super(TemplateIpsecEsp, self).tearDown() -class TestIpsecEsp1(TemplateIpsecEsp, IpsecTra46Tests, IpsecTun46Tests): +class TestIpsecEsp1(TemplateIpsecEsp, IpsecTra46Tests, + IpsecTun46Tests, IpsecTunEsp4Tests, + IpsecTra6ExtTests): """ Ipsec ESP - TUN & TRA tests """ pass diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py index eefd477c71d..469ebc7fc87 100644 --- a/test/test_ipsec_tun_if_esp.py +++ b/test/test_ipsec_tun_if_esp.py @@ -304,6 +304,7 @@ class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4): p.scapy_tra_spi = p.scapy_tra_spi + ii p.vpp_tra_sa_id = p.vpp_tra_sa_id + ii p.vpp_tra_spi = p.vpp_tra_spi + ii + p.tun_dst = self.pg0.remote_hosts[ii].ip4 p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi, p.scapy_tun_spi, @@ -311,7 +312,7 @@ class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4): p.crypt_key, p.crypt_key, p.auth_algo_vpp_id, p.auth_key, p.auth_key, - dst=self.pg0.remote_hosts[ii].ip4) + dst=p.tun_dst) p.tun_if.add_vpp_config() p.tun_if.admin_up() p.tun_if.config_ip4() @@ -334,6 +335,27 @@ class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4): c = p.tun_if.get_tx_stats() self.assertEqual(c['packets'], 127) + def test_tun_rr_44(self): + """ Round-robin packets acrros multiple interface """ + tx = [] + for p in self.multi_params: + tx = tx + self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + src=p.remote_tun_if_host, + dst=self.pg1.remote_ip4) + rxs = self.send_and_expect(self.tun_if, tx, self.pg1) + + for rx, p in zip(rxs, self.multi_params): + self.verify_decrypted(p, [rx]) + + tx = [] + for p in self.multi_params: + tx = tx + self.gen_pkts(self.pg1, src=self.pg1.remote_ip4, + dst=p.remote_tun_if_host) + rxs = self.send_and_expect(self.pg1, tx, self.tun_if) + + for rx, p in zip(rxs, self.multi_params): + self.verify_encrypted(p, p.vpp_tun_sa, [rx]) + class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4): """ IPsec IPv4 Tunnel interface all Algos """ @@ -521,6 +543,69 @@ class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4): p.tun_sa_in.remove_vpp_config() +class TestIpsec4TunIfEspNoAlgo(TemplateIpsec, IpsecTun4): + """ IPsec IPv4 Tunnel interface all Algos """ + + encryption_type = ESP + tun4_encrypt_node_name = "esp4-encrypt-tun" + tun4_decrypt_node_name = "esp4-decrypt-tun" + + def config_network(self, p): + + p.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t. + IPSEC_API_INTEG_ALG_NONE) + p.auth_algo = 'NULL' + p.auth_key = [] + + p.crypt_algo_vpp_id = (VppEnum.vl_api_ipsec_crypto_alg_t. + IPSEC_API_CRYPTO_ALG_NONE) + p.crypt_algo = 'NULL' + p.crypt_key = [] + + p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi, + p.scapy_tun_spi, + p.crypt_algo_vpp_id, + p.crypt_key, p.crypt_key, + p.auth_algo_vpp_id, p.auth_key, + p.auth_key, + salt=p.salt) + p.tun_if.add_vpp_config() + p.tun_if.admin_up() + p.tun_if.config_ip4() + config_tun_params(p, self.encryption_type, p.tun_if) + self.logger.info(self.vapi.cli("sh ipsec sa 0")) + self.logger.info(self.vapi.cli("sh ipsec sa 1")) + + p.route = VppIpRoute(self, p.remote_tun_if_host, 32, + [VppRoutePath(p.tun_if.remote_ip4, + 0xffffffff)]) + p.route.add_vpp_config() + + def unconfig_network(self, p): + p.tun_if.unconfig_ip4() + p.tun_if.remove_vpp_config() + p.route.remove_vpp_config() + + def setUp(self): + super(TestIpsec4TunIfEspNoAlgo, self).setUp() + + self.tun_if = self.pg0 + + def tearDown(self): + super(TestIpsec4TunIfEspNoAlgo, self).tearDown() + + def test_tun_44(self): + p = self.ipv4_params + + self.config_network(p) + + tx = self.gen_pkts(self.pg1, src=self.pg1.remote_ip4, + dst=p.remote_tun_if_host) + self.send_and_assert_no_replies(self.pg1, tx) + + self.unconfig_network(p) + + class TestIpsec6MultiTunIfEsp(TemplateIpsec, IpsecTun6): """ IPsec IPv6 Multi Tunnel interface """ @@ -919,6 +1004,16 @@ class TestIpsecGreIfEspTra(TemplateIpsec, Raw(b'X' * payload_size)) for i in range(count)] + def gen_encrypt_non_ip_pkts(self, sa, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IP(src=self.pg0.remote_ip4, + dst=self.pg0.local_ip4) / + GRE() / + UDP(sport=1144, dport=2233) / + Raw(b'X' * payload_size)) + for i in range(count)] + def gen_pkts(self, sw_intf, src, dst, count=1, payload_size=100): return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / @@ -996,6 +1091,115 @@ class TestIpsecGreIfEspTra(TemplateIpsec, p.tun_if.unconfig_ip4() super(TestIpsecGreIfEspTra, self).tearDown() + def test_gre_non_ip(self): + p = self.ipv4_params + tx = self.gen_encrypt_non_ip_pkts(p.scapy_tun_sa, self.tun_if, + src=p.remote_tun_if_host, + dst=self.pg1.remote_ip6) + self.send_and_assert_no_replies(self.tun_if, tx) + node_name = ('/err/%s/unsupported payload' % + self.tun4_decrypt_node_name) + self.assertEqual(1, self.statistics.get_err_counter(node_name)) + + +class TestIpsecGre6IfEspTra(TemplateIpsec, + IpsecTun6Tests): + """ Ipsec GRE ESP - TRA tests """ + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = "esp6-decrypt-tun" + encryption_type = ESP + + def gen_encrypt_pkts6(self, sa, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IPv6(src=self.pg0.remote_ip6, + dst=self.pg0.local_ip6) / + GRE() / + IPv6(src=self.pg1.local_ip6, + dst=self.pg1.remote_ip6) / + UDP(sport=1144, dport=2233) / + Raw(b'X' * payload_size)) + for i in range(count)] + + def gen_pkts6(self, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + IPv6(src="1::1", dst="1::2") / + UDP(sport=1144, dport=2233) / + Raw(b'X' * payload_size) + for i in range(count)] + + def verify_decrypted6(self, p, rxs): + for rx in rxs: + self.assert_equal(rx[Ether].dst, self.pg1.remote_mac) + self.assert_equal(rx[IPv6].dst, self.pg1.remote_ip6) + + def verify_encrypted6(self, p, sa, rxs): + for rx in rxs: + try: + pkt = sa.decrypt(rx[IPv6]) + if not pkt.haslayer(IPv6): + pkt = IPv6(pkt[Raw].load) + self.assert_packet_checksums_valid(pkt) + self.assertTrue(pkt.haslayer(GRE)) + e = pkt[GRE] + self.assertEqual(e[IPv6].dst, "1::2") + except (IndexError, AssertionError): + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", pkt)) + except: + pass + raise + + def setUp(self): + super(TestIpsecGre6IfEspTra, self).setUp() + + self.tun_if = self.pg0 + + p = self.ipv6_params + + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + + p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol) + p.tun_sa_out.add_vpp_config() + + p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi, + p.auth_algo_vpp_id, p.auth_key, + p.crypt_algo_vpp_id, p.crypt_key, + self.vpp_esp_protocol) + p.tun_sa_in.add_vpp_config() + + p.tun_if = VppGreInterface(self, + self.pg0.local_ip6, + self.pg0.remote_ip6) + p.tun_if.add_vpp_config() + + p.tun_protect = VppIpsecTunProtect(self, + p.tun_if, + p.tun_sa_out, + [p.tun_sa_in]) + p.tun_protect.add_vpp_config() + + p.tun_if.admin_up() + p.tun_if.config_ip6() + config_tra_params(p, self.encryption_type, p.tun_if) + + r = VppIpRoute(self, "1::2", 128, + [VppRoutePath(p.tun_if.remote_ip6, + 0xffffffff, + proto=DpoProto.DPO_PROTO_IP6)]) + r.add_vpp_config() + + def tearDown(self): + p = self.ipv6_params + p.tun_if.unconfig_ip6() + super(TestIpsecGre6IfEspTra, self).tearDown() + class TemplateIpsec4TunProtect(object): """ IPsec IPv4 Tunnel protect """ @@ -1286,6 +1490,54 @@ class TestIpsec4TunProtectTun(TemplateIpsec, self.unconfig_network(p) +class TestIpsec4TunProtectTunDrop(TemplateIpsec, + TemplateIpsec4TunProtect, + IpsecTun4): + """ IPsec IPv4 Tunnel protect - tunnel mode - drop""" + + encryption_type = ESP + tun4_encrypt_node_name = "esp4-encrypt-tun" + tun4_decrypt_node_name = "esp4-decrypt-tun" + + def setUp(self): + super(TestIpsec4TunProtectTunDrop, self).setUp() + + self.tun_if = self.pg0 + + def tearDown(self): + super(TestIpsec4TunProtectTunDrop, self).tearDown() + + def gen_encrypt_pkts(self, sa, sw_intf, src, dst, count=1, + payload_size=100): + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IP(src=sw_intf.remote_ip4, + dst="5.5.5.5") / + IP(src=src, dst=dst) / + UDP(sport=1144, dport=2233) / + Raw(b'X' * payload_size)) + for i in range(count)] + + def test_tun_drop_44(self): + """IPSEC tunnel protect bogus tunnel header """ + + p = self.ipv4_params + + self.config_network(p) + self.config_sa_tun(p) + self.config_protect(p) + + tx = self.gen_encrypt_pkts(p.scapy_tun_sa, self.tun_if, + src=p.remote_tun_if_host, + dst=self.pg1.remote_ip4, + count=63) + self.send_and_assert_no_replies(self.tun_if, tx) + + # teardown + self.unconfig_protect(p) + self.unconfig_sa(p) + self.unconfig_network(p) + + class TemplateIpsec6TunProtect(object): """ IPsec IPv6 Tunnel protect """ @@ -1379,7 +1631,7 @@ class TestIpsec6TunProtect(TemplateIpsec, super(TestIpsec6TunProtect, self).tearDown() def test_tun_66(self): - """IPSEC tunnel protect""" + """IPSEC tunnel protect 6o6""" p = self.ipv6_params @@ -1413,6 +1665,15 @@ class TestIpsec6TunProtect(TemplateIpsec, c = p.tun_if.get_tx_stats() self.assertEqual(c['packets'], 254) + # bounce the interface state + p.tun_if.admin_down() + self.verify_drop_tun_66(np, count=127) + node = ('/err/ipsec6-tun-input/%s' % + 'ipsec packets received on disabled interface') + self.assertEqual(127, self.statistics.get_err_counter(node)) + p.tun_if.admin_up() + self.verify_tun_66(np, count=127) + # 3 phase rekey # 1) add two input SAs [old, new] # 2) swap output SA to [new] @@ -1447,9 +1708,9 @@ class TestIpsec6TunProtect(TemplateIpsec, self.verify_drop_tun_66(np, count=127) c = p.tun_if.get_rx_stats() - self.assertEqual(c['packets'], 127*7) + self.assertEqual(c['packets'], 127*9) c = p.tun_if.get_tx_stats() - self.assertEqual(c['packets'], 127*7) + self.assertEqual(c['packets'], 127*8) self.unconfig_sa(np) # teardown @@ -1458,7 +1719,7 @@ class TestIpsec6TunProtect(TemplateIpsec, self.unconfig_network(p) def test_tun_46(self): - """IPSEC tunnel protect""" + """IPSEC tunnel protect 4o6""" p = self.ipv6_params @@ -1581,5 +1842,54 @@ class TestIpsec6TunProtectTun(TemplateIpsec, self.unconfig_network(p) +class TestIpsec6TunProtectTunDrop(TemplateIpsec, + TemplateIpsec6TunProtect, + IpsecTun6): + """ IPsec IPv6 Tunnel protect - tunnel mode - drop""" + + encryption_type = ESP + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = "esp6-decrypt-tun" + + def setUp(self): + super(TestIpsec6TunProtectTunDrop, self).setUp() + + self.tun_if = self.pg0 + + def tearDown(self): + super(TestIpsec6TunProtectTunDrop, self).tearDown() + + def gen_encrypt_pkts5(self, sa, sw_intf, src, dst, count=1, + payload_size=100): + # the IP destination of the revelaed packet does not match + # that assigned to the tunnel + return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / + sa.encrypt(IPv6(src=sw_intf.remote_ip6, + dst="5::5") / + IPv6(src=src, dst=dst) / + UDP(sport=1144, dport=2233) / + Raw(b'X' * payload_size)) + for i in range(count)] + + def test_tun_drop_66(self): + """IPSEC 6 tunnel protect bogus tunnel header """ + + p = self.ipv6_params + + self.config_network(p) + self.config_sa_tun(p) + self.config_protect(p) + + tx = self.gen_encrypt_pkts6(p.scapy_tun_sa, self.tun_if, + src=p.remote_tun_if_host, + dst=self.pg1.remote_ip6, + count=63) + self.send_and_assert_no_replies(self.tun_if, tx) + + self.unconfig_protect(p) + self.unconfig_sa(p) + self.unconfig_network(p) + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) -- 2.16.6