ikev2: add support for custom ipsec-over-udp port 73/25573/4
authorFilip Tehlar <ftehlar@cisco.com>
Mon, 2 Mar 2020 15:17:37 +0000 (15:17 +0000)
committerDamjan Marion <dmarion@me.com>
Sat, 21 Mar 2020 11:50:03 +0000 (11:50 +0000)
Type: feature

Change-Id: Ifee2b3dca85ea915067b9285e3636802bf0c19a8
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
12 files changed:
src/plugins/ikev2/ikev2.api
src/plugins/ikev2/ikev2.c
src/plugins/ikev2/ikev2.h
src/plugins/ikev2/ikev2_api.c
src/plugins/ikev2/ikev2_cli.c
src/plugins/ikev2/ikev2_priv.h
src/plugins/ikev2/ikev2_test.c
src/vnet/api_errno.h
src/vnet/ipsec/ipsec_api.c
src/vnet/ipsec/ipsec_cli.c
src/vnet/ipsec/ipsec_sa.c
src/vnet/ipsec/ipsec_sa.h

index c872c6f..274d496 100644 (file)
@@ -330,6 +330,22 @@ autoreply define ikev2_profile_set_udp_encap
   string name[64];
 };
 
+/** \brief IKEv2: Set/unset custom ipsec-over-udp port
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_set - whether set or unset custom port
+    @port - port number
+    @param name - IKEv2 profile name
+*/
+autoreply define ikev2_profile_set_ipsec_udp_port
+{
+  u32 client_index;
+  u32 context;
+
+  u8 is_set;
+  u16 port;
+  string name[64];
+};
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
index 2dd0819..ea99e37 100644 (file)
@@ -399,6 +399,7 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai)
   sa->last_sa_init_req_packet_data = _(sai->last_sa_init_req_packet_data);
   sa->childs = _(sai->childs);
   sa->udp_encap = sai->udp_encap;
+  sa->dst_port = sai->dst_port;
 #undef _
 
 
@@ -1379,8 +1380,10 @@ ikev2_sa_auth (ikev2_sa_t * sa)
   /* *INDENT-ON* */
 
   if (sel_p)
-    sa->udp_encap = sel_p->udp_encap;
-
+    {
+      sa->udp_encap = sel_p->udp_encap;
+      sa->dst_port = sel_p->dst_port;
+    }
   vec_free (authmsg);
 
   if (sa->state == IKEV2_STATE_AUTHENTICATED)
@@ -1502,6 +1505,7 @@ typedef struct
   ipsec_key_t loc_ckey, rem_ckey, loc_ikey, rem_ikey;
   u8 is_rekey;
   u32 old_remote_sa_id;
+  u16 dst_port;
 } ikev2_add_ipsec_tunnel_args_t;
 
 static void
@@ -1559,13 +1563,13 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
                               IPSEC_PROTOCOL_ESP, a->encr_type,
                               &a->loc_ckey, a->integ_type, &a->loc_ikey,
                               a->flags, 0, a->salt_local, &a->local_ip,
-                              &a->remote_ip, NULL);
+                              &a->remote_ip, NULL, a->dst_port);
   rv |= ipsec_sa_add_and_lock (a->remote_sa_id, a->remote_spi,
                               IPSEC_PROTOCOL_ESP, a->encr_type, &a->rem_ckey,
                               a->integ_type, &a->rem_ikey,
                               (a->flags | IPSEC_SA_FLAG_IS_INBOUND), 0,
                               a->salt_remote, &a->remote_ip,
-                              &a->local_ip, NULL);
+                              &a->local_ip, NULL, a->dst_port);
 
   rv |= ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in);
 }
@@ -1799,6 +1803,7 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm,
   child->remote_sa_id = a.remote_sa_id = remote_sa_id;
 
   a.sw_if_index = (sa->is_tun_itf_set ? sa->tun_itf : ~0);
+  a.dst_port = sa->dst_port;
 
   vl_api_rpc_call_main_thread (ikev2_add_tunnel_from_main,
                               (u8 *) & a, sizeof (a));
