X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fikev2%2Fikev2.c;h=45da023f10405e268aef5ffa721651515f33c57a;hb=59fea5a6a3fafe0a5a0d2f543db53af790013e3a;hp=dfa697fc714c9719c1e43567d24255f2221f18ad;hpb=c8e19cba7eb9ade3bbb53ebeef55a80976d1bb5f;p=vpp.git diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index dfa697fc714..45da023f104 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -29,6 +29,9 @@ #include #include +#define IKEV2_LIVENESS_RETRIES 3 +#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) + km->liveness_period; +} 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,8 +2595,9 @@ ikev2_node_fn (vlib_main_t * vm, } } } - if (!sa0->is_initiator) + if (!(ike0->flags & IKEV2_HDR_FLAG_RESPONSE)) { + ike0->flags |= IKEV2_HDR_FLAG_RESPONSE; len = ikev2_generate_message (sa0, ike0, 0); } } @@ -2745,24 +2783,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 +2957,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 +3023,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 +3035,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 +3245,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, @@ -3229,10 +3374,12 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) ikev2_sa_free_proposal_vector (&proposals); sa.is_initiator = 1; - sa.profile_index = km->profiles - p; + sa.profile_index = p - km->profiles; 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); @@ -3563,11 +3710,14 @@ ikev2_init (vlib_main_t * vm) km->vnet_main = vnet_get_main (); km->vlib_main = vm; + km->liveness_period = IKEV2_LIVENESS_PERIOD_CHECK; + km->liveness_max_retries = IKEV2_LIVENESS_RETRIES; ikev2_crypto_init (km); 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 +3726,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); @@ -3698,6 +3849,19 @@ ikev2_set_log_level (ikev2_log_level_t log_level) return 0; } +clib_error_t * +ikev2_set_liveness_params (u32 period, u32 max_retries) +{ + ikev2_main_t *km = &ikev2_main; + + if (period == 0 || max_retries == 0) + return clib_error_return (0, "invalid args"); + + km->liveness_period = period; + km->liveness_max_retries = max_retries; + return 0; +} + static void ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) { @@ -3742,14 +3906,101 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) } } +static void +ikev2_process_pending_sa_init (ikev2_main_t * km) +{ + u32 sai; + u64 ispi; + ikev2_sa_t *sa; + + /* *INDENT-OFF* */ + 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; + + vlib_buffer_t * b = vlib_get_buffer (km->vlib_main, bi0); + clib_memcpy_fast (vlib_buffer_get_current (b), + sa->last_sa_init_req_packet_data, + vec_len (sa->last_sa_init_req_packet_data)); + ikev2_send_ike (km->vlib_main, &sa->iaddr, &sa->raddr, bi0, + vec_len (sa->last_sa_init_req_packet_data)); + })); + /* *INDENT-ON* */ +} + 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->sk_ai || !sa->sk_ar) + return 0; + + if (sa->liveness_retries >= km->liveness_max_retries) + return 1; + + f64 now = vlib_time_now (vm); + + if (sa->liveness_period_check < now) + { + sa->liveness_retries++; + sa->liveness_period_check = now + km->liveness_period; + 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) { @@ -3762,6 +4013,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; @@ -3778,8 +4031,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 */ @@ -3790,6 +4064,8 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, })); /* *INDENT-ON* */ + ikev2_process_pending_sa_init (km); + if (req_sent) { vlib_process_wait_for_event_or_clock (vm, 5);