From 8fce5463707e38c14320d2d0f49cd5ec32dc791e Mon Sep 17 00:00:00 2001 From: Arthur de Kerhor Date: Fri, 16 Jun 2023 09:48:52 +0200 Subject: [PATCH] ipsec: allow receiving encrypted IP packets with TFC padding MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Type: feature Change-Id: I7b29c71d3d053af9a53931aa333484bf43a424ca Signed-off-by: Arthur de Kerhor Signed-off-by: Benoît Ganne --- src/vnet/ipsec/esp_decrypt.c | 46 +++++++++++++++++++++++--- test/test_ipsec_tun_if_esp.py | 76 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c index 7b74e6e2c67..fba4549048d 100644 --- a/src/vnet/ipsec/esp_decrypt.c +++ b/src/vnet/ipsec/esp_decrypt.c @@ -161,6 +161,9 @@ esp_remove_tail (vlib_main_t * vm, vlib_buffer_t * b, vlib_buffer_t * last, { vlib_buffer_t *before_last = b; + if (b != last) + b->total_length_not_including_first_buffer -= tail; + if (last->current_length > tail) { last->current_length -= tail; @@ -178,6 +181,37 @@ esp_remove_tail (vlib_main_t * vm, vlib_buffer_t * b, vlib_buffer_t * last, before_last->flags &= ~VLIB_BUFFER_NEXT_PRESENT; } +always_inline void +esp_remove_tail_and_tfc_padding (vlib_main_t *vm, vlib_node_runtime_t *node, + const esp_decrypt_packet_data_t *pd, + vlib_buffer_t *b, vlib_buffer_t *last, + u16 *next, u16 tail, int is_ip6) +{ + const u16 total_buffer_length = vlib_buffer_length_in_chain (vm, b); + u16 ip_packet_length; + if (is_ip6) + { + const ip6_header_t *ip6 = vlib_buffer_get_current (b); + ip_packet_length = + clib_net_to_host_u16 (ip6->payload_length) + sizeof (ip6_header_t); + } + else + { + const ip4_header_t *ip4 = vlib_buffer_get_current (b); + ip_packet_length = clib_net_to_host_u16 (ip4->length); + } + /* In case of TFC padding, the size of the buffer data needs to be adjusted + * to the ip packet length */ + if (PREDICT_FALSE (total_buffer_length < ip_packet_length + tail)) + { + esp_decrypt_set_next_index (b, node, vm->thread_index, + ESP_DECRYPT_ERROR_NO_TAIL_SPACE, 0, next, + ESP_DECRYPT_NEXT_DROP, pd->sa_index); + return; + } + esp_remove_tail (vm, b, last, total_buffer_length - ip_packet_length); +} + /* ICV is splitted in last two buffers so move it to the last buffer and return pointer to it */ static_always_inline u8 * @@ -203,9 +237,12 @@ esp_move_icv (vlib_main_t * vm, vlib_buffer_t * first, before_last->current_length -= first_sz; if (before_last == first) pd->current_length -= first_sz; + else + first->total_length_not_including_first_buffer -= first_sz; clib_memset (vlib_buffer_get_tail (before_last), 0, first_sz); if (dif) dif[0] = first_sz; + first->total_length_not_including_first_buffer -= last_sz; pd2->lb = before_last; pd2->icv_removed = 1; pd2->free_buffer_index = before_last->next_buffer; @@ -857,8 +894,7 @@ esp_decrypt_post_crypto (vlib_main_t *vm, vlib_node_runtime_t *node, u16 tail = sizeof (esp_footer_t) + pad_length + icv_sz; u16 tail_orig = sizeof (esp_footer_t) + pad_length + pd->icv_sz; b->flags &= - ~(VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_L4_CHECKSUM_COMPUTED | - VNET_BUFFER_F_L4_CHECKSUM_CORRECT); + ~(VNET_BUFFER_F_L4_CHECKSUM_COMPUTED | VNET_BUFFER_F_L4_CHECKSUM_CORRECT); if ((pd->flags & tun_flags) == 0 && !is_tun) /* transport mode */ { @@ -908,14 +944,16 @@ esp_decrypt_post_crypto (vlib_main_t *vm, vlib_node_runtime_t *node, next[0] = ESP_DECRYPT_NEXT_IP4_INPUT; b->current_data = pd->current_data + adv; b->current_length = pd->current_length - adv; - esp_remove_tail (vm, b, lb, tail); + esp_remove_tail_and_tfc_padding (vm, node, pd, b, lb, next, tail, + false); } else if (next_header == IP_PROTOCOL_IPV6) { next[0] = ESP_DECRYPT_NEXT_IP6_INPUT; b->current_data = pd->current_data + adv; b->current_length = pd->current_length - adv; - esp_remove_tail (vm, b, lb, tail); + esp_remove_tail_and_tfc_padding (vm, node, pd, b, lb, next, tail, + true); } else if (next_header == IP_PROTOCOL_MPLS_IN_IP) { diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py index e1579ebfaed..5131fbefe7d 100644 --- a/test/test_ipsec_tun_if_esp.py +++ b/test/test_ipsec_tun_if_esp.py @@ -5,8 +5,8 @@ import copy from scapy.layers.ipsec import SecurityAssociation, ESP from scapy.layers.l2 import Ether, GRE, Dot1Q from scapy.packet import Raw, bind_layers -from scapy.layers.inet import IP, UDP -from scapy.layers.inet6 import IPv6 +from scapy.layers.inet import IP, UDP, ICMP +from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest from scapy.contrib.mpls import MPLS from asfframework import VppTestRunner, tag_fixme_vpp_workers from template_ipsec import ( @@ -367,6 +367,29 @@ class TemplateIpsec4TunIfEspUdp(TemplateIpsec4TunProtect, TemplateIpsec): super(TemplateIpsec4TunIfEspUdp, self).tearDown() +class TemplateIpsec4TunTfc: + """IPsec IPv4 tunnel with TFC""" + + def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1, payload_size=54): + pkt = ( + IP(src=src, dst=dst, len=28 + payload_size) + / ICMP() + / Raw(b"X" * payload_size) + / Padding(b"Y" * 100) + ) + return [ + Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) / sa.encrypt(pkt) + for i in range(count) + ] + + def verify_decrypted(self, p, rxs): + for rx in rxs: + self.assert_equal(rx[IP].src, p.remote_tun_if_host) + self.assert_equal(rx[IP].dst, self.pg1.remote_ip4) + self.assert_equal(rx[IP].len, len(rx[IP])) + self.assert_packet_checksums_valid(rx) + + class TestIpsec4TunIfEsp1(TemplateIpsec4TunIfEsp, IpsecTun4Tests): """Ipsec ESP - TUN tests""" @@ -671,6 +694,28 @@ class TemplateIpsec6TunIfEspUdp(TemplateIpsec6TunProtect, TemplateIpsec): super(TemplateIpsec6TunIfEspUdp, self).tearDown() +class TemplateIpsec6TunTfc: + """IPsec IPv6 tunnel with TFC""" + + def gen_encrypt_pkts6(self, p, 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, hlim=p.inner_hop_limit, fl=p.inner_flow_label) + / ICMPv6EchoRequest(id=0, seq=1, data="X" * payload_size) + / Padding(b"Y" * 100) + ) + for i in range(count) + ] + + def verify_decrypted6(self, p, rxs): + for rx in rxs: + self.assert_equal(rx[IPv6].src, p.remote_tun_if_host) + self.assert_equal(rx[IPv6].dst, self.pg1.remote_ip6) + self.assert_equal(rx[IPv6].plen, len(rx[IPv6].payload)) + self.assert_packet_checksums_valid(rx) + + class TestIpsec6TunIfEspUdp(TemplateIpsec6TunIfEspUdp, IpsecTun6Tests): """Ipsec ESP 6 UDP tests""" @@ -2469,9 +2514,14 @@ class TestIpsec4TunProtect(TemplateIpsec, TemplateIpsec4TunProtect, IpsecTun4): self.unconfig_network(p) +@tag_fixme_vpp_workers +class TestIpsec4TunProtectTfc(TemplateIpsec4TunTfc, TestIpsec4TunProtect): + """IPsec IPv4 Tunnel protect with TFC - transport mode""" + + @tag_fixme_vpp_workers class TestIpsec4TunProtectUdp(TemplateIpsec, TemplateIpsec4TunProtect, IpsecTun4): - """IPsec IPv4 Tunnel protect - transport mode""" + """IPsec IPv4 UDP Tunnel protect - transport mode""" def setUp(self): super(TestIpsec4TunProtectUdp, self).setUp() @@ -2513,6 +2563,11 @@ class TestIpsec4TunProtectUdp(TemplateIpsec, TemplateIpsec4TunProtect, IpsecTun4 self.verify_keepalive(self.ipv4_params) +@tag_fixme_vpp_workers +class TestIpsec4TunProtectUdpTfc(TemplateIpsec4TunTfc, TestIpsec4TunProtectUdp): + """IPsec IPv4 UDP Tunnel protect with TFC - transport mode""" + + @tag_fixme_vpp_workers class TestIpsec4TunProtectTun(TemplateIpsec, TemplateIpsec4TunProtect, IpsecTun4): """IPsec IPv4 Tunnel protect - tunnel mode""" @@ -2787,6 +2842,11 @@ class TestIpsec6TunProtect(TemplateIpsec, TemplateIpsec6TunProtect, IpsecTun6): self.unconfig_network(p) +@tag_fixme_vpp_workers +class TestIpsec6TunProtectTfc(TemplateIpsec6TunTfc, TestIpsec6TunProtect): + """IPsec IPv6 Tunnel protect with TFC - transport mode""" + + @tag_fixme_vpp_workers class TestIpsec6TunProtectTun(TemplateIpsec, TemplateIpsec6TunProtect, IpsecTun6): """IPsec IPv6 Tunnel protect - tunnel mode""" @@ -3203,6 +3263,11 @@ class TestIpsecItf4(TemplateIpsec, TemplateIpsecItf4, IpsecTun4): self.unconfig_network(p) +@tag_fixme_vpp_workers +class TestIpsecItf4Tfc(TemplateIpsec4TunTfc, TestIpsecItf4): + """IPsec Interface IPv4 with TFC""" + + class TestIpsecItf4MPLS(TemplateIpsec, TemplateIpsecItf4, IpsecTun4): """IPsec Interface MPLSoIPv4""" @@ -3515,6 +3580,11 @@ class TestIpsecItf6(TemplateIpsec, TemplateIpsecItf6, IpsecTun6): self.unconfig_network(p) +@tag_fixme_vpp_workers +class TestIpsecItf6Tfc(TemplateIpsec6TunTfc, TestIpsecItf6): + """IPsec Interface IPv6 with TFC""" + + class TestIpsecMIfEsp4(TemplateIpsec, IpsecTun4): """Ipsec P2MP ESP v4 tests""" -- 2.16.6