@@ -2928,6 +2933,58 @@ ikev2_set_local_key (vlib_main_t * vm, u8 * file)
   return 0;
 }
 
+static_always_inline vnet_api_error_t
+ikev2_register_udp_port (ikev2_profile_t * p, u16 port)
+{
+  ikev2_main_t *km = &ikev2_main;
+  udp_dst_port_info_t *pi;
+
+  uword *v = hash_get (km->udp_ports, port);
+  pi = udp_get_dst_port_info (&udp_main, port, UDP_IP4);
+
+  if (v)
+    {
+      /* IKE already uses this port, only increment reference counter */
+      ASSERT (pi);
+      v[0]++;
+    }
+  else
+    {
+      if (pi)
+       return VNET_API_ERROR_UDP_PORT_TAKEN;
+
+      udp_register_dst_port (km->vlib_main, port,
+                            ipsec4_tun_input_node.index, 1);
+      hash_set (km->udp_ports, port, 1);
+    }
+  p->dst_port = port;
+  return 0;
+}
+
+static_always_inline void
+ikev2_unregister_udp_port (ikev2_profile_t * p)
+{
+  ikev2_main_t *km = &ikev2_main;
+  uword *v;
+
+  if (p->dst_port == IPSEC_UDP_PORT_NONE)
+    return;
+
+  v = hash_get (km->udp_ports, p->dst_port);
+  if (!v)
+    return;
+
+  v[0]--;
+
+  if (v[0] == 0)
+    {
+      udp_unregister_dst_port (km->vlib_main, p->dst_port, 1);
+      hash_unset (km->udp_ports, p->dst_port);
+    }
+
+  p->dst_port = IPSEC_UDP_PORT_NONE;
+}
+
 clib_error_t *
 ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add)
 {
@@ -2942,6 +2999,7 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add)
       pool_get (km->profiles, p);
       clib_memset (p, 0, sizeof (*p));
       p->name = vec_dup (name);
+      p->dst_port = IPSEC_UDP_PORT_NONE;
       p->responder.sw_if_index = ~0;
       p->tun_itf = ~0;
       uword index = p - km->profiles;
@@ -2953,6 +3011,8 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add)
       if (!p)
        return clib_error_return (0, "policy %v does not exists", name);
 
+      ikev2_unregister_udp_port (p);
+
       vec_free (p->name);
       pool_put (km->profiles, p);
       mhash_unset (&km->profile_index_by_name, name, 0);
@@ -3161,6 +3221,39 @@ ikev2_set_profile_tunnel_interface (vlib_main_t * vm,
   return 0;
 }
 
