ipsec: new api for sa ips and ports updates
[vpp.git] / src / vnet / ipsec / ipsec_sa.c
index b1e3374..295323b 100644 (file)
@@ -28,6 +28,10 @@ vlib_combined_counter_main_t ipsec_sa_counters = {
   .name = "SA",
   .stat_segment_name = "/net/ipsec/sa",
 };
+vlib_simple_counter_main_t ipsec_sa_lost_counters = {
+  .name = "SA-lost",
+  .stat_segment_name = "/net/ipsec/sa/lost",
+};
 
 ipsec_sa_t *ipsec_sa_pool;
 
@@ -100,7 +104,8 @@ ipsec_sa_set_crypto_alg (ipsec_sa_t * sa, ipsec_crypto_alg_t crypto_alg)
   sa->crypto_calg = im->crypto_algs[crypto_alg].alg;
   ASSERT (sa->crypto_iv_size <= ESP_MAX_IV_SIZE);
   ASSERT (sa->esp_block_align <= ESP_MAX_BLOCK_SIZE);
-  if (IPSEC_CRYPTO_ALG_IS_GCM (crypto_alg))
+  if (IPSEC_CRYPTO_ALG_IS_GCM (crypto_alg) ||
+      IPSEC_CRYPTO_ALG_CTR_AEAD_OTHERS (crypto_alg))
     {
       sa->integ_icv_size = im->crypto_algs[crypto_alg].icv_size;
       ipsec_sa_set_IS_CTR (sa);
@@ -166,6 +171,137 @@ ipsec_sa_set_async_op_ids (ipsec_sa_t * sa)
   /* *INDENT-ON* */
 }
 
+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,
@@ -193,6 +329,8 @@ ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
 
   vlib_validate_combined_counter (&ipsec_sa_counters, sa_index);
   vlib_zero_combined_counter (&ipsec_sa_counters, sa_index);
+  vlib_validate_simple_counter (&ipsec_sa_lost_counters, sa_index);
+  vlib_zero_simple_counter (&ipsec_sa_lost_counters, sa_index);
 
   tunnel_copy (tun, &sa->tunnel);
   sa->id = id;
@@ -270,6 +408,10 @@ ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
       return VNET_API_ERROR_SYSCALL_ERROR_1;
     }
 
+  if (ipsec_sa_is_set_IS_TUNNEL (sa) &&
+      AF_IP6 == ip_addr_version (&tun->t_src))
+    ipsec_sa_set_IS_TUNNEL_V6 (sa);
+
   if (ipsec_sa_is_set_IS_TUNNEL (sa) && !ipsec_sa_is_set_IS_INBOUND (sa))
     {
       sa->tunnel_flags = sa->tunnel.t_encap_decap_flags;
@@ -315,7 +457,8 @@ ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
        sa->udp_hdr.src_port = clib_host_to_net_u16 (src_port);
 
       if (ipsec_sa_is_set_IS_INBOUND (sa))
-       ipsec_register_udp_port (clib_host_to_net_u16 (sa->udp_hdr.dst_port));
+       ipsec_register_udp_port (clib_host_to_net_u16 (sa->udp_hdr.dst_port),
+                                !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
     }
 
   hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
@@ -343,7 +486,8 @@ ipsec_sa_del (ipsec_sa_t * sa)
   if (ipsec_sa_is_set_IS_ASYNC (sa))
     vnet_crypto_request_async_mode (0);
   if (ipsec_sa_is_set_UDP_ENCAP (sa) && ipsec_sa_is_set_IS_INBOUND (sa))
-    ipsec_unregister_udp_port (clib_net_to_host_u16 (sa->udp_hdr.dst_port));
+    ipsec_unregister_udp_port (clib_net_to_host_u16 (sa->udp_hdr.dst_port),
+                              !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
 
   if (ipsec_sa_is_set_IS_TUNNEL (sa) && !ipsec_sa_is_set_IS_INBOUND (sa))
     dpo_reset (&sa->dpo);
@@ -418,6 +562,7 @@ void
 ipsec_sa_clear (index_t sai)
 {
   vlib_zero_combined_counter (&ipsec_sa_counters, sai);
+  vlib_zero_simple_counter (&ipsec_sa_lost_counters, sai);
 }
 
 void