Allow IPsec interface to have SAs reset 94/8794/5
authorMatthew Smith <mgsmith@netgate.com>
Thu, 12 Oct 2017 17:06:59 +0000 (12:06 -0500)
committerSergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
Thu, 26 Oct 2017 13:48:54 +0000 (13:48 +0000)
Make it easier to integrate with external IKE daemon.
IPsec interfaces can have one or both SAs replaced after
creation. This allows for the possibility of setting a
new child SA on an interface when rekeying occurs. It also
allows for the possibility of creating an interface ahead
of time and updating the SA when parameters that are
negotiated during IKE exchange become known.

Change-Id: I0a31afdcc2bdff7098a924a51abbc58bdab2bd08
Signed-off-by: Matthew Smith <mgsmith@netgate.com>
src/vat/api_format.c
src/vnet/ipsec/ipsec.api
src/vnet/ipsec/ipsec.c
src/vnet/ipsec/ipsec.h
src/vnet/ipsec/ipsec_api.c
src/vnet/ipsec/ipsec_if.c

index 08d0c2e..d8f7209 100644 (file)
@@ -5107,6 +5107,7 @@ _(ipsec_sad_add_del_entry_reply)                        \
 _(ipsec_sa_set_key_reply)                               \
 _(ipsec_tunnel_if_add_del_reply)                        \
 _(ipsec_tunnel_if_set_key_reply)                        \
+_(ipsec_tunnel_if_set_sa_reply)                         \
 _(ikev2_profile_add_del_reply)                          \
 _(ikev2_profile_set_auth_reply)                         \
 _(ikev2_profile_set_id_reply)                           \
@@ -5341,6 +5342,7 @@ _(IPSEC_SA_DETAILS, ipsec_sa_details)                                   \
 _(IPSEC_SA_SET_KEY_REPLY, ipsec_sa_set_key_reply)                       \
 _(IPSEC_TUNNEL_IF_ADD_DEL_REPLY, ipsec_tunnel_if_add_del_reply)         \
 _(IPSEC_TUNNEL_IF_SET_KEY_REPLY, ipsec_tunnel_if_set_key_reply)         \
+_(IPSEC_TUNNEL_IF_SET_SA_REPLY, ipsec_tunnel_if_set_sa_reply)           \
 _(IKEV2_PROFILE_ADD_DEL_REPLY, ikev2_profile_add_del_reply)             \
 _(IKEV2_PROFILE_SET_AUTH_REPLY, ikev2_profile_set_auth_reply)           \
 _(IKEV2_PROFILE_SET_ID_REPLY, ikev2_profile_set_id_reply)               \
@@ -14401,6 +14403,57 @@ api_ipsec_tunnel_if_set_key (vat_main_t * vam)
   return ret;
 }
 
