From 6f1eb484c85cfdb091cbef18252a02b3310b9aae Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 9 Aug 2022 22:19:38 +0000 Subject: ipsec: enable UDP encap for IPv6 ESP tun protect Type: improvement If an SA protecting an IPv6 tunnel interface has UDP encapsulation enabled, the code in esp_encrypt_inline() inserts a UDP header but does not set the next protocol or the UDP payload length, so the peer that receives the packet drops it. Set the next protocol field and the UDP payload length correctly. The port(s) for UDP encapsulation of IPsec was not registered for IPv6. Add this registration for IPv6 SAs when UDP encapsulation is enabled. Add punt handling for IPv6 IKE on NAT-T port. Add registration of linux-cp for the new punt reason. Add unit tests of IPv6 ESP w/ UDP encapsulation on tun protect Signed-off-by: Matthew Smith Change-Id: Ibb28e423ab8c7bcea2c1964782a788a0f4da5268 --- test/template_ipsec.py | 34 ++++++++++++ test/test_ipsec_tun_if_esp.py | 126 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) (limited to 'test') diff --git a/test/template_ipsec.py b/test/template_ipsec.py index ce188622a9e..def31cbebbd 100644 --- a/test/template_ipsec.py +++ b/test/template_ipsec.py @@ -1695,6 +1695,40 @@ class IpsecTun6(object): self.logger.info(self.vapi.ppcli("show ipsec all")) self.verify_counters6(p, p, count) + def verify_keepalive(self, p): + # the sizeof Raw is calculated to pad to the minimum ehternet + # frame size of 64 btyes + pkt = ( + Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) + / IPv6(src=p.remote_tun_if_host, dst=self.tun_if.local_ip6) + / UDP(sport=333, dport=4500) + / Raw(b"\xff") + / Padding(0 * 1) + ) + self.send_and_assert_no_replies(self.tun_if, pkt * 31) + self.assert_error_counter_equal( + "/err/%s/nat_keepalive" % self.tun6_input_node, 31 + ) + + pkt = ( + Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) + / IPv6(src=p.remote_tun_if_host, dst=self.tun_if.local_ip6) + / UDP(sport=333, dport=4500) + / Raw(b"\xfe") + ) + self.send_and_assert_no_replies(self.tun_if, pkt * 31) + self.assert_error_counter_equal("/err/%s/too_short" % self.tun6_input_node, 31) + + pkt = ( + Ether(src=self.tun_if.remote_mac, dst=self.tun_if.local_mac) + / IPv6(src=p.remote_tun_if_host, dst=self.tun_if.local_ip6) + / UDP(sport=333, dport=4500) + / Raw(b"\xfe") + / Padding(0 * 21) + ) + self.send_and_assert_no_replies(self.tun_if, pkt * 31) + self.assert_error_counter_equal("/err/%s/too_short" % self.tun6_input_node, 62) + class IpsecTun6Tests(IpsecTun6): """UT test methods for Tunnel v6""" diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py index d10ad216bd6..61a66d40a4e 100644 --- a/test/test_ipsec_tun_if_esp.py +++ b/test/test_ipsec_tun_if_esp.py @@ -561,6 +561,132 @@ class TemplateIpsec6TunIfEsp(TemplateIpsec6TunProtect, TemplateIpsec): super(TemplateIpsec6TunIfEsp, self).tearDown() +class TemplateIpsec6TunIfEspUdp(TemplateIpsec6TunProtect, TemplateIpsec): + """IPsec6 UDP tunnel interface tests""" + + tun4_encrypt_node_name = "esp6-encrypt-tun" + tun4_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"] + encryption_type = ESP + + @classmethod + def setUpClass(cls): + super(TemplateIpsec6TunIfEspUdp, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TemplateIpsec6TunIfEspUdp, cls).tearDownClass() + + def verify_encrypted(self, p, sa, rxs): + for rx in rxs: + try: + # ensure the UDP ports are correct before we decrypt + # which strips them + self.assertTrue(rx.haslayer(UDP)) + self.assert_equal(rx[UDP].sport, p.nat_header.sport) + self.assert_equal(rx[UDP].dport, 4500) + + pkt = sa.decrypt(rx[IP]) + if not pkt.haslayer(IP): + pkt = IP(pkt[Raw].load) + + self.assert_packet_checksums_valid(pkt) + self.assert_equal( + pkt[IP].dst, "1111:1111:1111:1111:1111:1111:1111:1111" + ) + self.assert_equal(pkt[IP].src, self.pg1.remote_ip6) + except (IndexError, AssertionError): + self.logger.debug(ppp("Unexpected packet:", rx)) + try: + self.logger.debug(ppp("Decrypted packet:", pkt)) + except: + pass + raise + + def config_sa_tra(self, p): + config_tun_params(p, self.encryption_type, p.tun_if) + + 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, + flags=p.flags, + udp_src=p.nat_header.sport, + udp_dst=p.nat_header.dport, + ) + 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, + flags=p.flags, + udp_src=p.nat_header.sport, + udp_dst=p.nat_header.dport, + ) + p.tun_sa_in.add_vpp_config() + + def setUp(self): + super(TemplateIpsec6TunIfEspUdp, self).setUp() + + p = self.ipv6_params + p.flags = VppEnum.vl_api_ipsec_sad_flags_t.IPSEC_API_SAD_FLAG_UDP_ENCAP + p.nat_header = UDP(sport=5454, dport=4500) + + self.tun_if = self.pg0 + + self.config_network(p) + self.config_sa_tra(p) + self.config_protect(p) + + def tearDown(self): + super(TemplateIpsec6TunIfEspUdp, self).tearDown() + + +class TestIpsec6TunIfEspUdp(TemplateIpsec6TunIfEspUdp, IpsecTun6Tests): + """Ipsec ESP 6 UDP tests""" + + tun6_input_node = "ipsec6-tun-input" + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"] + + def setUp(self): + super(TestIpsec6TunIfEspUdp, self).setUp() + + def test_keepalive(self): + """IPSEC6 NAT Keepalive""" + self.verify_keepalive(self.ipv6_params) + + +class TestIpsec6TunIfEspUdpGCM(TemplateIpsec6TunIfEspUdp, IpsecTun6Tests): + """Ipsec ESP 6 UDP GCM tests""" + + tun6_input_node = "ipsec6-tun-input" + tun6_encrypt_node_name = "esp6-encrypt-tun" + tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"] + + def setUp(self): + super(TestIpsec6TunIfEspUdpGCM, self).setUp() + p = self.ipv6_params + p.auth_algo_vpp_id = VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_NONE + p.crypt_algo_vpp_id = ( + VppEnum.vl_api_ipsec_crypto_alg_t.IPSEC_API_CRYPTO_ALG_AES_GCM_256 + ) + p.crypt_algo = "AES-GCM" + p.auth_algo = "NULL" + p.crypt_key = b"JPjyOWBeVEQiMe7hJPjyOWBeVEQiMe7h" + p.salt = 0 + + class TestIpsec6TunIfEsp1(TemplateIpsec6TunIfEsp, IpsecTun6Tests): """Ipsec ESP - TUN tests""" -- cgit 1.2.3-korg