diff options
Diffstat (limited to 'src/vnet/ipsec/ipsec_sa.c')
-rw-r--r-- | src/vnet/ipsec/ipsec_sa.c | 131 |
1 files changed, 131 insertions, 0 deletions
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, |