summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur de Kerhor <arthurdekerhor@gmail.com>2022-08-31 19:13:03 +0200
committerBeno�t Ganne <bganne@cisco.com>2022-12-16 10:13:24 +0000
commit4117b24acb4241d7f2ef38248bc254f6a4a7b422 (patch)
tree982eb58cbe8cfd0d43e2a09e129d129c1a8a5511
parent863d1c8711018ded9bd4db5f27851a0b8ac45836 (diff)
ipsec: new api for sa ips and ports updates
Useful to update the tunnel paramaters and udp ports (NAT-T) of an SA without having to rekey. Could be done by deleting and re-adding the SA but it would not preserve the anti-replay window if there is one. Use case: a nat update/reboot between the 2 endpoints of the tunnel. Type: feature Change-Id: Icf5c0aac218603e8aa9a008ed6f614e4a6db59a0 Signed-off-by: Arthur de Kerhor <arthurdekerhor@gmail.com>
-rw-r--r--src/vnet/ipsec/ipsec.api22
-rw-r--r--src/vnet/ipsec/ipsec_api.c25
-rw-r--r--src/vnet/ipsec/ipsec_sa.c131
-rw-r--r--src/vnet/ipsec/ipsec_sa.h2
-rw-r--r--src/vnet/ipsec/ipsec_test.c6
-rw-r--r--test/template_ipsec.py2
-rw-r--r--test/test_ipsec_tun_if_esp.py45
-rw-r--r--test/vpp_ipsec.py20
8 files changed, 247 insertions, 6 deletions
diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api
index 56ad646d001..6cbad6e74fa 100644
--- a/src/vnet/ipsec/ipsec.api
+++ b/src/vnet/ipsec/ipsec.api
@@ -201,6 +201,28 @@ autoreply define ipsec_sad_entry_del
u32 id;
};
+/** \brief An API to update the tunnel parameters and the ports associated with an SA
+
+ Used in the NAT-T case when the NAT data changes
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sa_id - the id of the SA to update
+ @param is_tun - update the tunnel if non-zero, else update only the ports
+ @param tunnel - sender context, to match reply w/ request
+ @param udp_src_port - new src port for NAT-T. Used if different from 0xffff
+ @param udp_dst_port - new dst port for NAT-T. Used if different from 0xffff
+ */
+autoreply define ipsec_sad_entry_update
+{
+ u32 client_index;
+ u32 context;
+ u32 sad_id;
+ bool is_tun;
+ vl_api_tunnel_t tunnel;
+ u16 udp_src_port [default=0xffff];
+ u16 udp_dst_port [default=0xffff];
+};
+
define ipsec_sad_entry_add_del_reply
{
option deprecated;
diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c
index 378f493ec14..3994150d895 100644
--- a/src/vnet/ipsec/ipsec_api.c
+++ b/src/vnet/ipsec/ipsec_api.c
@@ -568,6 +568,31 @@ vl_api_ipsec_sad_entry_add_t_handler (vl_api_ipsec_sad_entry_add_t *mp)
}
static void
+vl_api_ipsec_sad_entry_update_t_handler (vl_api_ipsec_sad_entry_update_t *mp)
+{
+ vl_api_ipsec_sad_entry_update_reply_t *rmp;
+ u32 id;
+ tunnel_t tun = { 0 };
+ int rv;
+
+ id = ntohl (mp->sad_id);
+
+ if (mp->is_tun)
+ {
+ rv = tunnel_decode (&mp->tunnel, &tun);
+
+ if (rv)
+ goto out;
+ }
+
+ rv = ipsec_sa_update (id, htons (mp->udp_src_port), htons (mp->udp_dst_port),
+ &tun, mp->is_tun);
+
+out:
+ REPLY_MACRO (VL_API_IPSEC_SAD_ENTRY_UPDATE_REPLY);
+}
+
+static void
send_ipsec_spds_details (ipsec_spd_t * spd, vl_api_registration_t * reg,
u32 context)
{
diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c
index a330abcb244..295323b8f7e 100644
--- a/src/vnet/ipsec/ipsec_sa.c
+++ b/src/vnet/ipsec/ipsec_sa.c
@@ -172,6 +172,137 @@ ipsec_sa_set_async_op_ids (ipsec_sa_t * sa)
}
int
+ipsec_sa_update (u32 id, u16 src_port, u16 dst_port, const tunnel_t *tun,
+ bool is_tun)
+{
+ ipsec_main_t *im = &ipsec_main;
+ ipsec_sa_t *sa;
+ u32 sa_index;
+ uword *p;
+ int rv;
+
+ p = hash_get (im->sa_index_by_sa_id, id);
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ sa = ipsec_sa_get (p[0]);
+ sa_index = sa - ipsec_sa_pool;
+
+ if (is_tun && ipsec_sa_is_set_IS_TUNNEL (sa) &&
+ (ip_address_cmp (&tun->t_src, &sa->tunnel.t_src) != 0 ||
+ ip_address_cmp (&tun->t_dst, &sa->tunnel.t_dst) != 0))
+ {
+ /* if the source IP is updated for an inbound SA under a tunnel protect,
+ we need to update the tun_protect DB with the new src IP */
+ if (ipsec_sa_is_set_IS_INBOUND (sa) &&
+ ip_address_cmp (&tun->t_src, &sa->tunnel.t_src) != 0 &&
+ !ip46_address_is_zero (&tun->t_src.ip))
+ {
+ if (ip46_address_is_ip4 (&sa->tunnel.t_src.ip))
+ {
+ ipsec4_tunnel_kv_t old_key, new_key;
+ clib_bihash_kv_8_16_t res,
+ *bkey = (clib_bihash_kv_8_16_t *) &old_key;
+
+ ipsec4_tunnel_mk_key (&old_key, &sa->tunnel.t_src.ip.ip4,
+ clib_host_to_net_u32 (sa->spi));
+ ipsec4_tunnel_mk_key (&new_key, &tun->t_src.ip.ip4,
+ clib_host_to_net_u32 (sa->spi));
+
+ if (!clib_bihash_search_8_16 (&im->tun4_protect_by_key, bkey,
+ &res))
+ {
+ clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, &res, 0);
+ res.key = new_key.key;
+ clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, &res, 1);
+ }
+ }
+ else
+ {
+ ipsec6_tunnel_kv_t old_key = {
+ .key = {
+ .remote_ip = sa->tunnel.t_src.ip.ip6,
+ .spi = clib_host_to_net_u32 (sa->spi),
+ },
+ }, new_key = {
+ .key = {
+ .remote_ip = tun->t_src.ip.ip6,
+ .spi = clib_host_to_net_u32 (sa->spi),
+ }};
+ clib_bihash_kv_24_16_t res,
+ *bkey = (clib_bihash_kv_24_16_t *) &old_key;
+
+ if (!clib_bihash_search_24_16 (&im->tun6_protect_by_key, bkey,
+ &res))
+ {
+ clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, &res,
+ 0);
+ clib_memcpy (&res.key, &new_key.key, 3);
+ clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, &res,
+ 1);
+ }
+ }
+ }
+ tunnel_unresolve (&sa->tunnel);
+ tunnel_copy (tun, &sa->tunnel);
+ if (!ipsec_sa_is_set_IS_INBOUND (sa))
+ {
+ dpo_reset (&sa->dpo);
+
+ sa->tunnel_flags = sa->tunnel.t_encap_decap_flags;
+
+ rv = tunnel_resolve (&sa->tunnel, FIB_NODE_TYPE_IPSEC_SA, sa_index);
+
+ if (rv)
+ {
+ hash_unset (im->sa_index_by_sa_id, sa->id);
+ pool_put (ipsec_sa_pool, sa);
+ return rv;
+ }
+ ipsec_sa_stack (sa);
+ /* generate header templates */
+ if (ipsec_sa_is_set_IS_TUNNEL_V6 (sa))
+ {
+ tunnel_build_v6_hdr (&sa->tunnel,
+ (ipsec_sa_is_set_UDP_ENCAP (sa) ?
+ IP_PROTOCOL_UDP :
+ IP_PROTOCOL_IPSEC_ESP),
+ &sa->ip6_hdr);
+ }
+ else
+ {
+ tunnel_build_v4_hdr (&sa->tunnel,
+ (ipsec_sa_is_set_UDP_ENCAP (sa) ?
+ IP_PROTOCOL_UDP :
+ IP_PROTOCOL_IPSEC_ESP),
+ &sa->ip4_hdr);
+ }
+ }
+ }
+
+ if (ipsec_sa_is_set_UDP_ENCAP (sa))
+ {
+ if (dst_port != IPSEC_UDP_PORT_NONE &&
+ dst_port != clib_net_to_host_u16 (sa->udp_hdr.dst_port))
+ {
+ if (ipsec_sa_is_set_IS_INBOUND (sa))
+ {
+ ipsec_unregister_udp_port (
+ clib_net_to_host_u16 (sa->udp_hdr.dst_port),
+ !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
+ ipsec_register_udp_port (dst_port,
+ !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
+ }
+ sa->udp_hdr.dst_port = clib_host_to_net_u16 (dst_port);
+ }
+ if (src_port != IPSEC_UDP_PORT_NONE &&
+ src_port != clib_net_to_host_u16 (sa->udp_hdr.src_port))
+ sa->udp_hdr.src_port = clib_host_to_net_u16 (src_port);
+ }
+ return (0);
+}
+
+int
ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
ipsec_crypto_alg_t crypto_alg, const ipsec_key_t *ck,
ipsec_integ_alg_t integ_alg, const ipsec_key_t *ik,
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index 057e8cd9bff..df079b13872 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -270,6 +270,8 @@ extern vlib_simple_counter_main_t ipsec_sa_lost_counters;
extern void ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len);
+extern int ipsec_sa_update (u32 id, u16 src_port, u16 dst_port,
+ const tunnel_t *tun, bool is_tun);
extern int
ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
ipsec_crypto_alg_t crypto_alg, const ipsec_key_t *ck,
diff --git a/src/vnet/ipsec/ipsec_test.c b/src/vnet/ipsec/ipsec_test.c
index f1436193636..16d86427ea7 100644
--- a/src/vnet/ipsec/ipsec_test.c
+++ b/src/vnet/ipsec/ipsec_test.c
@@ -307,6 +307,12 @@ api_ipsec_sad_entry_add_del_v3 (vat_main_t *vat)
}
static int
+api_ipsec_sad_entry_update (vat_main_t *vat)
+{
+ return -1;
+}
+
+static int
api_ipsec_tunnel_protect_update (vat_main_t *vat)
{
return -1;
diff --git a/test/template_ipsec.py b/test/template_ipsec.py
index 9d9ea3a86d3..d00216c7308 100644
--- a/test/template_ipsec.py
+++ b/test/template_ipsec.py
@@ -1291,7 +1291,7 @@ class IpsecTun4(object):
decrypt_pkts = []
for rx in rxs:
if p.nat_header:
- self.assertEqual(rx[UDP].dport, 4500)
+ self.assertEqual(rx[UDP].dport, p.nat_header.dport)
self.assert_packet_checksums_valid(rx)
self.assertEqual(len(rx) - len(Ether()), rx[IP].len)
try:
diff --git a/test/test_ipsec_tun_if_esp.py b/test/test_ipsec_tun_if_esp.py
index 61a66d40a4e..fe05f98e6e6 100644
--- a/test/test_ipsec_tun_if_esp.py
+++ b/test/test_ipsec_tun_if_esp.py
@@ -300,7 +300,7 @@ class TemplateIpsec4TunIfEspUdp(TemplateIpsec4TunProtect, TemplateIpsec):
# 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)
+ self.assert_equal(rx[UDP].dport, p.nat_header.dport)
pkt = sa.decrypt(rx[IP])
if not pkt.haslayer(IP):
@@ -344,7 +344,8 @@ class TemplateIpsec4TunIfEspUdp(TemplateIpsec4TunProtect, TemplateIpsec):
p.crypt_algo_vpp_id,
p.crypt_key,
self.vpp_esp_protocol,
- flags=p.flags,
+ flags=p.flags
+ | VppEnum.vl_api_ipsec_sad_flags_t.IPSEC_API_SAD_FLAG_IS_INBOUND,
udp_src=p.nat_header.sport,
udp_dst=p.nat_header.dport,
)
@@ -429,6 +430,24 @@ class TestIpsec4TunIfEspUdpGCM(TemplateIpsec4TunIfEspUdp, IpsecTun4Tests):
p.salt = 0
+class TestIpsec4TunIfEspUdpUpdate(TemplateIpsec4TunIfEspUdp, IpsecTun4Tests):
+ """Ipsec ESP UDP update tests"""
+
+ tun4_input_node = "ipsec4-tun-input"
+
+ def setUp(self):
+ super(TestIpsec4TunIfEspUdpUpdate, self).setUp()
+ p = self.ipv4_params
+ p.nat_header = UDP(sport=6565, dport=7676)
+ config_tun_params(p, self.encryption_type, p.tun_if)
+ p.tun_sa_in.update_vpp_config(
+ udp_src=p.nat_header.dport, udp_dst=p.nat_header.sport
+ )
+ p.tun_sa_out.update_vpp_config(
+ udp_src=p.nat_header.sport, udp_dst=p.nat_header.dport
+ )
+
+
class TestIpsec4TunIfEsp2(TemplateIpsec4TunIfEsp, IpsecTcpTests):
"""Ipsec ESP - TCP tests"""
@@ -583,7 +602,7 @@ class TemplateIpsec6TunIfEspUdp(TemplateIpsec6TunProtect, TemplateIpsec):
# 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)
+ self.assert_equal(rx[UDP].dport, p.nat_header.dport)
pkt = sa.decrypt(rx[IP])
if not pkt.haslayer(IP):
@@ -629,7 +648,8 @@ class TemplateIpsec6TunIfEspUdp(TemplateIpsec6TunProtect, TemplateIpsec):
p.crypt_algo_vpp_id,
p.crypt_key,
self.vpp_esp_protocol,
- flags=p.flags,
+ flags=p.flags
+ | VppEnum.vl_api_ipsec_sad_flags_t.IPSEC_API_SAD_FLAG_IS_INBOUND,
udp_src=p.nat_header.sport,
udp_dst=p.nat_header.dport,
)
@@ -2957,7 +2977,8 @@ class TemplateIpsecItf4(object):
self.vpp_esp_protocol,
dst,
src,
- flags=p.flags,
+ flags=p.flags
+ | VppEnum.vl_api_ipsec_sad_flags_t.IPSEC_API_SAD_FLAG_IS_INBOUND,
)
p.tun_sa_in.add_vpp_config()
@@ -3063,6 +3084,20 @@ class TestIpsecItf4(TemplateIpsec, TemplateIpsecItf4, IpsecTun4):
self.tun4_encrypt_node_name = "esp4-encrypt-tun"
+ # update the SA tunnel
+ config_tun_params(
+ p, self.encryption_type, None, self.pg2.local_ip4, self.pg2.remote_ip4
+ )
+ p.tun_sa_in.update_vpp_config(
+ is_tun=True, tun_src=self.pg2.remote_ip4, tun_dst=self.pg2.local_ip4
+ )
+ p.tun_sa_out.update_vpp_config(
+ is_tun=True, tun_src=self.pg2.local_ip4, tun_dst=self.pg2.remote_ip4
+ )
+ self.verify_tun_44(p, count=n_pkts)
+ self.assertEqual(p.tun_if.get_rx_stats(), 5 * n_pkts)
+ self.assertEqual(p.tun_if.get_tx_stats(), 4 * n_pkts)
+
self.vapi.cli("clear interfaces")
# rekey - create new SAs and update the tunnel protection
diff --git a/test/vpp_ipsec.py b/test/vpp_ipsec.py
index eb0209fc57a..f50d491c396 100644
--- a/test/vpp_ipsec.py
+++ b/test/vpp_ipsec.py
@@ -295,6 +295,26 @@ class VppIpsecSA(VppObject):
self.test.registry.register(self, self.test.logger)
return self
+ def update_vpp_config(
+ self, udp_src=None, udp_dst=None, is_tun=False, tun_src=None, tun_dst=None
+ ):
+ if is_tun:
+ if tun_src:
+ self.tun_src = ip_address(text_type(tun_src))
+ if tun_dst:
+ self.tun_dst = ip_address(text_type(tun_dst))
+ if udp_src:
+ self.udp_src = udp_src
+ if udp_dst:
+ self.udp_dst = udp_dst
+ self.test.vapi.ipsec_sad_entry_update(
+ sad_id=self.id,
+ is_tun=is_tun,
+ tunnel=self.tunnel_encode(),
+ udp_src_port=udp_src,
+ udp_dst_port=udp_dst,
+ )
+
def remove_vpp_config(self):
self.test.vapi.ipsec_sad_entry_del(id=self.id)