ikev2: fix wrong usage of BN_bn2bin()
[vpp.git] / src / plugins / ikev2 / ikev2.c
index 55c9aa3..f288d4f 100644 (file)
@@ -29,6 +29,9 @@
 #include <plugins/ikev2/ikev2_priv.h>
 #include <openssl/sha.h>
 
+#define IKEV2_LIVENESS_RETRIES 2
+#define IKEV2_LIVENESS_PERIOD_CHECK 30
+
 ikev2_main_t ikev2_main;
 
 static int ikev2_delete_tunnel_interface (vnet_main_t * vnm,
@@ -377,6 +380,7 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai)
   ikev2_sa_transform_t *t = 0, *t2;
   ikev2_main_t *km = &ikev2_main;
 
+  sai->init_response_received = 1;
 
   /*move some data to the new SA */
 #define _(A) ({void* __tmp__ = (A); (A) = 0; __tmp__;})
@@ -398,6 +402,8 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai)
   sa->i_auth.key = _(sai->i_auth.key);
   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 _
 
 
@@ -983,6 +989,7 @@ ikev2_process_informational_req (vlib_main_t * vm, ikev2_sa_t * sa,
   ike_payload_header_t *ikep;
   u32 plen;
 
+  sa->liveness_retries = 0;
   ikev2_elog_exchange ("ispi %lx rspi %lx INFORMATIONAL received "
                       "from %d.%d.%d.%d", clib_host_to_net_u64 (ike->ispi),
                       clib_host_to_net_u64 (ike->rspi), sa->iaddr.as_u32);
@@ -1377,6 +1384,11 @@ ikev2_sa_auth (ikev2_sa_t * sa)
   }));
   /* *INDENT-ON* */
 
+  if (sel_p)
+    {
+      sa->udp_encap = sel_p->udp_encap;
+      sa->dst_port = sel_p->dst_port;
+    }
   vec_free (authmsg);
 
   if (sa->state == IKEV2_STATE_AUTHENTICATED)
@@ -1498,6 +1510,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
@@ -1541,6 +1554,8 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
   vec_add1 (sas_in, a->remote_sa_id);
   if (a->is_rekey)
     {
+      ipsec_tun_protect_del (sw_if_index, NULL);
+
       /* replace local SA immediately */
       ipsec_sa_unlock_id (a->local_sa_id);
 
@@ -1553,13 +1568,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);
 }
@@ -1606,6 +1621,11 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm,
     }
 
   a.flags = IPSEC_SA_FLAG_USE_ANTI_REPLAY;
+  if (sa->udp_encap)
+    {
+      a.flags |= IPSEC_SA_FLAG_IS_TUNNEL;
+      a.flags |= IPSEC_SA_FLAG_UDP_ENCAP;
+    }
   a.is_rekey = is_rekey;
 
   tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_ESN);
@@ -1790,6 +1810,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));
@@ -2283,6 +2304,13 @@ ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike)
   return -1;
 }
 
+static void
+ikev2_init_sa (ikev2_sa_t * sa)
+{
+  ikev2_main_t *km = &ikev2_main;
+  sa->liveness_period_check =
+    vlib_time_now (km->vlib_main) + IKEV2_LIVENESS_PERIOD_CHECK;
+}
 
 static uword
 ikev2_node_fn (vlib_main_t * vm,
@@ -2392,6 +2420,7 @@ ikev2_node_fn (vlib_main_t * vm,
                          pool_get (km->per_thread_data[thread_index].sas,
                                    sa0);
                          clib_memcpy_fast (sa0, &sa, sizeof (*sa0));
+                         ikev2_init_sa (sa0);
                          hash_set (km->
                                    per_thread_data[thread_index].sa_by_rspi,
                                    sa0->rspi,
@@ -2417,10 +2446,18 @@ ikev2_node_fn (vlib_main_t * vm,
                          ikev2_sa_t *sai =
                            pool_elt_at_index (km->sais, p[0]);
 
-                         ikev2_complete_sa_data (sa0, sai);
-                         ikev2_calc_keys (sa0);
-                         ikev2_sa_auth_init (sa0);
-                         len = ikev2_generate_message (sa0, ike0, 0);
+                         if (sai->init_response_received)
+                           {
+                             /* we've already processed sa-init response */
+                             sa0->state = IKEV2_STATE_UNKNOWN;
+                           }
+                         else
+                           {
+                             ikev2_complete_sa_data (sa0, sai);
+                             ikev2_calc_keys (sa0);
+                             ikev2_sa_auth_init (sa0);
+                             len = ikev2_generate_message (sa0, ike0, 0);
+                           }
                        }
                    }
 
@@ -2558,10 +2595,7 @@ ikev2_node_fn (vlib_main_t * vm,
                            }
                        }
                    }