+vnet_api_error_t
+ikev2_set_profile_ipsec_udp_port (vlib_main_t * vm, u8 * name, u16 port,
+                                 u8 is_set)
+{
+  ikev2_profile_t *p = ikev2_profile_index_by_name (name);
+  ikev2_main_t *km = &ikev2_main;
+  vnet_api_error_t rv = 0;
+  uword *v;
+
+  if (!p)
+    return VNET_API_ERROR_INVALID_VALUE;
+
+  if (is_set)
+    {
+      if (p->dst_port != IPSEC_UDP_PORT_NONE)
+       return VNET_API_ERROR_VALUE_EXIST;
+
+      rv = ikev2_register_udp_port (p, port);
+    }
+  else
+    {
+      v = hash_get (km->udp_ports, port);
+      if (!v)
+       return VNET_API_ERROR_IKE_NO_PORT;
+
+      if (p->dst_port == IPSEC_UDP_PORT_NONE)
+       return VNET_API_ERROR_INVALID_VALUE;
+
+      ikev2_unregister_udp_port (p);
+    }
+  return rv;
+}
+
 clib_error_t *
 ikev2_set_profile_udp_encap (vlib_main_t * vm, u8 * name)
 {
@@ -3262,6 +3355,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
     sa.state = IKEV2_STATE_SA_INIT;
     sa.tun_itf = p->tun_itf;
     sa.udp_encap = p->udp_encap;
+    sa.dst_port = p->dst_port;
     sa.is_tun_itf_set = 1;
     sa.initial_contact = 1;
     ikev2_generate_sa_init_data (&sa);
@@ -3606,6 +3700,7 @@ ikev2_init (vlib_main_t * vm)
 
   km->sa_by_ispi = hash_create (0, sizeof (uword));
   km->sw_if_indices = hash_create (0, 0);
+  km->udp_ports = hash_create (0, sizeof (uword));
 
   udp_register_dst_port (vm, 500, ikev2_node.index, 1);
 
index c73df15..d79a031 100644 (file)
@@ -394,6 +394,9 @@ clib_error_t *ikev2_set_profile_sa_lifetime (vlib_main_t * vm, u8 * name,
                                             u32 handover, u64 maxdata);
 clib_error_t *ikev2_set_profile_tunnel_interface (vlib_main_t * vm, u8 * name,
                                                  u32 sw_if_index);
+vnet_api_error_t ikev2_set_profile_ipsec_udp_port (vlib_main_t * vm,
+                                                  u8 * name, u16 port,
+                                                  u8 is_set);
 clib_error_t *ikev2_set_profile_udp_encap (vlib_main_t * vm, u8 * name);
 clib_error_t *ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name);
 clib_error_t *ikev2_initiate_delete_child_sa (vlib_main_t * vm, u32 ispi);
index f846bfa..0064f57 100644 (file)
@@ -311,6 +311,30 @@ vl_api_ikev2_set_sa_lifetime_t_handler (vl_api_ikev2_set_sa_lifetime_t * mp)
   REPLY_MACRO (VL_API_IKEV2_SET_SA_LIFETIME_REPLY);
 }
 
+static void
+  vl_api_ikev2_profile_set_ipsec_udp_port_t_handler
+  (vl_api_ikev2_profile_set_ipsec_udp_port_t * mp)
+{
+  vl_api_ikev2_profile_set_ipsec_udp_port_reply_t *rmp;
+  int rv = 0;
+
+#if WITH_LIBSSL > 0
+  vlib_main_t *vm = vlib_get_main ();
+
+  u8 *tmp = format (0, "%s", mp->name);
+
+  rv =
+    ikev2_set_profile_ipsec_udp_port (vm, tmp,
+                                     clib_net_to_host_u16 (mp->port),
+                                     mp->is_set);
+  vec_free (tmp);
+#else
+  rv = VNET_API_ERROR_UNIMPLEMENTED;
+#endif
+
+  REPLY_MACRO (VL_API_IKEV2_PROFILE_SET_IPSEC_UDP_PORT_REPLY);
+}
+
 static void
   vl_api_ikev2_set_tunnel_interface_t_handler
   (vl_api_ikev2_set_tunnel_interface_t * mp)
index 1acae91..03f9efd 100644 (file)
@@ -17,6 +17,7 @@
 #include <vnet/pg/pg.h>
 #include <vppinfra/error.h>
 #include <vnet/udp/udp.h>
+#include <vnet/ipsec/ipsec_sa.h>
 #include <plugins/ikev2/ikev2.h>
 #include <plugins/ikev2/ikev2_priv.h>
 
@@ -391,6 +392,14 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm,
          r = ikev2_set_profile_udp_encap (vm, name);
          goto done;
        }
+      else if (unformat (line_input, "set %U ipsec-over-udp port %u",
+                        unformat_token, valid_chars, &name, &tmp1))
+       {
+         int rv = ikev2_set_profile_ipsec_udp_port (vm, name, tmp1, 1);
+         if (rv)
+           r = clib_error_return (0, "Error: %U", format_vnet_api_errno, rv);
+         goto done;
+       }
       else
        break;
     }
@@ -502,6 +511,9 @@ show_ikev2_profile_command_fn (vlib_main_t * vm,
                       format_vnet_sw_if_index_name, vnet_get_main(), p->tun_itf);
     if (p->udp_encap)
       vlib_cli_output(vm, "  udp-encap");