+static int
+api_ipsec_tunnel_if_set_sa (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_ipsec_tunnel_if_set_sa_t *mp;
+  u32 sw_if_index = ~0;
+  u32 sa_id = ~0;
+  u8 is_outbound = (u8) ~ 0;
+  int ret;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "%U", api_unformat_sw_if_index, vam, &sw_if_index))
+       ;
+      else if (unformat (i, "sa_id %d", &sa_id))
+       ;
+      else if (unformat (i, "outbound"))
+       is_outbound = 1;
+      else if (unformat (i, "inbound"))
+       is_outbound = 0;
+      else
+       {
+         clib_warning ("parse error '%U'", format_unformat_error, i);
+         return -99;
+       }
+    }
+
+  if (sw_if_index == ~0)
+    {
+      errmsg ("interface must be specified");
+      return -99;
+    }
+
+  if (sa_id == ~0)
+    {
+      errmsg ("SA ID must be specified");
+      return -99;
+    }
+
+  M (IPSEC_TUNNEL_IF_SET_SA, mp);
+
+  mp->sw_if_index = htonl (sw_if_index);
+  mp->sa_id = htonl (sa_id);
+  mp->is_outbound = is_outbound;
+
+  S (mp);
+  W (ret);
+
+  return ret;
+}
+
 static int
 api_ikev2_profile_add_del (vat_main_t * vam)
 {
@@ -21708,6 +21761,7 @@ _(ipsec_tunnel_if_add_del, "local_spi <n> remote_spi <n>\n"             \
 _(ipsec_sa_dump, "[sa_id <n>]")                                         \
 _(ipsec_tunnel_if_set_key, "<intfc> <local|remote> <crypto|integ>\n"    \
   "  <alg> <hex>\n")                                                    \
+_(ipsec_tunnel_if_set_sa, "<intfc> sa_id <n> <inbound|outbound>\n")     \
 _(ikev2_profile_add_del, "name <profile_name> [del]")                   \
 _(ikev2_profile_set_auth, "name <profile_name> auth_method <method>\n"  \
   "(auth_data 0x<data> | auth_data <data>)")                            \
index 67c3336..1b2e4bd 100644 (file)
@@ -157,6 +157,7 @@ autoreply define ipsec_sad_add_del_entry
   u8 integrity_key[128];
 
   u8 use_extended_sequence_number;
+  u8 use_anti_replay;
 
   u8 is_tunnel;
   u8 is_tunnel_ipv6;
@@ -634,6 +635,21 @@ autoreply define ipsec_tunnel_if_set_key {
   u8 key[128];
 };
 
+/** \brief Set new SA on IPsec interface
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - index of tunnel interface
+    @param sa_id - ID of SA to use
+    @param is_outbound - 1 if outbound (local) SA, 0 if inbound (remote)
+*/
+autoreply define ipsec_tunnel_if_set_sa {
+  u32 client_index;
+  u32 context;
+  u32 sw_if_index;
+  u32 sa_id;
+  u8 is_outbound;
+};
+
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
index 2adcfd0..cd05c1b 100644 (file)
@@ -379,7 +379,7 @@ ipsec_add_del_policy (vlib_main_t * vm, ipsec_policy_t * policy, int is_add)
   return 0;
 }
 
-static u8
+u8
 ipsec_is_sa_used (u32 sa_index)
 {
   ipsec_main_t *im = &ipsec_main;
index 222025d..1572e55 100644 (file)
@@ -306,6 +306,7 @@ int ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add);
 int ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update);
 
 u32 ipsec_get_sa_index_by_sa_id (u32 sa_id);
+u8 ipsec_is_sa_used (u32 sa_index);
 u8 *format_ipsec_if_output_trace (u8 * s, va_list * args);
 u8 *format_ipsec_policy_action (u8 * s, va_list * args);
 u8 *format_ipsec_crypto_alg (u8 * s, va_list * args);
@@ -324,6 +325,8 @@ int ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm,
                                    args);
 int ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index,
                             ipsec_if_set_key_type_t type, u8 alg, u8 * key);