-                 if (!sa0->is_initiator)
-                   {
-                     len = ikev2_generate_message (sa0, ike0, 0);
-                   }
+                 len = ikev2_generate_message (sa0, ike0, 0);
                }
            }
          else if (ike0->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA)
@@ -2745,24 +2779,27 @@ ikev2_set_initiator_proposals (vlib_main_t * vm, ikev2_sa_t * sa,
       return r;
     }
 
-  /* Integrity */
-  error = 1;
-  vec_foreach (td, km->supported_transforms)
-  {
-    if (td->type == IKEV2_TRANSFORM_TYPE_INTEG
-       && td->integ_type == ts->integ_alg)
+  if (is_ike || IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16 != ts->crypto_alg)
+    {
+      /* Integrity */
+      error = 1;
+      vec_foreach (td, km->supported_transforms)
       {
-       vec_add1 (proposal->transforms, *td);
-       error = 0;
-       break;
+       if (td->type == IKEV2_TRANSFORM_TYPE_INTEG
+           && td->integ_type == ts->integ_alg)
+         {
+           vec_add1 (proposal->transforms, *td);
+           error = 0;
+           break;
+         }
       }
-  }
-  if (error)
-    {
-      ikev2_elog_error
-       ("Didn't find any supported algorithm for IKEV2_TRANSFORM_TYPE_INTEG");
-      r = clib_error_return (0, "Unsupported algorithm");
-      return r;
+      if (error)
+       {
+         ikev2_elog_error
+           ("Didn't find any supported algorithm for IKEV2_TRANSFORM_TYPE_INTEG");
+         r = clib_error_return (0, "Unsupported algorithm");
+         return r;
+       }
     }
 
   /* PRF */
@@ -2916,6 +2953,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)
 {
@@ -2930,6 +3019,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;
@@ -2941,6 +3031,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);
@@ -3149,6 +3241,55 @@ 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)
+{
+  ikev2_profile_t *p = ikev2_profile_index_by_name (name);
+  clib_error_t *r;
+
+  if (!p)
+    {
+      r = clib_error_return (0, "unknown profile %v", name);
+      return r;
+    }
+
+  p->udp_encap = 1;
+  return 0;
+}
+
 clib_error_t *
 ikev2_set_profile_sa_lifetime (vlib_main_t * vm, u8 * name,
                               u64 lifetime, u32 jitter, u32 handover,
@@ -3233,6 +3374,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
     sa.is_profile_index_set = 1;
     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);
@@ -3567,7 +3710,8 @@ ikev2_init (vlib_main_t * vm)
 
   mhash_init_vec_string (&km->profile_index_by_name, sizeof (uword));
 
-  vec_validate (km->per_thread_data, tm->n_vlib_mains - 1);
+  vec_validate_aligned (km->per_thread_data, tm->n_vlib_mains - 1,
+                       CLIB_CACHE_LINE_BYTES);
   for (thread_id = 0; thread_id < tm->n_vlib_mains - 1; thread_id++)
     {
       km->per_thread_data[thread_id].sa_by_rspi =
@@ -3576,6 +3720,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);
 