+
+    if (p->dst_port != IPSEC_UDP_PORT_NONE)
+      vlib_cli_output(vm, "  ipsec-over-udp port %d", p->dst_port);
   }));
   /* *INDENT-ON* */
 
index 1cae47b..d2f5f05 100644 (file)
@@ -358,6 +358,7 @@ typedef struct
   u64 lifetime_maxdata;
   u32 lifetime_jitter;
   u32 handover;
+  u16 dst_port;
 
   u32 tun_itf;
   u8 udp_encap;
@@ -424,6 +425,7 @@ typedef struct
   u8 is_tun_itf_set;
   u32 tun_itf;
   u8 udp_encap;
+  u16 dst_port;
 
   f64 old_id_expiration;
   u32 current_remote_id_mask;
@@ -481,6 +483,9 @@ typedef struct
 
   /* logging level */
   ikev2_log_level_t log_level;
+
+  /* custom ipsec-over-udp ports managed by ike */
+  uword *udp_ports;
 } ikev2_main_t;
 
 extern ikev2_main_t ikev2_main;
index 9ff4bbc..8be42fe 100644 (file)
@@ -99,6 +99,12 @@ static void vl_api_ikev2_plugin_get_version_reply_t_handler
   vam->result_ready = 1;
 }
 
+static int
+api_ikev2_profile_set_ipsec_udp_port (vat_main_t * vam)
+{
+  return 0;
+}
+
 static int
 api_ikev2_profile_add_del (vat_main_t * vam)
 {
index eb25f05..15b17a8 100644 (file)
@@ -153,7 +153,9 @@ _(FIB_PATH_UNSUPPORTED_NH_PROTO, -157, "Unsupported FIB Path protocol") \
 _(API_ENDIAN_FAILED, -159, "Endian mismatch detected")                 \
 _(NO_CHANGE, -160, "No change in table")                               \
 _(MISSING_CERT_KEY, -161, "Missing certifcate or key")                  \
-_(LIMIT_EXCEEDED, -162, "limit exceeded")
+_(LIMIT_EXCEEDED, -162, "limit exceeded")                               \
+_(IKE_NO_PORT, -163, "port not managed by IKE")                         \
+_(UDP_PORT_TAKEN, -164, "UDP port already taken")                       \
 
 typedef enum
 {
index c7ce3ef..177300a 100644 (file)
@@ -372,7 +372,7 @@ static void vl_api_ipsec_sad_entry_add_del_t_handler
                                crypto_alg, &crypto_key,
                                integ_alg, &integ_key, flags,
                                0, mp->entry.salt, &tun_src, &tun_dst,
-                               &sa_index);
+                               &sa_index, IPSEC_UDP_PORT_NONE);
   else
     rv = ipsec_sa_unlock_id (id);
 
@@ -662,7 +662,8 @@ vl_api_ipsec_tunnel_if_add_del_t_handler (vl_api_ipsec_tunnel_if_add_del_t *
                                  &integ_key,
                                  (flags | IPSEC_SA_FLAG_IS_INBOUND),
                                  ntohl (mp->tx_table_id),
-                                 mp->salt, &remote_ip, &local_ip, NULL);
+                                 mp->salt, &remote_ip, &local_ip, NULL,
+                                 IPSEC_UDP_PORT_NONE);
 
       if (rv)
        goto done;
@@ -676,7 +677,8 @@ vl_api_ipsec_tunnel_if_add_del_t_handler (vl_api_ipsec_tunnel_if_add_del_t *
                                  &integ_key,
                                  flags,
                                  ntohl (mp->tx_table_id),
-                                 mp->salt, &local_ip, &remote_ip, NULL);
+                                 mp->salt, &local_ip, &remote_ip, NULL,
+                                 IPSEC_UDP_PORT_NONE);
 
       if (rv)
        goto done;
