X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fikev2%2Fikev2.c;h=bb620a86ddd3526a6d54bf3aa5913266d9f5ca94;hb=abc5660c6;hp=64d5273af68ff2da7cfcb1bf2dc5a0533951cf87;hpb=cb3cfe876b3bbe2f360e3b32a43a060bdcb8bf9e;p=vpp.git diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index 64d5273af68..bb620a86ddd 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -28,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, @@ -36,7 +40,7 @@ static int ikev2_delete_tunnel_interface (vnet_main_t * vnm, #define ikev2_set_state(sa, v) do { \ (sa)->state = v; \ - clib_warning("sa state changed to " #v); \ + ikev2_elog_sa_state("ispi %lx SA state changed to " #v, sa->ispi); \ } while(0); typedef struct @@ -170,9 +174,6 @@ ikev2_select_proposal (ikev2_sa_proposal_t * proposals, } } - clib_warning ("bitmap is %x mandatory is %x optional is %x", - bitmap, mandatory_bitmap, optional_bitmap); - if ((bitmap & mandatory_bitmap) == mandatory_bitmap && (bitmap & ~optional_bitmap) == 0) { @@ -245,19 +246,24 @@ ikev2_sa_free_proposal_vector (ikev2_sa_proposal_t ** v) vec_free (*v); }; +static void +ikev2_sa_free_child_sa (ikev2_child_sa_t * c) +{ + ikev2_sa_free_proposal_vector (&c->r_proposals); + ikev2_sa_free_proposal_vector (&c->i_proposals); + vec_free (c->sk_ai); + vec_free (c->sk_ar); + vec_free (c->sk_ei); + vec_free (c->sk_er); + vec_free (c->tsi); + vec_free (c->tsr); +} + static void ikev2_sa_free_all_child_sa (ikev2_child_sa_t ** childs) { ikev2_child_sa_t *c; - vec_foreach (c, *childs) - { - ikev2_sa_free_proposal_vector (&c->r_proposals); - ikev2_sa_free_proposal_vector (&c->i_proposals); - vec_free (c->sk_ai); - vec_free (c->sk_ar); - vec_free (c->sk_ei); - vec_free (c->sk_er); - } + vec_foreach (c, *childs) ikev2_sa_free_child_sa (c); vec_free (*childs); } @@ -265,13 +271,7 @@ ikev2_sa_free_all_child_sa (ikev2_child_sa_t ** childs) static void ikev2_sa_del_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * child) { - ikev2_sa_free_proposal_vector (&child->r_proposals); - ikev2_sa_free_proposal_vector (&child->i_proposals); - vec_free (child->sk_ai); - vec_free (child->sk_ar); - vec_free (child->sk_ei); - vec_free (child->sk_er); - + ikev2_sa_free_child_sa (child); vec_del1 (sa->childs, child - sa->childs); } @@ -346,8 +346,6 @@ ikev2_generate_sa_init_data (ikev2_sa_t * sa) if (!t) { - clib_warning ("unknown dh data group %u (data len %u)", sa->dh_group, - vec_len (sa->i_dh_data)); sa->dh_group = IKEV2_TRANSFORM_DH_TYPE_NONE; return; } @@ -382,7 +380,6 @@ 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; - /*move some data to the new SA */ #define _(A) ({void* __tmp__ = (A); (A) = 0; __tmp__;}) sa->i_nonce = _(sai->i_nonce); @@ -394,6 +391,8 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) sa->i_id.type = sai->i_id.type; sa->profile_index = sai->profile_index; sa->is_profile_index_set = sai->is_profile_index_set; + sa->tun_itf = sai->tun_itf; + sa->is_tun_itf_set = sai->is_tun_itf_set; sa->i_id.data = _(sai->i_id.data); sa->i_auth.method = sai->i_auth.method; sa->i_auth.hex = sai->i_auth.hex; @@ -401,6 +400,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 _ @@ -421,8 +422,6 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) if (!t) { - clib_warning ("unknown dh data group %u (data len %u)", sa->dh_group, - vec_len (sa->i_dh_data)); sa->dh_group = IKEV2_TRANSFORM_DH_TYPE_NONE; return; } @@ -596,13 +595,10 @@ ikev2_process_sa_init_req (vlib_main_t * vm, ikev2_sa_t * sa, u32 len = clib_net_to_host_u32 (ike->length); u8 payload = ike->nextpayload; - clib_warning ("ispi %lx rspi %lx nextpayload %x version %x " - "exchange %x flags %x msgid %x length %u", - clib_net_to_host_u64 (ike->ispi), - clib_net_to_host_u64 (ike->rspi), - payload, ike->version, - ike->exchange, ike->flags, - clib_net_to_host_u32 (ike->msgid), len); + ikev2_elog_exchange ("ispi %lx rspi %lx IKE_INIT request received " + "from %d.%d.%d.%d", + clib_net_to_host_u64 (ike->ispi), + clib_net_to_host_u64 (ike->rspi), sa->iaddr.as_u32); sa->ispi = clib_net_to_host_u64 (ike->ispi); @@ -646,8 +642,8 @@ ikev2_process_sa_init_req (vlib_main_t * vm, ikev2_sa_t * sa, } else { - clib_warning ("unknown payload %u flags %x length %u", payload, - ikep->flags, plen); + ikev2_elog_uint (IKEV2_LOG_ERROR, "Unknown payload! type=%d", + payload); if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); @@ -671,17 +667,13 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, u32 len = clib_net_to_host_u32 (ike->length); u8 payload = ike->nextpayload; - clib_warning ("ispi %lx rspi %lx nextpayload %x version %x " - "exchange %x flags %x msgid %x length %u", - clib_net_to_host_u64 (ike->ispi), - clib_net_to_host_u64 (ike->rspi), - payload, ike->version, - ike->exchange, ike->flags, - clib_net_to_host_u32 (ike->msgid), len); - sa->ispi = clib_net_to_host_u64 (ike->ispi); sa->rspi = clib_net_to_host_u64 (ike->rspi); + ikev2_elog_exchange ("ispi %lx rspi %lx IKE_INIT response received " + "from %d.%d.%d.%d", sa->ispi, sa->rspi, + sa->raddr.as_u32); + /* store whole IKE payload - needed for PSK auth */ vec_free (sa->last_sa_init_res_packet_data); vec_add (sa->last_sa_init_res_packet_data, ike, len); @@ -728,8 +720,8 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, } else { - clib_warning ("unknown payload %u flags %x length %u", payload, - ikep->flags, plen); + ikev2_elog_uint (IKEV2_LOG_ERROR, "Unknown payload! type=%d", + payload); if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); @@ -767,13 +759,12 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload) if (*payload == IKEV2_PAYLOAD_SK) { - clib_warning ("received IKEv2 payload SK, len %u", plen - 4); last_payload = *payload; } else { - clib_warning ("unknown payload %u flags %x length %u", payload, - ikep->flags, plen); + ikev2_elog_uint (IKEV2_LOG_ERROR, "Unknown payload! type=%d", + *payload); if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { sa->unsupported_cp = *payload; @@ -787,7 +778,7 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload) if (last_payload != IKEV2_PAYLOAD_SK) { - clib_warning ("Last payload must be SK"); + ikev2_elog_error ("Last payload must be SK"); return 0; } @@ -799,7 +790,7 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload) if (memcmp (hmac, &ikep->payload[plen], tr_integ->key_trunc)) { - clib_warning ("message integrity check failed"); + ikev2_elog_error ("message integrity check failed"); vec_free (hmac); return 0; } @@ -852,20 +843,16 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) { ikev2_child_sa_t *first_child_sa; int p = 0; - u32 len = clib_net_to_host_u32 (ike->length); u8 payload = ike->nextpayload; u8 *plaintext = 0; - ike_payload_header_t *ikep; u32 plen; - clib_warning ("ispi %lx rspi %lx nextpayload %x version %x " - "exchange %x flags %x msgid %x length %u", - clib_net_to_host_u64 (ike->ispi), - clib_net_to_host_u64 (ike->rspi), - payload, ike->version, - ike->exchange, ike->flags, - clib_net_to_host_u32 (ike->msgid), len); + ikev2_elog_exchange ("ispi %lx rspi %lx EXCHANGE_IKE_AUTH received " + "from %d.%d.%d.%d", clib_host_to_net_u64 (ike->ispi), + clib_host_to_net_u64 (ike->rspi), + sa->is_initiator ? sa->raddr.as_u32 : sa-> + iaddr.as_u32); ikev2_calc_keys (sa); @@ -902,7 +889,6 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) if (payload == IKEV2_PAYLOAD_SA) /* 33 */ { - clib_warning ("received payload SA, len %u", plen - sizeof (*ikep)); if (sa->is_initiator) { ikev2_sa_free_proposal_vector (&first_child_sa->r_proposals); @@ -921,9 +907,6 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) sa->i_id.type = id->id_type; vec_free (sa->i_id.data); vec_add (sa->i_id.data, id->payload, plen - sizeof (*id)); - - clib_warning ("received payload IDi, len %u id_type %u", - plen - sizeof (*id), id->id_type); } else if (payload == IKEV2_PAYLOAD_IDR) /* 36 */ { @@ -932,9 +915,6 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) sa->r_id.type = id->id_type; vec_free (sa->r_id.data); vec_add (sa->r_id.data, id->payload, plen - sizeof (*id)); - - clib_warning ("received payload IDr len %u id_type %u", - plen - sizeof (*id), id->id_type); } else if (payload == IKEV2_PAYLOAD_AUTH) /* 39 */ { @@ -952,9 +932,6 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) vec_free (sa->i_auth.data); vec_add (sa->i_auth.data, a->payload, plen - sizeof (*a)); } - - clib_warning ("received payload AUTH, len %u auth_type %u", - plen - sizeof (*a), a->auth_method); } else if (payload == IKEV2_PAYLOAD_NOTIFY) /* 41 */ { @@ -971,25 +948,18 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) } else if (payload == IKEV2_PAYLOAD_TSI) /* 44 */ { - clib_warning ("received payload TSi, len %u", - plen - sizeof (*ikep)); - vec_free (first_child_sa->tsi); first_child_sa->tsi = ikev2_parse_ts_payload (ikep); } else if (payload == IKEV2_PAYLOAD_TSR) /* 45 */ { - clib_warning ("received payload TSr, len %u", - plen - sizeof (*ikep)); - vec_free (first_child_sa->tsr); first_child_sa->tsr = ikev2_parse_ts_payload (ikep); } else { - clib_warning ("unknown payload %u flags %x length %u data %u", - payload, ikep->flags, plen - 4, - format_hex_bytes, ikep->payload, plen - 4); + ikev2_elog_uint (IKEV2_LOG_ERROR, "Unknown payload! type=%d", + payload); if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { @@ -1012,20 +982,15 @@ ikev2_process_informational_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) { int p = 0; - u32 len = clib_net_to_host_u32 (ike->length); u8 payload = ike->nextpayload; u8 *plaintext = 0; - ike_payload_header_t *ikep; u32 plen; - clib_warning ("ispi %lx rspi %lx nextpayload %x version %x " - "exchange %x flags %x msgid %x length %u", - clib_net_to_host_u64 (ike->ispi), - clib_net_to_host_u64 (ike->rspi), - payload, ike->version, - ike->exchange, ike->flags, - clib_net_to_host_u32 (ike->msgid), len); + 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); plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload); @@ -1059,10 +1024,8 @@ ikev2_process_informational_req (vlib_main_t * vm, ikev2_sa_t * sa, } else { - clib_warning ("unknown payload %u flags %x length %u data %u", - payload, ikep->flags, plen - 4, - format_hex_bytes, ikep->payload, plen - 4); - + ikev2_elog_uint (IKEV2_LOG_ERROR, "Unknown payload! type=%d", + payload); if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { sa->unsupported_cp = payload; @@ -1083,7 +1046,6 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike) { int p = 0; - u32 len = clib_net_to_host_u32 (ike->length); u8 payload = ike->nextpayload; u8 *plaintext = 0; u8 rekeying = 0; @@ -1097,13 +1059,9 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, ikev2_sa_proposal_t *proposal = 0; ikev2_child_sa_t *child_sa; - clib_warning ("ispi %lx rspi %lx nextpayload %x version %x " - "exchange %x flags %x msgid %x length %u", - clib_net_to_host_u64 (ike->ispi), - clib_net_to_host_u64 (ike->rspi), - payload, ike->version, - ike->exchange, ike->flags, - clib_net_to_host_u32 (ike->msgid), len); + ikev2_elog_exchange ("ispi %lx rspi %lx CREATE_CHILD_SA received " + "from %d.%d.%d.%d", clib_host_to_net_u64 (ike->ispi), + clib_host_to_net_u64 (ike->rspi), sa->raddr.as_u32); plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload); @@ -1154,10 +1112,8 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, } else { - clib_warning ("unknown payload %u flags %x length %u data %u", - payload, ikep->flags, plen - 4, - format_hex_bytes, ikep->payload, plen - 4); - + ikev2_elog_uint (IKEV2_LOG_ERROR, "Unknown payload! type=%d", + payload); if (ikep->flags & IKEV2_PAYLOAD_FLAG_CRITICAL) { sa->unsupported_cp = payload; @@ -1194,7 +1150,8 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, child_sa = ikev2_sa_get_child (sa, n->spi, n->protocol_id, 1); if (!child_sa) { - clib_warning ("child SA spi %lx not found", n->spi); + ikev2_elog_uint (IKEV2_LOG_ERROR, "child SA spi %lx not found", + n->spi); goto cleanup_and_exit; } vec_add2 (sa->rekey, rekey, 1); @@ -1356,8 +1313,9 @@ ikev2_sa_auth (ikev2_sa_t * sa) if (!(sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC || sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG)) { - clib_warning ("unsupported authentication method %u", - sa->i_auth.method); + ikev2_elog_uint (IKEV2_LOG_ERROR, + "unsupported authentication method %u", + sa->i_auth.method); ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); return; } @@ -1424,6 +1382,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) @@ -1453,10 +1416,18 @@ ikev2_sa_auth (ikev2_sa_t * sa) sa->childs[0].r_proposals = ikev2_select_proposal (sa->childs[0].i_proposals, IKEV2_PROTOCOL_ESP); + + if (~0 != sel_p->tun_itf) + { + sa->is_tun_itf_set = 1; + sa->tun_itf = sel_p->tun_itf; + } } } else { + ikev2_elog_uint (IKEV2_LOG_ERROR, "authentication failed, no matching " + "profile found! ispi %lx", sa->ispi); ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); } vec_free (psk); @@ -1478,8 +1449,9 @@ ikev2_sa_auth_init (ikev2_sa_t * sa) if (!(sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC || sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG)) { - clib_warning ("unsupported authentication method %u", - sa->i_auth.method); + ikev2_elog_uint (IKEV2_LOG_ERROR, + "unsupported authentication method %u", + sa->i_auth.method); ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); return; } @@ -1508,34 +1480,120 @@ ikev2_sa_auth_init (ikev2_sa_t * sa) } static u32 -ikev2_mk_local_sa_id (u32 ti) +ikev2_mk_local_sa_id (u32 sai, u32 ci, u32 ti) { - return (0x80000000 | ti); + return (0x80000000 | (ti << 24) | (sai << 12) | ci); } static u32 -ikev2_mk_remote_sa_id (u32 ti) +ikev2_mk_remote_sa_id (u32 sai, u32 ci, u32 ti) { - return (0xc0000000 | ti); + return (0xc0000000 | (ti << 24) | (sai << 12) | ci); +} + +typedef struct +{ + u32 sw_if_index; + u32 salt_local; + u32 salt_remote; + u32 local_sa_id; + u32 remote_sa_id; + ipsec_sa_flags_t flags; + u32 local_spi; + u32 remote_spi; + ipsec_crypto_alg_t encr_type; + ipsec_integ_alg_t integ_type; + ip46_address_t local_ip; + ip46_address_t remote_ip; + 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 +ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a) +{ + ikev2_main_t *km = &ikev2_main; + u32 sw_if_index; + int rv = 0; + + if (~0 == a->sw_if_index) + { + /* no tunnel associated with the SA/profile - create a new one */ + rv = ipip_add_tunnel (IPIP_TRANSPORT_IP4, ~0, + &a->local_ip, &a->remote_ip, 0, + TUNNEL_ENCAP_DECAP_FLAG_NONE, IP_DSCP_CS0, + TUNNEL_MODE_P2P, &sw_if_index); + + if (rv == VNET_API_ERROR_IF_ALREADY_EXISTS) + { + if (hash_get (km->sw_if_indices, sw_if_index)) + /* interface is managed by IKE; proceed with updating SAs */ + rv = 0; + } + hash_set1 (km->sw_if_indices, sw_if_index); + } + else + { + sw_if_index = a->sw_if_index; + vnet_sw_interface_admin_up (vnet_get_main (), sw_if_index); + } + + if (rv) + { + ikev2_elog_peers (IKEV2_LOG_ERROR, "installing ipip tunnel failed! " + "loc:%d.%d.%d.%d rem:%d.%d.%d.%d", + a->local_ip.ip4.as_u32, a->remote_ip.ip4.as_u32); + return; + } + + u32 *sas_in = NULL; + 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); + + /* keep the old sa */ + vec_add1 (sas_in, a->old_remote_sa_id); + } + + rv |= ipsec_sa_add_and_lock (a->local_sa_id, + a->local_spi, + 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->dst_port, 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->dst_port, a->dst_port); + + rv |= ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in); } static int -ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, - ikev2_child_sa_t * child) +ikev2_create_tunnel_interface (vlib_main_t * vm, + u32 thread_index, + ikev2_sa_t * sa, + ikev2_child_sa_t * child, u32 sa_index, + u32 child_index, u8 is_rekey) { ikev2_main_t *km = &ikev2_main; + ipsec_crypto_alg_t encr_type; + ipsec_integ_alg_t integ_type; ikev2_profile_t *p = 0; - ipsec_key_t loc_ckey, rem_ckey, loc_ikey, rem_ikey; ikev2_sa_transform_t *tr; ikev2_sa_proposal_t *proposals; - ipsec_sa_flags_t flags = 0; - ipsec_crypto_alg_t encr_type; - ipsec_integ_alg_t integ_type; - u32 local_spi, remote_spi; u8 is_aead = 0; - u32 salt_local = 0, salt_remote = 0; - ip46_address_t local_ip, remote_ip; - int rv; + ikev2_add_ipsec_tunnel_args_t a; + + clib_memset (&a, 0, sizeof (a)); if (!child->r_proposals) { @@ -1545,26 +1603,32 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, if (sa->is_initiator) { - ip46_address_set_ip4 (&local_ip, &sa->iaddr); - ip46_address_set_ip4 (&remote_ip, &sa->raddr); + ip46_address_set_ip4 (&a.local_ip, &sa->iaddr); + ip46_address_set_ip4 (&a.remote_ip, &sa->raddr); proposals = child->i_proposals; - local_spi = child->r_proposals[0].spi; - remote_spi = child->i_proposals[0].spi; + a.local_spi = child->r_proposals[0].spi; + a.remote_spi = child->i_proposals[0].spi; } else { - ip46_address_set_ip4 (&local_ip, &sa->raddr); - ip46_address_set_ip4 (&remote_ip, &sa->iaddr); + ip46_address_set_ip4 (&a.local_ip, &sa->raddr); + ip46_address_set_ip4 (&a.remote_ip, &sa->iaddr); proposals = child->r_proposals; - local_spi = child->i_proposals[0].spi; - remote_spi = child->r_proposals[0].spi; + a.local_spi = child->i_proposals[0].spi; + a.remote_spi = child->r_proposals[0].spi; } - flags = IPSEC_SA_FLAG_USE_ANTI_REPLAY; + 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); if (tr && tr->esn_type) - flags |= IPSEC_SA_FLAG_USE_ESN; + a.flags |= IPSEC_SA_FLAG_USE_ESN; tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_ENCR); if (tr) @@ -1620,6 +1684,7 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); return 1; } + a.encr_type = encr_type; if (!is_aead) { @@ -1656,30 +1721,31 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, integ_type = IPSEC_INTEG_ALG_NONE; } + a.integ_type = integ_type; ikev2_calc_child_keys (sa, child); if (sa->is_initiator) { - ipsec_mk_key (&loc_ikey, child->sk_ai, vec_len (child->sk_ai)); - ipsec_mk_key (&rem_ikey, child->sk_ar, vec_len (child->sk_ar)); - ipsec_mk_key (&loc_ckey, child->sk_ei, vec_len (child->sk_ei)); - ipsec_mk_key (&rem_ckey, child->sk_er, vec_len (child->sk_er)); + ipsec_mk_key (&a.loc_ikey, child->sk_ai, vec_len (child->sk_ai)); + ipsec_mk_key (&a.rem_ikey, child->sk_ar, vec_len (child->sk_ar)); + ipsec_mk_key (&a.loc_ckey, child->sk_ei, vec_len (child->sk_ei)); + ipsec_mk_key (&a.rem_ckey, child->sk_er, vec_len (child->sk_er)); if (is_aead) { - salt_remote = child->salt_er; - salt_local = child->salt_ei; + a.salt_remote = child->salt_er; + a.salt_local = child->salt_ei; } } else { - ipsec_mk_key (&loc_ikey, child->sk_ar, vec_len (child->sk_ar)); - ipsec_mk_key (&rem_ikey, child->sk_ai, vec_len (child->sk_ai)); - ipsec_mk_key (&loc_ckey, child->sk_er, vec_len (child->sk_er)); - ipsec_mk_key (&rem_ckey, child->sk_ei, vec_len (child->sk_ei)); + ipsec_mk_key (&a.loc_ikey, child->sk_ar, vec_len (child->sk_ar)); + ipsec_mk_key (&a.rem_ikey, child->sk_ai, vec_len (child->sk_ai)); + ipsec_mk_key (&a.loc_ckey, child->sk_er, vec_len (child->sk_er)); + ipsec_mk_key (&a.rem_ckey, child->sk_ei, vec_len (child->sk_ei)); if (is_aead) { - salt_remote = child->salt_ei; - salt_local = child->salt_er; + a.salt_remote = child->salt_ei; + a.salt_local = child->salt_er; } } @@ -1688,8 +1754,7 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, if (p && p->lifetime) { - child->time_to_expiration = - vlib_time_now (vnm->vlib_main) + p->lifetime; + child->time_to_expiration = vlib_time_now (vm) + p->lifetime; if (p->lifetime_jitter) { // This is not much better than rand(3), which Coverity warns @@ -1697,47 +1762,146 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, // however fast. If this perturbance to the expiration time // needs to use a better RNG then we may need to use something // like /dev/urandom which has significant overhead. - u32 rnd = (u32) (vlib_time_now (vnm->vlib_main) * 1e6); + u32 rnd = (u32) (vlib_time_now (vm) * 1e6); rnd = random_u32 (&rnd); child->time_to_expiration += 1 + (rnd % p->lifetime_jitter); } } - rv = ipip_add_tunnel (IPIP_TRANSPORT_IP4, ~0, - &local_ip, &remote_ip, 0, 0, &child->sw_if_index); + if (thread_index & 0xffffffc0) + ikev2_elog_error ("error: thread index exceeds max range 0x3f!"); - child->local_sa = ikev2_mk_local_sa_id (child->sw_if_index); - child->remote_sa = ikev2_mk_remote_sa_id (child->sw_if_index); + if (child_index & 0xfffff000 || sa_index & 0xfffff000) + ikev2_elog_error ("error: sa/child index exceeds max range 0xfff!"); - rv |= ipsec_sa_add_and_lock (child->local_sa, - local_spi, - IPSEC_PROTOCOL_ESP, encr_type, - &loc_ckey, integ_type, &loc_ikey, flags, - 0, salt_local, &local_ip, &remote_ip, NULL); - rv |= ipsec_sa_add_and_lock (child->remote_sa, - remote_spi, - IPSEC_PROTOCOL_ESP, encr_type, - &rem_ckey, integ_type, &rem_ikey, - (flags | IPSEC_SA_FLAG_IS_INBOUND), - 0, salt_remote, &remote_ip, &local_ip, NULL); + child->local_sa_id = + a.local_sa_id = + ikev2_mk_local_sa_id (sa_index, child_index, thread_index); - u32 *sas_in = NULL; - vec_add1 (sas_in, child->remote_sa); - rv |= - ipsec_tun_protect_update (child->sw_if_index, child->local_sa, sas_in); + u32 remote_sa_id = ikev2_mk_remote_sa_id (sa_index, child_index, + thread_index); + + if (is_rekey) + { + /* create a new remote SA ID to keep the old SA for a bit longer + * so the peer has some time to swap their SAs */ + + /* use most significat bit of child index part in id */ + u32 mask = 0x800; + if (sa->current_remote_id_mask) + { + sa->old_remote_id = a.old_remote_sa_id = remote_sa_id | mask; + sa->current_remote_id_mask = 0; + } + else + { + sa->old_remote_id = a.old_remote_sa_id = remote_sa_id; + sa->current_remote_id_mask = mask; + remote_sa_id |= mask; + } + sa->old_id_expiration = 3.0; + sa->old_remote_id_present = 1; + } + 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)); return 0; } +typedef struct +{ + ip46_address_t local_ip; + ip46_address_t remote_ip; + u32 remote_sa_id; + u32 local_sa_id; + u32 sw_if_index; +} ikev2_del_ipsec_tunnel_args_t; + +static_always_inline u32 +ikev2_flip_alternate_sa_bit (u32 id) +{ + u32 mask = 0x800; + if (mask & id) + return id & ~mask; + return id | mask; +} + +static void +ikev2_del_tunnel_from_main (ikev2_del_ipsec_tunnel_args_t * a) +{ + ikev2_main_t *km = &ikev2_main; + ipip_tunnel_t *ipip = NULL; + u32 sw_if_index; + + if (~0 == a->sw_if_index) + { + /* *INDENT-OFF* */ + ipip_tunnel_key_t key = { + .src = a->local_ip, + .dst = a->remote_ip, + .transport = IPIP_TRANSPORT_IP4, + .fib_index = 0, + }; + /* *INDENT-ON* */ + + ipip = ipip_tunnel_db_find (&key); + + if (ipip) + { + sw_if_index = ipip->sw_if_index; + hash_unset (km->sw_if_indices, ipip->sw_if_index); + } + else + sw_if_index = ~0; + } + else + { + sw_if_index = a->sw_if_index; + vnet_sw_interface_admin_down (vnet_get_main (), sw_if_index); + } + + if (~0 != sw_if_index) + ipsec_tun_protect_del (sw_if_index, NULL); + + ipsec_sa_unlock_id (a->remote_sa_id); + ipsec_sa_unlock_id (a->local_sa_id); + ipsec_sa_unlock_id (ikev2_flip_alternate_sa_bit (a->remote_sa_id)); + + if (ipip) + ipip_del_tunnel (ipip->sw_if_index); +} + static int ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, ikev2_child_sa_t * child) { - ipsec_tun_protect_del (child->sw_if_index); - ipsec_sa_unlock_id (child->remote_sa); - ipsec_sa_unlock_id (child->local_sa); - ipip_del_tunnel (child->sw_if_index); + ikev2_del_ipsec_tunnel_args_t a; + + clib_memset (&a, 0, sizeof (a)); + + if (sa->is_initiator) + { + ip46_address_set_ip4 (&a.local_ip, &sa->iaddr); + ip46_address_set_ip4 (&a.remote_ip, &sa->raddr); + } + else + { + ip46_address_set_ip4 (&a.local_ip, &sa->raddr); + ip46_address_set_ip4 (&a.remote_ip, &sa->iaddr); + } + + a.remote_sa_id = child->remote_sa_id; + a.local_sa_id = child->local_sa_id; + a.sw_if_index = (sa->is_tun_itf_set ? sa->tun_itf : ~0); + + vl_api_rpc_call_main_thread (ikev2_del_tunnel_from_main, (u8 *) & a, + sizeof (a)); return 0; } @@ -1852,6 +2016,8 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user) ikev2_payload_add_sa (chain, sa->childs[0].i_proposals); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); + ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_INITIAL_CONTACT, + 0); } else { @@ -2066,17 +2232,21 @@ ikev2_retransmit_sa_init (ike_header_t * ike, ike->length = tmp->length; clib_memcpy_fast(ike->payload, tmp->payload, clib_net_to_host_u32(tmp->length) - sizeof(*ike)); - clib_warning("IKE_SA_INIT retransmit from %U to %U", - format_ip4_address, &raddr, - format_ip4_address, &iaddr); + ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, + "ispi %lx IKE_SA_INIT retransmit " + "from %d.%d.%d.%d to %d.%d.%d.%d", + ike->ispi, + raddr.as_u32, iaddr.as_u32); return 1; } /* else ignore req */ else { - clib_warning("IKE_SA_INIT ignore from %U to %U", - format_ip4_address, &raddr, - format_ip4_address, &iaddr); + ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, + "ispi %lx IKE_SA_INIT ignore " + "from %d.%d.%d.%d to %d.%d.%d.%d", + ike->ispi, + raddr.as_u32, iaddr.as_u32); return -1; } } @@ -2118,23 +2288,46 @@ ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike) ike->length = tmp->length; clib_memcpy_fast (ike->payload, tmp->payload, clib_net_to_host_u32 (tmp->length) - sizeof (*ike)); - clib_warning ("IKE msgid %u retransmit from %U to %U", - msg_id, - format_ip4_address, &sa->raddr, - format_ip4_address, &sa->iaddr); + ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, "IKE retransmit msgid %d", + msg_id, sa->raddr.as_u32, sa->iaddr.as_u32); return 1; } /* old req ignore */ else { - clib_warning ("IKE msgid %u req ignore from %U to %U", - msg_id, - format_ip4_address, &sa->raddr, - format_ip4_address, &sa->iaddr); - return -1; + ikev2_elog_uint_peers (IKEV2_LOG_DEBUG, "IKE req ignore msgid %d", + msg_id, sa->raddr.as_u32, sa->iaddr.as_u32); + } + return -1; +} + +static void +ikev2_init_sa (vlib_main_t * vm, ikev2_sa_t * sa) +{ + ikev2_main_t *km = &ikev2_main; + sa->liveness_period_check = vlib_time_now (vm) + km->liveness_period; +} + +static void +ikev2_del_sa_init_from_main (u64 * ispi) +{ + ikev2_main_t *km = &ikev2_main; + uword *p = hash_get (km->sa_by_ispi, *ispi); + if (p) + { + ikev2_sa_t *sai = pool_elt_at_index (km->sais, p[0]); + hash_unset (km->sa_by_ispi, sai->ispi); + ikev2_sa_free_all_vec (sai); + pool_put (km->sais, sai); } } +static void +ikev2_del_sa_init (u64 ispi) +{ + vl_api_rpc_call_main_thread (ikev2_del_sa_init_from_main, (u8 *) & ispi, + sizeof (ispi)); +} static uword ikev2_node_fn (vlib_main_t * vm, @@ -2244,6 +2437,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 (vm, sa0); hash_set (km-> per_thread_data[thread_index].sa_by_rspi, sa0->rspi, @@ -2269,10 +2463,19 @@ 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 (clib_atomic_bool_cmp_and_swap + (&sai->init_response_received, 0, 1)) + { + ikev2_complete_sa_data (sa0, sai); + ikev2_calc_keys (sa0); + ikev2_sa_auth_init (sa0); + len = ikev2_generate_message (sa0, ike0, 0); + } + else + { + /* we've already processed sa-init response */ + sa0->state = IKEV2_STATE_UNKNOWN; + } } } @@ -2326,21 +2529,14 @@ ikev2_node_fn (vlib_main_t * vm, ikev2_initial_contact_cleanup (sa0); ikev2_sa_match_ts (sa0); if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE) - ikev2_create_tunnel_interface (km->vnet_main, sa0, - &sa0->childs[0]); + ikev2_create_tunnel_interface (vm, thread_index, sa0, + &sa0->childs[0], + p[0], 0, 0); } if (sa0->is_initiator) { - uword *p = hash_get (km->sa_by_ispi, ike0->ispi); - if (p) - { - ikev2_sa_t *sai = - pool_elt_at_index (km->sais, p[0]); - hash_unset (km->sa_by_ispi, sai->ispi); - ikev2_sa_free_all_vec (sai); - pool_put (km->sais, sai); - } + ikev2_del_sa_init (ike0->ispi); } else { @@ -2408,8 +2604,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); } } @@ -2447,14 +2644,18 @@ ikev2_node_fn (vlib_main_t * vm, { if (sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE) { + if (sa0->childs) + vec_free (sa0->childs); ikev2_child_sa_t *child; vec_add2 (sa0->childs, child, 1); child->r_proposals = sa0->rekey[0].r_proposal; child->i_proposals = sa0->rekey[0].i_proposal; child->tsi = sa0->rekey[0].tsi; child->tsr = sa0->rekey[0].tsr; - ikev2_create_tunnel_interface (km->vnet_main, sa0, - child); + ikev2_create_tunnel_interface (vm, thread_index, + sa0, child, p[0], + child - sa0->childs, + 1); } if (sa0->is_initiator) { @@ -2469,10 +2670,11 @@ ikev2_node_fn (vlib_main_t * vm, } else { - clib_warning ("IKEv2 exchange %u packet received from %U to %U", - ike0->exchange, - format_ip4_address, ip40->src_address.as_u8, - format_ip4_address, ip40->dst_address.as_u8); + ikev2_elog_uint_peers (IKEV2_LOG_WARNING, "IKEv2 exchange %d " + "received from %d.%d.%d.%d to %d.%d.%d.%d", + ike0->exchange, + ip40->src_address.as_u32, + ip40->dst_address.as_u32); } dispatch0: @@ -2589,24 +2791,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) - { - clib_warning - ("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 */ @@ -2760,6 +2965,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) { @@ -2774,7 +3031,9 @@ 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; mhash_set_mem (&km->profile_index_by_name, name, &index, 0); } @@ -2784,6 +3043,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); @@ -2972,6 +3233,75 @@ ikev2_set_profile_esp_transforms (vlib_main_t * vm, u8 * name, return 0; } +clib_error_t * +ikev2_set_profile_tunnel_interface (vlib_main_t * vm, + u8 * name, u32 sw_if_index) +{ + ikev2_profile_t *p; + clib_error_t *r; + + p = ikev2_profile_index_by_name (name); + + if (!p) + { + r = clib_error_return (0, "unknown profile %v", name); + return r; + } + + p->tun_itf = sw_if_index; + + 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, @@ -3052,9 +3382,14 @@ 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); ikev2_payload_add_ke (chain, sa.dh_group, sa.i_dh_data); ikev2_payload_add_nonce (chain, sa.i_nonce); @@ -3134,6 +3469,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) vec_add (sa.childs[0].tsi, &p->loc_ts, 1); vec_add (sa.childs[0].tsr, &p->rem_ts, 1); + ikev2_initial_contact_cleanup (&sa); + /* add SA to the pool */ ikev2_sa_t *sa0 = 0; pool_get (km->sais, sa0); @@ -3142,6 +3479,9 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) ikev2_send_ike (vm, if_ip, &p->responder.ip4, bi0, len); + ikev2_elog_exchange ("ispi %lx rspi %lx IKEV2_EXCHANGE_SA_INIT sent to " + "%d.%d.%d.%d", clib_host_to_net_u64 (sa0->ispi), 0, + p->responder.ip4.as_u32); } return 0; @@ -3378,11 +3718,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 = @@ -3390,11 +3733,15 @@ 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); ikev2_cli_reference (); + km->log_level = IKEV2_LOG_ERROR; + km->log_class = vlib_log_register_class ("ikev2", 0); return 0; } @@ -3405,9 +3752,9 @@ VLIB_INIT_FUNCTION (ikev2_init) = }; /* *INDENT-ON* */ - static u8 -ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa) +ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa, + u8 del_old_ids) { ikev2_main_t *km = &ikev2_main; ikev2_profile_t *p = 0; @@ -3433,8 +3780,8 @@ ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa) else if (csa->rekey_retries > 0) { csa->rekey_retries--; - clib_warning ("Rekeying Child SA 0x%x, retries left %d", - csa->i_proposals->spi, csa->rekey_retries); + ikev2_log_debug ("Rekeying Child SA 0x%x, retries left %d", + csa->i_proposals->spi, csa->rekey_retries); if (csa->rekey_retries == 0) { csa->rekey_retries = -1; @@ -3450,9 +3797,79 @@ ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa) } } + if (del_old_ids) + { + ipip_tunnel_t *ipip = NULL; + u32 sw_if_index = sa->is_tun_itf_set ? sa->tun_itf : ~0; + if (~0 == sw_if_index) + { + ip46_address_t local_ip; + ip46_address_t remote_ip; + if (sa->is_initiator) + { + ip46_address_set_ip4 (&local_ip, &sa->iaddr); + ip46_address_set_ip4 (&remote_ip, &sa->raddr); + } + else + { + ip46_address_set_ip4 (&local_ip, &sa->raddr); + ip46_address_set_ip4 (&remote_ip, &sa->iaddr); + } + + /* *INDENT-OFF* */ + ipip_tunnel_key_t key = { + .src = local_ip, + .dst = remote_ip, + .transport = IPIP_TRANSPORT_IP4, + .fib_index = 0, + }; + /* *INDENT-ON* */ + + ipip = ipip_tunnel_db_find (&key); + + if (ipip) + sw_if_index = ipip->sw_if_index; + else + return res; + } + + u32 *sas_in = NULL; + vec_add1 (sas_in, csa->remote_sa_id); + ipsec_tun_protect_update (sw_if_index, NULL, csa->local_sa_id, sas_in); + ipsec_sa_unlock_id (ikev2_flip_alternate_sa_bit (csa->remote_sa_id)); + } + return res; } +int +ikev2_set_log_level (ikev2_log_level_t log_level) +{ + ikev2_main_t *km = &ikev2_main; + + if (log_level >= IKEV2_LOG_MAX) + { + ikev2_log_error ("unknown logging level %d", log_level); + return -1; + } + + km->log_level = 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) { @@ -3497,14 +3914,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) { @@ -3517,15 +4021,47 @@ 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; + u8 del_old_ids = 0; + if (sa->old_remote_id_present && 0 > sa->old_id_expiration) + { + sa->old_remote_id_present = 0; + del_old_ids = 1; + } + else + sa->old_id_expiration -= 1; + vec_foreach (c, sa->childs) { - req_sent |= ikev2_mngr_process_child_sa(sa, c); + 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 */ @@ -3536,6 +4072,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);