@@ -3753,6 +3898,9 @@ ikev2_process_pending_sa_init (ikev2_main_t * km)
   hash_foreach (ispi, sai, km->sa_by_ispi,
   ({
     sa = pool_elt_at_index (km->sais, sai);
+    if (sa->init_response_received)
+      continue;
+
     u32 bi0;
     if (vlib_buffer_alloc (km->vlib_main, &bi0, 1) != 1)
       return;
@@ -3769,12 +3917,68 @@ ikev2_process_pending_sa_init (ikev2_main_t * km)
 
 static vlib_node_registration_t ikev2_mngr_process_node;
 
+static void
+ikev2_send_informational_request (ikev2_sa_t * sa)
+{
+  ikev2_main_t *km = &ikev2_main;
+  ip4_address_t *src, *dst;
+  ike_header_t *ike0;
+  u32 bi0 = 0;
+  int len;
+
+  bi0 = ikev2_get_new_ike_header_buff (km->vlib_main, &ike0);
+
+  ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL;
+  ike0->ispi = clib_host_to_net_u64 (sa->ispi);
+  ike0->rspi = clib_host_to_net_u64 (sa->rspi);
+  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1);
+  sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
+  len = ikev2_generate_message (sa, ike0, 0);
+
+  if (sa->is_initiator)
+    {
+      src = &sa->iaddr;
+      dst = &sa->raddr;
+    }
+  else
+    {
+
+      dst = &sa->iaddr;
+      src = &sa->raddr;
+    }
+
+  ikev2_send_ike (km->vlib_main, src, dst, bi0, len);
+}
+
+static_always_inline int
+ikev2_mngr_process_responder_sas (ikev2_sa_t * sa)
+{
+  ikev2_main_t *km = &ikev2_main;
+  vlib_main_t *vm = km->vlib_main;
+
+  if (sa->liveness_retries > IKEV2_LIVENESS_RETRIES)
+    return 1;
+
+  f64 now = vlib_time_now (vm);
+
+  if (sa->liveness_period_check < now)
+    {
+      sa->liveness_retries++;
+      sa->liveness_period_check = now + IKEV2_LIVENESS_PERIOD_CHECK;
+      ikev2_send_informational_request (sa);
+    }
+  return 0;
+}
+
 static uword
 ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
                       vlib_frame_t * f)
 {
   ikev2_main_t *km = &ikev2_main;
   ipsec_main_t *im = &ipsec_main;
+  ikev2_profile_t *p;
+  ikev2_child_sa_t *c;
+  u32 *sai;
 
   while (1)
     {
@@ -3787,6 +3991,8 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
       vec_foreach (tkm, km->per_thread_data)
       {
        ikev2_sa_t *sa;
+       u32 *to_be_deleted = 0;
+
         /* *INDENT-OFF* */
         pool_foreach (sa, tkm->sas, ({
           ikev2_child_sa_t *c;
@@ -3803,8 +4009,29 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
             {
             req_sent |= ikev2_mngr_process_child_sa(sa, c, del_old_ids);
             }
+
+          if (ikev2_mngr_process_responder_sas (sa))
+            vec_add1 (to_be_deleted, sa - tkm->sas);
         }));
         /* *INDENT-ON* */
+
+       vec_foreach (sai, to_be_deleted)
+       {
+         sa = pool_elt_at_index (tkm->sas, sai[0]);
+         if (sa->is_initiator && sa->is_profile_index_set)
+           {
+             p = pool_elt_at_index (km->profiles, sa->profile_index);
+             if (p)
+               {
+                 ikev2_initiate_sa_init (vm, p->name);
+                 continue;
+               }
+           }
+         vec_foreach (c, sa->childs)
+           ikev2_delete_tunnel_interface (km->vnet_main, sa, c);
+         hash_unset (tkm->sa_by_rspi, sa->rspi);
+         pool_put (tkm->sas, sa);
+       }
       }
 
       /* process ipsec sas */