index 0406190..7779e79 100644 (file)
@@ -149,7 +149,8 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm,
     rv = ipsec_sa_add_and_lock (id, spi, proto, crypto_alg,
                                &ck, integ_alg, &ik, flags,
                                0, clib_host_to_net_u32 (salt),
-                               &tun_src, &tun_dst, NULL);
+                               &tun_src, &tun_dst, NULL,
+                               IPSEC_UDP_PORT_NONE);
   else
     rv = ipsec_sa_unlock_id (id);
 
@@ -852,14 +853,14 @@ create_ipsec_tunnel_command_fn (vlib_main_t * vm,
                               local_spi, IPSEC_PROTOCOL_ESP, crypto_alg,
                               &lck, integ_alg, &lik, flags, table_id,
                               clib_host_to_net_u32 (salt), &local_ip,
-                              &remote_ip, NULL);
+                              &remote_ip, NULL, IPSEC_UDP_PORT_NONE);
       rv |=
        ipsec_sa_add_and_lock (ipsec_tun_mk_remote_sa_id (sw_if_index),
                               remote_spi, IPSEC_PROTOCOL_ESP, crypto_alg,
                               &rck, integ_alg, &rik,
                               (flags | IPSEC_SA_FLAG_IS_INBOUND), table_id,
                               clib_host_to_net_u32 (salt), &remote_ip,
-                              &local_ip, NULL);
+                              &local_ip, NULL, IPSEC_UDP_PORT_NONE);
       rv |=
        ipsec_tun_protect_update_one (sw_if_index, &nh,
                                      ipsec_tun_mk_local_sa_id (sw_if_index),
index 4401c2e..0e1e63d 100644 (file)
@@ -135,7 +135,8 @@ ipsec_sa_add_and_lock (u32 id,
                       u32 tx_table_id,
                       u32 salt,
                       const ip46_address_t * tun_src,
-                      const ip46_address_t * tun_dst, u32 * sa_out_index)
+                      const ip46_address_t * tun_dst, u32 * sa_out_index,
+                      u16 dst_port)
 {
   vlib_main_t *vm = vlib_get_main ();
   ipsec_main_t *im = &ipsec_main;
@@ -269,8 +270,16 @@ ipsec_sa_add_and_lock (u32 id,
 
   if (ipsec_sa_is_set_UDP_ENCAP (sa))
     {
-      sa->udp_hdr.src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
-      sa->udp_hdr.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
+      if (dst_port == IPSEC_UDP_PORT_NONE)
+       {
+         sa->udp_hdr.src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
+         sa->udp_hdr.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipsec);
+       }
+      else
+       {
+         sa->udp_hdr.src_port = clib_host_to_net_u16 (dst_port);
+         sa->udp_hdr.dst_port = clib_host_to_net_u16 (dst_port);
+       }
     }
 
   hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
index e0d74e1..0997eb7 100644 (file)
@@ -16,6 +16,7 @@
 #define __IPSEC_SPD_SA_H__
 
 #include <vlib/vlib.h>
+#include <vnet/crypto/crypto.h>
 #include <vnet/ip/ip.h>
 #include <vnet/fib/fib_node.h>
 
@@ -209,7 +210,7 @@ extern int ipsec_sa_add_and_lock (u32 id,
                                  u32 salt,
                                  const ip46_address_t * tunnel_src_addr,
                                  const ip46_address_t * tunnel_dst_addr,
-                                 u32 * sa_index);
+                                 u32 * sa_index, u16 dst_port);
 extern index_t ipsec_sa_find_and_lock (u32 id);
 extern int ipsec_sa_unlock_id (u32 id);
 extern void ipsec_sa_unlock (index_t sai);
@@ -233,6 +234,8 @@ extern uword unformat_ipsec_integ_alg (unformat_input_t * input,
                                       va_list * args);
 extern uword unformat_ipsec_key (unformat_input_t * input, va_list * args);
 
+#define IPSEC_UDP_PORT_NONE ((u16)~0)
+
 /*
  * Anti Replay definitions
  */