+int ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id,
+                           u8 is_outbound);
 
 
 /*
index c3f5745..e96da81 100644 (file)
@@ -57,6 +57,7 @@ _(IPSEC_SA_DUMP, ipsec_sa_dump)                                         \
 _(IPSEC_SPD_DUMP, ipsec_spd_dump)                                       \
 _(IPSEC_TUNNEL_IF_ADD_DEL, ipsec_tunnel_if_add_del)                     \
 _(IPSEC_TUNNEL_IF_SET_KEY, ipsec_tunnel_if_set_key)                     \
+_(IPSEC_TUNNEL_IF_SET_SA, ipsec_tunnel_if_set_sa)                       \
 _(IKEV2_PROFILE_ADD_DEL, ikev2_profile_add_del)                         \
 _(IKEV2_PROFILE_SET_AUTH, ikev2_profile_set_auth)                       \
 _(IKEV2_PROFILE_SET_ID, ikev2_profile_set_id)                           \
@@ -236,6 +237,7 @@ static void vl_api_ipsec_sad_add_del_entry_t_handler
       clib_memcpy (&sa.tunnel_src_addr.ip4.data, mp->tunnel_src_address, 4);
       clib_memcpy (&sa.tunnel_dst_addr.ip4.data, mp->tunnel_dst_address, 4);
     }
+  sa.use_anti_replay = mp->use_anti_replay;
 
   ASSERT (im->cb.check_support_cb);
   clib_error_t *err = im->cb.check_support_cb (&sa);
@@ -565,6 +567,28 @@ out:
 }
 
 
+static void
+vl_api_ipsec_tunnel_if_set_sa_t_handler (vl_api_ipsec_tunnel_if_set_sa_t * mp)
+{
+  vl_api_ipsec_tunnel_if_set_sa_reply_t *rmp;
+  ipsec_main_t *im = &ipsec_main;
+  vnet_main_t *vnm = im->vnet_main;
+  vnet_sw_interface_t *sw;
+  int rv;
+
+#if WITH_LIBSSL > 0
+  sw = vnet_get_sw_interface (vnm, ntohl (mp->sw_if_index));
+
+  rv = ipsec_set_interface_sa (vnm, sw->hw_if_index, ntohl (mp->sa_id),
+                              mp->is_outbound);
+#else
+  clib_warning ("unimplemented");
+#endif
+
+  REPLY_MACRO (VL_API_IPSEC_TUNNEL_IF_SET_SA_REPLY);
+}
+
+
 static void
 vl_api_ikev2_profile_add_del_t_handler (vl_api_ikev2_profile_add_del_t * mp)
 {
index 9745534..5a0d489 100644 (file)
@@ -399,6 +399,85 @@ ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index,
 }
 
 
+int
+ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id,
+                       u8 is_outbound)
+{
+  ipsec_main_t *im = &ipsec_main;
+  vnet_hw_interface_t *hi;
+  ipsec_tunnel_if_t *t;
+  ipsec_sa_t *sa, *old_sa;
+  u32 sa_index, old_sa_index;
+  uword *p;
+
+  hi = vnet_get_hw_interface (vnm, hw_if_index);
+  t = pool_elt_at_index (im->tunnel_interfaces, hi->dev_instance);
+
+  sa_index = ipsec_get_sa_index_by_sa_id (sa_id);
+  if (sa_index == ~0)
+    {
+      clib_warning ("SA with ID %u not found", sa_id);
+      return VNET_API_ERROR_INVALID_VALUE;
+    }
+
+  if (ipsec_is_sa_used (sa_index))
+    {
+      clib_warning ("SA with ID %u is already in use", sa_id);
+      return VNET_API_ERROR_INVALID_VALUE;
+    }
+
+  sa = pool_elt_at_index (im->sad, sa_index);
+  if (sa->is_tunnel_ip6)
+    {
+      clib_warning ("IPsec interface not supported with IPv6 endpoints");
+      return VNET_API_ERROR_UNIMPLEMENTED;
+    }
+
+  if (!is_outbound)
+    {
+      u64 key;
+
+      old_sa_index = t->input_sa_index;
+      old_sa = pool_elt_at_index (im->sad, old_sa_index);
+
+      /* unset old inbound hash entry. packets should stop arriving */
+      key =
+       (u64) old_sa->tunnel_dst_addr.ip4.as_u32 << 32 | (u64) old_sa->spi;
+      p = hash_get (im->ipsec_if_pool_index_by_key, key);
+      if (p)
+       hash_unset (im->ipsec_if_pool_index_by_key, key);
+
+      /* set new inbound SA, then set new hash entry */
+      t->input_sa_index = sa_index;
+      key = (u64) sa->tunnel_dst_addr.ip4.as_u32 << 32 | (u64) sa->spi;
+      hash_set (im->ipsec_if_pool_index_by_key, key, hi->dev_instance);
+    }
+  else
+    {
+      old_sa_index = t->output_sa_index;
+      old_sa = pool_elt_at_index (im->sad, old_sa_index);
+      t->output_sa_index = sa_index;
+    }
+
+  /* remove sa_id to sa_index mapping on old SA */
+  if (ipsec_get_sa_index_by_sa_id (old_sa->id) == old_sa_index)
+    hash_unset (im->sa_index_by_sa_id, old_sa->id);
+
+  if (im->cb.add_del_sa_sess_cb)
+    {
+      clib_error_t *err;
+
+      err = im->cb.add_del_sa_sess_cb (old_sa_index, 0);
+      if (err)
+       return VNET_API_ERROR_SYSCALL_ERROR_1;
+    }
+
+  pool_put (im->sad, old_sa);
+
+  return 0;
+}
+
+
 clib_error_t *
 ipsec_tunnel_if_init (vlib_main_t * vm)
 {