X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fikev2%2Fikev2.c;h=9bea2c96d12196fd96f4622147cbac13f2d86c77;hb=HEAD;hp=cfc3d9f99406532a575bacff98edcc8060d2f49b;hpb=887f1aa725ed64c3c83aca557bd7bb6588bacaf7;p=vpp.git diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index cfc3d9f9940..9bea2c96d12 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -26,8 +26,10 @@ #include #include #include +#include #include #include +#include #define IKEV2_LIVENESS_RETRIES 3 #define IKEV2_LIVENESS_PERIOD_CHECK 30 @@ -92,31 +94,6 @@ format_ikev2_gen_sa_error (u8 * s, va_list * args) return s; } -#define foreach_ikev2_error \ -_(PROCESSED, "IKEv2 packets processed") \ -_(IKE_SA_INIT_RETRANSMIT, "IKE_SA_INIT retransmit ") \ -_(IKE_SA_INIT_IGNORE, "IKE_SA_INIT ignore (IKE SA already auth)") \ -_(IKE_REQ_RETRANSMIT, "IKE request retransmit") \ -_(IKE_REQ_IGNORE, "IKE request ignore (old msgid)") \ -_(NOT_IKEV2, "Non IKEv2 packets received") \ -_(BAD_LENGTH, "Bad packet length") \ -_(MALFORMED_PACKET, "Malformed packet") \ -_(NO_BUFF_SPACE, "No buffer space") - -typedef enum -{ -#define _(sym,str) IKEV2_ERROR_##sym, - foreach_ikev2_error -#undef _ - IKEV2_N_ERROR, -} ikev2_error_t; - -static char *ikev2_error_strings[] = { -#define _(sym,string) string, - foreach_ikev2_error -#undef _ -}; - typedef enum { IKEV2_NEXT_IP4_LOOKUP, @@ -133,14 +110,14 @@ typedef enum typedef u32 ikev2_non_esp_marker; -static_always_inline u16 -ikev2_get_port (ikev2_sa_t * sa) +static u16 +ikev2_get_port (ikev2_sa_t *sa) { return ikev2_natt_active (sa) ? IKEV2_PORT_NATT : IKEV2_PORT; } -static_always_inline int -ikev2_insert_non_esp_marker (ike_header_t * ike, int len) +static int +ikev2_insert_non_esp_marker (ike_header_t *ike, int len) { memmove ((u8 *) ike + sizeof (ikev2_non_esp_marker), ike, len); clib_memset (ike, 0, sizeof (ikev2_non_esp_marker)); @@ -234,6 +211,8 @@ ikev2_select_proposal (ikev2_sa_proposal_t * proposals, rv->proposal_num = proposal->proposal_num; rv->protocol_id = proposal->protocol_id; RAND_bytes ((u8 *) & rv->spi, sizeof (rv->spi)); + if (rv->protocol_id != IKEV2_PROTOCOL_IKE) + rv->spi &= 0xffffffff; goto done; } else @@ -428,8 +407,8 @@ ikev2_generate_sa_init_data (ikev2_sa_t * sa) RAND_bytes ((u8 *) & sa->rspi, 8); /* generate nonce */ - sa->r_nonce = vec_new (u8, IKEV2_NONCE_SIZE); - RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE); + sa->r_nonce = vec_new (u8, vec_len (sa->i_nonce)); + RAND_bytes ((u8 *) sa->r_nonce, vec_len (sa->i_nonce)); } /* generate dh keys */ @@ -503,11 +482,10 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) } static void -ikev2_calc_keys (ikev2_sa_t * sa) +ikev2_calc_keys_internal (ikev2_sa_t *sa, u8 *skeyseed) { u8 *tmp; /* calculate SKEYSEED = prf(Ni | Nr, g^ir) */ - u8 *skeyseed = 0; u8 *s = 0; u16 integ_key_len = 0, salt_len = 0; ikev2_sa_transform_t *tr_encr, *tr_prf, *tr_integ; @@ -525,7 +503,6 @@ ikev2_calc_keys (ikev2_sa_t * sa) vec_append (s, sa->i_nonce); vec_append (s, sa->r_nonce); - skeyseed = ikev2_calc_prf (tr_prf, s, sa->dh_shared_key); /* Calculate S = Ni | Nr | SPIi | SPIr */ u64 *spi; @@ -543,7 +520,6 @@ ikev2_calc_keys (ikev2_sa_t * sa) salt_len * 2; keymat = ikev2_calc_prfplus (tr_prf, skeyseed, s, len); - vec_free (skeyseed); vec_free (s); int pos = 0; @@ -591,7 +567,42 @@ ikev2_calc_keys (ikev2_sa_t * sa) } static void -ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child) +ikev2_calc_keys_rekey (ikev2_sa_t *sa_new, ikev2_sa_t *sa_old) +{ + u8 *s = 0, *skeyseed = 0; + ikev2_sa_transform_t *tr_prf = + ikev2_sa_get_td_for_type (sa_old->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); + + vec_append (s, sa_new->dh_shared_key); + vec_append (s, sa_new->i_nonce); + vec_append (s, sa_new->r_nonce); + skeyseed = ikev2_calc_prf (tr_prf, sa_old->sk_d, s); + + ikev2_calc_keys_internal (sa_new, skeyseed); + + vec_free (skeyseed); + vec_free (s); +} + +static void +ikev2_calc_keys (ikev2_sa_t *sa) +{ + u8 *s = 0, *skeyseed = 0; + ikev2_sa_transform_t *tr_prf = + ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); + + vec_append (s, sa->i_nonce); + vec_append (s, sa->r_nonce); + skeyseed = ikev2_calc_prf (tr_prf, s, sa->dh_shared_key); + + ikev2_calc_keys_internal (sa, skeyseed); + + vec_free (skeyseed); + vec_free (s); +} + +static void +ikev2_calc_child_keys (ikev2_sa_t *sa, ikev2_child_sa_t *child, u8 kex) { u8 *s = 0; u16 integ_key_len = 0; @@ -610,6 +621,8 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child) else salt_len = sizeof (u32); + if (kex) + vec_append (s, sa->dh_shared_key); vec_append (s, sa->i_nonce); vec_append (s, sa->r_nonce); /* calculate PRFplus */ @@ -661,8 +674,8 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child) vec_free (keymat); } -static_always_inline u8 * -ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t * ia, u16 port) +static u8 * +ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t *ia, u16 port) { const u32 max_buf_size = sizeof (ispi) + sizeof (rspi) + sizeof (ip6_address_t) + sizeof (u16); @@ -685,7 +698,10 @@ ikev2_parse_ke_payload (const void *p, u32 rlen, ikev2_sa_t * sa, u16 plen = clib_net_to_host_u16 (ke->length); ASSERT (plen >= sizeof (*ke) && plen <= rlen); if (sizeof (*ke) > rlen) - return 0; + { + ikev2_elog_error ("KE: packet too small"); + return 0; + } sa->dh_group = clib_net_to_host_u16 (ke->dh_group); vec_reset_length (ke_data[0]); @@ -694,13 +710,20 @@ ikev2_parse_ke_payload (const void *p, u32 rlen, ikev2_sa_t * sa, } static int -ikev2_parse_nonce_payload (const void *p, u32 rlen, u8 * nonce) +ikev2_parse_nonce_payload (const void *p, u32 rlen, const u8 **nonce) { const ike_payload_header_t *ikep = p; u16 plen = clib_net_to_host_u16 (ikep->length); ASSERT (plen >= sizeof (*ikep) && plen <= rlen); - clib_memcpy_fast (nonce, ikep->payload, plen - sizeof (*ikep)); - return 1; + int len = plen - sizeof (*ikep); + ASSERT (len >= 16 && len <= 256); + if (PREDICT_FALSE (len < 16 || len > 256)) + { + ikev2_elog_error ("NONCE: bad size"); + return 0; + } + *nonce = ikep->payload; + return len; } static int @@ -708,10 +731,16 @@ ikev2_check_payload_length (const ike_payload_header_t * ikep, int rlen, u16 * plen) { if (sizeof (*ikep) > rlen) - return 0; + { + ikev2_elog_error ("payload: packet too small"); + return 0; + } *plen = clib_net_to_host_u16 (ikep->length); if (*plen < sizeof (*ikep) || *plen > rlen) - return 0; + { + ikev2_elog_error ("payload: bad size"); + return 0; + } return 1; } @@ -719,7 +748,6 @@ static int ikev2_process_sa_init_req (vlib_main_t *vm, ikev2_sa_t *sa, ike_header_t *ike, udp_header_t *udp, u32 len, u32 sw_if_index) { - u8 nonce[IKEV2_NONCE_SIZE]; int p = 0; u8 payload = ike->nextpayload; ike_payload_header_t *ikep; @@ -739,7 +767,10 @@ ikev2_process_sa_init_req (vlib_main_t *vm, ikev2_sa_t *sa, ike_header_t *ike, vec_add (sa->last_sa_init_req_packet_data, ike, len); if (len < sizeof (*ike)) - return 0; + { + ikev2_elog_error ("IKE_INIT request too small"); + return 0; + } len -= sizeof (*ike); while (p < len && payload != IKEV2_PAYLOAD_NONE) @@ -762,9 +793,13 @@ ikev2_process_sa_init_req (vlib_main_t *vm, ikev2_sa_t *sa, ike_header_t *ike, } else if (payload == IKEV2_PAYLOAD_NONCE) { + const u8 *nonce; + int nonce_len; vec_reset_length (sa->i_nonce); - if (ikev2_parse_nonce_payload (ikep, current_length, nonce)) - vec_add (sa->i_nonce, nonce, plen - sizeof (*ikep)); + if ((nonce_len = ikev2_parse_nonce_payload (ikep, current_length, + &nonce)) <= 0) + return 0; + vec_add (sa->i_nonce, nonce, nonce_len); } else if (payload == IKEV2_PAYLOAD_NOTIFY) { @@ -828,7 +863,6 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, udp_header_t * udp, u32 len) { - u8 nonce[IKEV2_NONCE_SIZE]; int p = 0; u8 payload = ike->nextpayload; ike_payload_header_t *ikep; @@ -847,7 +881,10 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, vec_add (sa->last_sa_init_res_packet_data, ike, len); if (sizeof (*ike) > len) - return; + { + ikev2_elog_error ("IKE_INIT response too small"); + return; + } len -= sizeof (*ike); while (p < len && payload != IKEV2_PAYLOAD_NONE) @@ -876,9 +913,13 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, } else if (payload == IKEV2_PAYLOAD_NONCE) { + const u8 *nonce; + int nonce_len; vec_reset_length (sa->r_nonce); - if (ikev2_parse_nonce_payload (ikep, current_length, nonce)) - vec_add (sa->r_nonce, nonce, plen - sizeof (*ikep)); + if ((nonce_len = ikev2_parse_nonce_payload (ikep, current_length, + &nonce)) <= 0) + return; + vec_add (sa->r_nonce, nonce, nonce_len); } else if (payload == IKEV2_PAYLOAD_NOTIFY) { @@ -1044,8 +1085,8 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, return plaintext; } -static_always_inline int -ikev2_is_id_equal (ikev2_id_t * i1, ikev2_id_t * i2) +static int +ikev2_is_id_equal (const ikev2_id_t *i1, const ikev2_id_t *i2) { if (i1->type != i2->type) return 0; @@ -1069,7 +1110,6 @@ ikev2_initial_contact_cleanup_internal (ikev2_main_per_thread_data_t * ptd, ikev2_child_sa_t *c; /* find old IKE SAs with the same authenticated identity */ - /* *INDENT-OFF* */ pool_foreach (tmp, ptd->sas) { if (!ikev2_is_id_equal (&tmp->i_id, &sa->i_id) || !ikev2_is_id_equal(&tmp->r_id, &sa->r_id)) @@ -1078,7 +1118,6 @@ ikev2_initial_contact_cleanup_internal (ikev2_main_per_thread_data_t * ptd, if (sa->rspi != tmp->rspi) vec_add1(delete, tmp - ptd->sas); } - /* *INDENT-ON* */ for (i = 0; i < vec_len (delete); i++) { @@ -1354,6 +1393,159 @@ ikev2_process_informational_req (vlib_main_t * vm, return 1; } +static int +ikev2_process_create_child_sa_rekey (ikev2_sa_t *sa, ikev2_sa_t *sar, + ikev2_rekey_t *rekey, + ikev2_sa_proposal_t *proposal, + ikev2_ts_t *tsi, ikev2_ts_t *tsr, + const u8 *nonce, int nonce_len) +{ + ikev2_sa_transform_t *tr; + + rekey->i_proposal = proposal; + rekey->r_proposal = ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP); + + if (sar->dh_group) + { + tr = + ikev2_sa_get_td_for_type (rekey->r_proposal, IKEV2_TRANSFORM_TYPE_DH); + + if (!tr || tr->dh_type != sar->dh_group) + { + rekey->notify_type = IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD; + ikev2_sa_free_proposal_vector (&rekey->r_proposal); + return 0; + } + + vec_free (sa->dh_shared_key); + vec_free (sa->dh_private_key); + vec_free (sa->i_dh_data); + vec_free (sa->r_dh_data); + + sa->dh_group = sar->dh_group; + sa->i_dh_data = sar->i_dh_data; + sar->i_dh_data = 0; + + ikev2_generate_dh (sa, tr); + rekey->kex = 1; + } + + vec_reset_length (sa->i_nonce); + vec_add (sa->i_nonce, nonce, nonce_len); + + vec_validate (sa->r_nonce, nonce_len - 1); + RAND_bytes ((u8 *) sa->r_nonce, nonce_len); + + rekey->tsi = tsi; + rekey->tsr = tsr; + + return 1; +} + +static void +ikev2_complete_sa_rekey (ikev2_sa_t *sa_new, ikev2_sa_t *sa_old, + ikev2_sa_rekey_t *sa_rekey) +{ + sa_new->del = 0; + sa_new->rekey = 0; + sa_new->new_child = 0; + sa_new->sa_rekey = 0; + sa_new->last_sa_init_req_packet_data = 0; + sa_new->last_sa_init_res_packet_data = 0; + sa_new->last_msg_id = ~0; + sa_new->last_res_packet_data = 0; + sa_new->last_init_msg_id = 0; + clib_memset (&sa_new->stats, 0, sizeof (sa_new->stats)); + + sa_new->ispi = sa_rekey->ispi; + sa_new->rspi = sa_rekey->rspi; + sa_new->i_nonce = sa_rekey->i_nonce; + sa_new->r_nonce = sa_rekey->r_nonce; + sa_new->dh_group = sa_rekey->dh_group; + sa_new->dh_shared_key = sa_rekey->dh_shared_key; + sa_new->dh_private_key = sa_rekey->dh_private_key; + sa_new->i_dh_data = sa_rekey->i_dh_data; + sa_new->r_dh_data = sa_rekey->r_dh_data; + sa_new->i_proposals = sa_rekey->i_proposals; + sa_new->r_proposals = sa_rekey->r_proposals; + + sa_new->sk_d = 0; + sa_new->sk_ai = 0; + sa_new->sk_ar = 0; + sa_new->sk_ei = 0; + sa_new->sk_er = 0; + sa_new->sk_pi = 0; + sa_new->sk_pr = 0; + ikev2_calc_keys_rekey (sa_new, sa_old); + + sa_new->i_auth.data = vec_dup (sa_old->i_auth.data); + sa_new->i_auth.key = sa_old->i_auth.key; + if (sa_new->i_auth.key) + EVP_PKEY_up_ref (sa_new->i_auth.key); + + sa_new->r_auth.data = vec_dup (sa_old->r_auth.data); + sa_new->r_auth.key = sa_old->r_auth.key; + if (sa_new->r_auth.key) + EVP_PKEY_up_ref (sa_new->r_auth.key); + + sa_new->i_id.data = vec_dup (sa_old->i_id.data); + sa_new->r_id.data = vec_dup (sa_old->r_id.data); + + sa_old->is_tun_itf_set = 0; + sa_old->tun_itf = ~0; + sa_old->old_id_expiration = 0; + sa_old->current_remote_id_mask = 0; + sa_old->old_remote_id = 0; + sa_old->old_remote_id_present = 0; + sa_old->childs = 0; + sa_old->sw_if_index = ~0; +} + +static void +ikev2_process_sa_rekey (ikev2_sa_t *sa_new, ikev2_sa_t *sa_old, + ikev2_sa_rekey_t *sa_rekey) +{ + ikev2_sa_transform_t *tr; + + if (ikev2_generate_sa_init_data (sa_new) != IKEV2_GENERATE_SA_INIT_OK) + { + sa_rekey->notify_type = IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD; + return; + } + + sa_new->r_proposals = + ikev2_select_proposal (sa_new->i_proposals, IKEV2_PROTOCOL_IKE); + + tr = ikev2_sa_get_td_for_type (sa_new->r_proposals, IKEV2_TRANSFORM_TYPE_DH); + if (!tr || tr->dh_type != sa_new->dh_group) + { + sa_rekey->notify_type = IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD; + return; + } + + sa_rekey->notify_type = 0; + sa_rekey->ispi = sa_new->i_proposals[0].spi; + sa_rekey->rspi = sa_new->r_proposals[0].spi; + sa_rekey->i_nonce = sa_new->i_nonce; + sa_rekey->r_nonce = sa_new->r_nonce; + sa_rekey->dh_group = sa_new->dh_group; + sa_rekey->dh_shared_key = sa_new->dh_shared_key; + sa_rekey->dh_private_key = sa_new->dh_private_key; + sa_rekey->i_dh_data = sa_new->i_dh_data; + sa_rekey->r_dh_data = sa_new->r_dh_data; + sa_rekey->i_proposals = sa_new->i_proposals; + sa_rekey->r_proposals = sa_new->r_proposals; + + sa_new->i_nonce = 0; + sa_new->r_nonce = 0; + sa_new->dh_shared_key = 0; + sa_new->dh_private_key = 0; + sa_new->i_dh_data = 0; + sa_new->r_dh_data = 0; + sa_new->i_proposals = 0; + sa_new->r_proposals = 0; +} + static int ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, @@ -1362,22 +1554,29 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, int p = 0; u8 payload = ike->nextpayload; u8 *plaintext = 0; - u8 rekeying = 0; - u8 nonce[IKEV2_NONCE_SIZE]; - + ikev2_rekey_t *rekey; ike_payload_header_t *ikep; ikev2_notify_t *n = 0; ikev2_ts_t *tsi = 0; ikev2_ts_t *tsr = 0; ikev2_sa_proposal_t *proposal = 0; ikev2_child_sa_t *child_sa; - u32 dlen = 0; + u32 dlen = 0, src; u16 plen; + const u8 *nonce = 0; + int nonce_len = 0; + ikev2_sa_t sar; - ikev2_elog_exchange ("ispi %lx rspi %lx CREATE_CHILD_SA received " - "from ", clib_host_to_net_u64 (ike->ispi), - clib_host_to_net_u64 (ike->rspi), - ip_addr_v4 (&sa->raddr).as_u32, + clib_memset (&sar, 0, sizeof (sar)); + + if (sa->is_initiator) + src = ip_addr_v4 (&sa->raddr).as_u32; + else + src = ip_addr_v4 (&sa->iaddr).as_u32; + + ikev2_elog_exchange ("ispi %lx rspi %lx CREATE_CHILD_SA received from", + clib_host_to_net_u64 (ike->ispi), + clib_host_to_net_u64 (ike->rspi), src, ip_addr_version (&sa->raddr) == AF_IP4); plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len, &dlen); @@ -1398,13 +1597,23 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, { proposal = ikev2_parse_sa_payload (ikep, current_length); } + else if (payload == IKEV2_PAYLOAD_KE) + { + if (!ikev2_parse_ke_payload (ikep, current_length, &sar, + &sar.i_dh_data)) + goto cleanup_and_exit; + } else if (payload == IKEV2_PAYLOAD_NOTIFY) { - n = ikev2_parse_notify_payload (ikep, current_length); - if (n->msg_type == IKEV2_NOTIFY_MSG_REKEY_SA) + ikev2_notify_t *n0; + n0 = ikev2_parse_notify_payload (ikep, current_length); + if (n0->msg_type == IKEV2_NOTIFY_MSG_REKEY_SA) { - rekeying = 1; + vec_free (n); + n = n0; } + else + vec_free (n0); } else if (payload == IKEV2_PAYLOAD_DELETE) { @@ -1416,7 +1625,9 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, } else if (payload == IKEV2_PAYLOAD_NONCE) { - ikev2_parse_nonce_payload (ikep, current_length, nonce); + nonce_len = ikev2_parse_nonce_payload (ikep, current_length, &nonce); + if (nonce_len <= 0) + goto cleanup_and_exit; } else if (payload == IKEV2_PAYLOAD_TSI) { @@ -1440,12 +1651,17 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, p += plen; } - if (sa->is_initiator && proposal - && proposal->protocol_id == IKEV2_PROTOCOL_ESP) + if (!proposal || !nonce || + (proposal->protocol_id != IKEV2_PROTOCOL_ESP && + proposal->protocol_id != IKEV2_PROTOCOL_IKE)) + goto cleanup_and_exit; + + if (sa->is_initiator) { - ikev2_rekey_t *rekey = sa->rekey; + rekey = sa->rekey; if (vec_len (rekey) == 0) goto cleanup_and_exit; + rekey->notify_type = 0; rekey->protocol_id = proposal->protocol_id; rekey->i_proposal = ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP); @@ -1455,41 +1671,71 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, rekey->tsr = tsr; /* update Nr */ vec_reset_length (sa->r_nonce); - vec_add (sa->r_nonce, nonce, IKEV2_NONCE_SIZE); + vec_add (sa->r_nonce, nonce, nonce_len); child_sa = ikev2_sa_get_child (sa, rekey->ispi, IKEV2_PROTOCOL_ESP, 1); if (child_sa) { child_sa->rekey_retries = 0; } } - else if (rekeying) + else { - ikev2_rekey_t *rekey; - child_sa = ikev2_sa_get_child (sa, n->spi, n->protocol_id, 1); - if (!child_sa) + if (n) { - ikev2_elog_uint (IKEV2_LOG_ERROR, "child SA spi %lx not found", - n->spi); - goto cleanup_and_exit; + child_sa = ikev2_sa_get_child (sa, n->spi, n->protocol_id, 1); + if (!child_sa) + { + ikev2_elog_uint (IKEV2_LOG_ERROR, "child SA spi %lx not found", + n->spi); + goto cleanup_and_exit; + } + vec_add2 (sa->rekey, rekey, 1); + rekey->notify_type = 0; + rekey->kex = 0; + rekey->protocol_id = n->protocol_id; + rekey->spi = n->spi; + if (sa->old_remote_id_present) + { + rekey->notify_type = IKEV2_NOTIFY_MSG_TEMPORARY_FAILURE; + vec_free (proposal); + vec_free (tsr); + vec_free (tsi); + } + else if (!ikev2_process_create_child_sa_rekey ( + sa, &sar, rekey, proposal, tsi, tsr, nonce, nonce_len)) + { + vec_free (proposal); + vec_free (tsr); + vec_free (tsi); + } + } + else if (proposal[0].protocol_id == IKEV2_PROTOCOL_IKE) + { + ikev2_sa_rekey_t *sa_rekey; + if (tsi || tsr) + goto cleanup_and_exit; + sar.i_proposals = proposal; + vec_add (sar.i_nonce, nonce, nonce_len); + vec_add2 (sa->sa_rekey, sa_rekey, 1); + ikev2_process_sa_rekey (&sar, sa, sa_rekey); + } + else + { + /* create new child SA */ + vec_add2 (sa->new_child, rekey, 1); + rekey->notify_type = 0; + rekey->kex = 0; + if (!ikev2_process_create_child_sa_rekey ( + sa, &sar, rekey, proposal, tsi, tsr, nonce, nonce_len)) + { + vec_free (proposal); + vec_free (tsr); + vec_free (tsi); + } } - vec_add2 (sa->rekey, rekey, 1); - rekey->protocol_id = n->protocol_id; - rekey->spi = n->spi; - rekey->i_proposal = proposal; - rekey->r_proposal = - ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP); - rekey->tsi = tsi; - rekey->tsr = tsr; - /* update Ni */ - vec_reset_length (sa->i_nonce); - vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE); - /* generate new Nr */ - vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1); - RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE); } - else - goto cleanup_and_exit; vec_free (n); + ikev2_sa_free_all_vec (&sar); return 1; cleanup_and_exit: @@ -1497,6 +1743,7 @@ cleanup_and_exit: vec_free (proposal); vec_free (tsr); vec_free (tsi); + ikev2_sa_free_all_vec (&sar); return 0; } @@ -1543,6 +1790,25 @@ ikev2_sa_generate_authmsg (ikev2_sa_t * sa, int is_responder) return authmsg; } +static int +ikev2_match_profile (const ikev2_profile_t *p, const ikev2_id_t *id_loc, + const ikev2_id_t *id_rem, int is_initiator) +{ + /* on the initiator, IDi is always present and must match + * however on the responder, IDr (which is our local id) is optional */ + if ((is_initiator || id_loc->type != 0) && + !ikev2_is_id_equal (&p->loc_id, id_loc)) + return 0; + + /* on the initiator, we might not have configured a specific remote id + * however on the responder, the remote id should always be configured */ + if ((!is_initiator || p->rem_id.type != 0) && + !ikev2_is_id_equal (&p->rem_id, id_rem)) + return 0; + + return 1; +} + static int ikev2_ts_cmp (ikev2_ts_t * ts1, ikev2_ts_t * ts2) { @@ -1563,7 +1829,6 @@ ikev2_sa_match_ts (ikev2_sa_t * sa) ikev2_ts_t *ts, *p_tsi, *p_tsr, *tsi = 0, *tsr = 0; ikev2_id_t *id_rem, *id_loc; - /* *INDENT-OFF* */ pool_foreach (p, km->profiles) { if (sa->is_initiator) @@ -1581,9 +1846,7 @@ ikev2_sa_match_ts (ikev2_sa_t * sa) id_loc = &sa->r_id; } - /* check id */ - if (!ikev2_is_id_equal (&p->rem_id, id_rem) - || !ikev2_is_id_equal (&p->loc_id, id_loc)) + if (!ikev2_match_profile (p, id_loc, id_rem, sa->is_initiator)) continue; sa->profile_index = p - km->profiles; @@ -1608,7 +1871,6 @@ ikev2_sa_match_ts (ikev2_sa_t * sa) break; } - /* *INDENT-ON* */ if (tsi && tsr) { @@ -1625,33 +1887,16 @@ ikev2_sa_match_ts (ikev2_sa_t * sa) } } -static void -ikev2_sa_auth (ikev2_sa_t * sa) +static ikev2_profile_t * +ikev2_select_profile (vlib_main_t *vm, ikev2_main_t *km, ikev2_sa_t *sa, + ikev2_sa_transform_t *tr_prf, u8 *key_pad) { - ikev2_main_t *km = &ikev2_main; - ikev2_profile_t *p, *sel_p = 0; - u8 *authmsg, *key_pad, *psk = 0, *auth = 0; - ikev2_sa_transform_t *tr_prf; - - tr_prf = - ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); - - /* only shared key and rsa signature */ - if (!(sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC || - sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG)) - { - ikev2_elog_uint (IKEV2_LOG_ERROR, - "unsupported authentication method %u", - sa->i_auth.method); - ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); - return; - } - - key_pad = format (0, "%s", IKEV2_KEY_PAD); - authmsg = ikev2_sa_generate_authmsg (sa, sa->is_initiator); - + ikev2_profile_t *ret = 0, *p; ikev2_id_t *id_rem, *id_loc; ikev2_auth_t *sa_auth; + u8 *authmsg, *psk = 0, *auth = 0; + + authmsg = ikev2_sa_generate_authmsg (sa, sa->is_initiator); if (sa->is_initiator) { @@ -1666,59 +1911,87 @@ ikev2_sa_auth (ikev2_sa_t * sa) sa_auth = &sa->i_auth; } - /* *INDENT-OFF* */ - pool_foreach (p, km->profiles) { + pool_foreach (p, km->profiles) + { + if (!ikev2_match_profile (p, id_loc, id_rem, sa->is_initiator)) + continue; - /* check id */ - if (!ikev2_is_id_equal (&p->rem_id, id_rem) - || !ikev2_is_id_equal (&p->loc_id, id_loc)) - continue; + if (sa_auth->method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC) + { + if (!p->auth.data || + p->auth.method != IKEV2_AUTH_METHOD_SHARED_KEY_MIC) + continue; - if (sa_auth->method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC) - { - if (!p->auth.data || - p->auth.method != IKEV2_AUTH_METHOD_SHARED_KEY_MIC) - continue; + psk = ikev2_calc_prf (tr_prf, p->auth.data, key_pad); + auth = ikev2_calc_prf (tr_prf, psk, authmsg); - psk = ikev2_calc_prf(tr_prf, p->auth.data, key_pad); - auth = ikev2_calc_prf(tr_prf, psk, authmsg); + if (!clib_memcmp (auth, sa_auth->data, vec_len (sa_auth->data))) + { + ikev2_set_state (sa, IKEV2_STATE_AUTHENTICATED); + sa->auth_timestamp = vlib_time_now (vm); + vec_free (auth); + ret = p; + break; + } + else + { + ikev2_elog_uint (IKEV2_LOG_ERROR, + "shared key mismatch! ispi %lx", sa->ispi); + } + } + else if (sa_auth->method == IKEV2_AUTH_METHOD_RSA_SIG) + { + if (p->auth.method != IKEV2_AUTH_METHOD_RSA_SIG) + continue; - if (!clib_memcmp(auth, sa_auth->data, vec_len(sa_auth->data))) - { - ikev2_set_state(sa, IKEV2_STATE_AUTHENTICATED); - vec_free(auth); - sel_p = p; - break; - } + if (ikev2_verify_sign (p->auth.key, sa_auth->data, authmsg) == 1) + { + ikev2_set_state (sa, IKEV2_STATE_AUTHENTICATED); + sa->auth_timestamp = vlib_time_now (vm); + ret = p; + break; + } + else + { + ikev2_elog_uint (IKEV2_LOG_ERROR, + "cert verification failed! ispi %lx", sa->ispi); + } + } + } + vec_free (authmsg); + return ret; +} - } - else if (sa_auth->method == IKEV2_AUTH_METHOD_RSA_SIG) - { - if (p->auth.method != IKEV2_AUTH_METHOD_RSA_SIG) - continue; +static void +ikev2_sa_auth (ikev2_sa_t *sa, vlib_main_t *vm) +{ + ikev2_main_t *km = &ikev2_main; + ikev2_profile_t *sel_p = 0; + ikev2_sa_transform_t *tr_prf; + u8 *psk, *authmsg, *key_pad; - if (ikev2_verify_sign(p->auth.key, sa_auth->data, authmsg) == 1) - { - ikev2_set_state(sa, IKEV2_STATE_AUTHENTICATED); - sel_p = p; - break; - } - } + tr_prf = + ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); - vec_free(auth); - vec_free(psk); - } - /* *INDENT-ON* */ + /* only shared key and rsa signature */ + if (!(sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC || + sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG)) + { + ikev2_elog_uint (IKEV2_LOG_ERROR, "unsupported authentication method %u", + sa->i_auth.method); + ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); + return; + } + + key_pad = format (0, "%s", IKEV2_KEY_PAD); + sel_p = ikev2_select_profile (vm, km, sa, tr_prf, key_pad); if (sel_p) { + ASSERT (sa->state == IKEV2_STATE_AUTHENTICATED); sa->udp_encap = sel_p->udp_encap; sa->ipsec_over_udp_port = sel_p->ipsec_over_udp_port; - } - vec_free (authmsg); - if (sa->state == IKEV2_STATE_AUTHENTICATED) - { if (!sa->is_initiator) { vec_free (sa->r_id.data); @@ -1732,8 +2005,10 @@ ikev2_sa_auth (ikev2_sa_t * sa) if (sel_p->auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC) { vec_free (sa->r_auth.data); + psk = ikev2_calc_prf (tr_prf, sel_p->auth.data, key_pad); sa->r_auth.data = ikev2_calc_prf (tr_prf, psk, authmsg); sa->r_auth.method = IKEV2_AUTH_METHOD_SHARED_KEY_MIC; + vec_free (psk); } else if (sel_p->auth.method == IKEV2_AUTH_METHOD_RSA_SIG) { @@ -1762,11 +2037,9 @@ ikev2_sa_auth (ikev2_sa_t * sa) "profile found! ispi %lx", sa->ispi); ikev2_set_state (sa, IKEV2_STATE_AUTH_FAILED); } - vec_free (psk); vec_free (key_pad); } - static void ikev2_sa_auth_init (ikev2_sa_t * sa) { @@ -1788,15 +2061,17 @@ ikev2_sa_auth_init (ikev2_sa_t * sa) return; } - key_pad = format (0, "%s", IKEV2_KEY_PAD); authmsg = ikev2_sa_generate_authmsg (sa, 0); - psk = ikev2_calc_prf (tr_prf, sa->i_auth.data, key_pad); if (sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC) { + key_pad = format (0, "%s", IKEV2_KEY_PAD); + psk = ikev2_calc_prf (tr_prf, sa->i_auth.data, key_pad); vec_free (sa->i_auth.data); sa->i_auth.data = ikev2_calc_prf (tr_prf, psk, authmsg); sa->i_auth.method = IKEV2_AUTH_METHOD_SHARED_KEY_MIC; + vec_free (psk); + vec_free (key_pad); } else if (sa->i_auth.method == IKEV2_AUTH_METHOD_RSA_SIG) { @@ -1804,9 +2079,6 @@ ikev2_sa_auth_init (ikev2_sa_t * sa) sa->i_auth.data = ikev2_calc_sign (km->pkey, authmsg); sa->i_auth.method = IKEV2_AUTH_METHOD_RSA_SIG; } - - vec_free (psk); - vec_free (key_pad); vec_free (authmsg); } @@ -1834,8 +2106,8 @@ typedef struct 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; + ip_address_t local_ip; + ip_address_t remote_ip; ipsec_key_t loc_ckey, rem_ckey, loc_ikey, rem_ikey; u8 is_rekey; u32 old_remote_sa_id; @@ -1850,12 +2122,32 @@ 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; + tunnel_t tun_in = { + .t_flags = TUNNEL_FLAG_NONE, + .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE, + .t_dscp = 0, + .t_mode = TUNNEL_MODE_P2P, + .t_table_id = 0, + .t_hop_limit = 255, + .t_src = a->remote_ip, + .t_dst = a->local_ip, + }; + tunnel_t tun_out = { + .t_flags = TUNNEL_FLAG_NONE, + .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE, + .t_dscp = 0, + .t_mode = TUNNEL_MODE_P2P, + .t_table_id = 0, + .t_hop_limit = 255, + .t_src = a->local_ip, + .t_dst = a->remote_ip, + }; 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, + rv = ipip_add_tunnel (IPIP_TRANSPORT_IP4, ~0, &ip_addr_46 (&a->local_ip), + &ip_addr_46 (&a->remote_ip), 0, TUNNEL_ENCAP_DECAP_FLAG_NONE, IP_DSCP_CS0, TUNNEL_MODE_P2P, &sw_if_index); @@ -1894,24 +2186,18 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a) 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, TUNNEL_ENCAP_DECAP_FLAG_NONE, - IP_DSCP_CS0, NULL, a->src_port, a->dst_port); + 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, a->salt_local, + a->src_port, a->dst_port, 0, &tun_out, NULL); if (rv) goto err0; - 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, TUNNEL_ENCAP_DECAP_FLAG_NONE, - IP_DSCP_CS0, NULL, - a->ipsec_over_udp_port, a->ipsec_over_udp_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), a->salt_remote, + a->ipsec_over_udp_port, a->ipsec_over_udp_port, 0, &tun_in, NULL); if (rv) goto err1; @@ -1930,10 +2216,9 @@ err0: } static int -ikev2_create_tunnel_interface (vlib_main_t * vm, - ikev2_sa_t * sa, - ikev2_child_sa_t * child, u32 sa_index, - u32 child_index, u8 is_rekey) +ikev2_create_tunnel_interface (vlib_main_t *vm, ikev2_sa_t *sa, + ikev2_child_sa_t *child, u32 sa_index, + u32 child_index, u8 is_rekey, u8 kex) { u32 thread_index = vlib_get_thread_index (); ikev2_main_t *km = &ikev2_main; @@ -1947,6 +2232,8 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, clib_memset (&a, 0, sizeof (a)); + child->timestamp = vlib_time_now (vm); + if (!child->r_proposals) { ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN); @@ -1955,16 +2242,16 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, if (sa->is_initiator) { - ip_address_to_46 (&sa->iaddr, &a.local_ip); - ip_address_to_46 (&sa->raddr, &a.remote_ip); + ip_address_copy (&a.local_ip, &sa->iaddr); + ip_address_copy (&a.remote_ip, &sa->raddr); proposals = child->r_proposals; a.local_spi = child->r_proposals[0].spi; a.remote_spi = child->i_proposals[0].spi; } else { - ip_address_to_46 (&sa->raddr, &a.local_ip); - ip_address_to_46 (&sa->iaddr, &a.remote_ip); + ip_address_copy (&a.local_ip, &sa->raddr); + ip_address_copy (&a.remote_ip, &sa->iaddr); proposals = child->i_proposals; a.local_spi = child->i_proposals[0].spi; a.remote_spi = child->r_proposals[0].spi; @@ -2076,7 +2363,7 @@ ikev2_create_tunnel_interface (vlib_main_t * vm, } a.integ_type = integ_type; - ikev2_calc_child_keys (sa, child); + ikev2_calc_child_keys (sa, child, kex); if (sa->is_initiator) { @@ -2181,7 +2468,7 @@ typedef struct u32 sw_if_index; } ikev2_del_ipsec_tunnel_args_t; -static_always_inline u32 +static u32 ikev2_flip_alternate_sa_bit (u32 id) { u32 mask = 0x800; @@ -2199,14 +2486,12 @@ ikev2_del_tunnel_from_main (ikev2_del_ipsec_tunnel_args_t * a) 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); @@ -2263,9 +2548,50 @@ ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa, return 0; } +static void +ikev2_add_invalid_ke_payload (ikev2_sa_t *sa, ikev2_payload_chain_t *chain) +{ + u8 *data = vec_new (u8, 2); + ikev2_sa_transform_t *tr_dh = + ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_DH); + ASSERT (tr_dh && tr_dh->dh_type); + data[0] = (tr_dh->dh_type >> 8) & 0xff; + data[1] = (tr_dh->dh_type) & 0xff; + ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD, data); + vec_free (data); +} + +static void +ikev2_add_notify_payload (ikev2_sa_t *sa, ikev2_payload_chain_t *chain, + u16 notify_type) +{ + if (notify_type == IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD) + ikev2_add_invalid_ke_payload (sa, chain); + else + ikev2_payload_add_notify (chain, notify_type, 0); +} + +static void +ikev2_add_create_child_resp (ikev2_sa_t *sa, ikev2_rekey_t *rekey, + ikev2_payload_chain_t *chain) +{ + if (rekey->notify_type) + { + ikev2_add_notify_payload (sa, chain, rekey->notify_type); + return; + } + + ikev2_payload_add_sa (chain, rekey->r_proposal, 0); + ikev2_payload_add_nonce (chain, sa->r_nonce); + if (rekey->kex) + ikev2_payload_add_ke (chain, sa->dh_group, sa->r_dh_data); + ikev2_payload_add_ts (chain, rekey->tsi, IKEV2_PAYLOAD_TSI); + ikev2_payload_add_ts (chain, rekey->tsr, IKEV2_PAYLOAD_TSR); +} + static u32 -ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, - ike_header_t * ike, void *user, udp_header_t * udp) +ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike, + void *user, udp_header_t *udp, ikev2_stats_t *stats) { ikev2_main_t *km = &ikev2_main; u16 buffer_data_size = vlib_buffer_get_default_data_size (km->vlib_main); @@ -2293,20 +2619,7 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, } else if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE) { - u8 *data = vec_new (u8, 2); - ikev2_sa_transform_t *tr_dh; - tr_dh = - ikev2_sa_get_td_for_type (sa->r_proposals, - IKEV2_TRANSFORM_TYPE_DH); - ASSERT (tr_dh && tr_dh->dh_type); - - data[0] = (tr_dh->dh_type >> 8) & 0xff; - data[1] = (tr_dh->dh_type) & 0xff; - - ikev2_payload_add_notify (chain, - IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD, - data); - vec_free (data); + ikev2_add_invalid_ke_payload (sa, chain); ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE); } else if (sa->state == IKEV2_STATE_NOTIFY_AND_DELETE) @@ -2324,7 +2637,7 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, ASSERT (udp); ike->rspi = clib_host_to_net_u64 (sa->rspi); - ikev2_payload_add_sa (chain, sa->r_proposals); + ikev2_payload_add_sa (chain, sa->r_proposals, 0); ikev2_payload_add_ke (chain, sa->dh_group, sa->r_dh_data); ikev2_payload_add_nonce (chain, sa->r_nonce); @@ -2351,9 +2664,8 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, if (sa->state == IKEV2_STATE_AUTHENTICATED) { ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); - ikev2_payload_add_id (chain, &sa->i_id, IKEV2_PAYLOAD_IDI); ikev2_payload_add_auth (chain, &sa->r_auth); - ikev2_payload_add_sa (chain, sa->childs[0].r_proposals); + ikev2_payload_add_sa (chain, sa->childs[0].r_proposals, 0); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); } @@ -2393,9 +2705,12 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, else if (sa->state == IKEV2_STATE_SA_INIT) { ikev2_payload_add_id (chain, &sa->i_id, IKEV2_PAYLOAD_IDI); - ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); + /* IDr is optional when sending INIT from the initiator */ + ASSERT (sa->r_id.type != 0 || sa->is_initiator); + if (sa->r_id.type != 0) + ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); ikev2_payload_add_auth (chain, &sa->i_auth); - ikev2_payload_add_sa (chain, sa->childs[0].i_proposals); + ikev2_payload_add_sa (chain, sa->childs[0].i_proposals, 0); 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, @@ -2448,7 +2763,16 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, vec_free (data); sa->unsupported_cp = 0; } - /* else send empty response */ + else + /* else send empty response */ + { + if (ike_hdr_is_response (ike)) + { + ASSERT (stats != 0); + stats->n_keepalives++; + sa->stats.n_keepalives++; + } + } } else if (ike->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA) { @@ -2463,7 +2787,7 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, notify.spi = sa->childs[0].i_proposals->spi; *(u32 *) data = clib_host_to_net_u32 (notify.spi); - ikev2_payload_add_sa (chain, proposals); + ikev2_payload_add_sa (chain, proposals, 0); ikev2_payload_add_nonce (chain, sa->i_nonce); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); @@ -2472,35 +2796,43 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa, vec_free (data); } - else + else if (vec_len (sa->rekey) > 0) { - if (vec_len (sa->rekey) > 0) - { - ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal); - ikev2_payload_add_nonce (chain, sa->r_nonce); - ikev2_payload_add_ts (chain, sa->rekey[0].tsi, - IKEV2_PAYLOAD_TSI); - ikev2_payload_add_ts (chain, sa->rekey[0].tsr, - IKEV2_PAYLOAD_TSR); - vec_del1 (sa->rekey, 0); - } - else if (sa->unsupported_cp) - { - u8 *data = vec_new (u8, 1); - - data[0] = sa->unsupported_cp; - ikev2_payload_add_notify (chain, - IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD, - data); - vec_free (data); - sa->unsupported_cp = 0; - } + ikev2_add_create_child_resp (sa, &sa->rekey[0], chain); + vec_del1 (sa->rekey, 0); + } + else if (vec_len (sa->new_child) > 0) + { + ikev2_add_create_child_resp (sa, &sa->new_child[0], chain); + vec_del1 (sa->new_child, 0); + } + else if (vec_len (sa->sa_rekey) > 0) + { + if (sa->sa_rekey[0].notify_type) + ikev2_add_notify_payload (sa, chain, sa->sa_rekey[0].notify_type); else { - ikev2_payload_add_notify (chain, - IKEV2_NOTIFY_MSG_NO_ADDITIONAL_SAS, - 0); + ikev2_payload_add_sa (chain, sa->sa_rekey[0].r_proposals, 1); + ikev2_payload_add_nonce (chain, sa->sa_rekey[0].r_nonce); + ikev2_payload_add_ke (chain, sa->sa_rekey[0].dh_group, + sa->sa_rekey[0].r_dh_data); } + vec_del1 (sa->sa_rekey, 0); + } + else if (sa->unsupported_cp) + { + u8 *data = vec_new (u8, 1); + + data[0] = sa->unsupported_cp; + ikev2_payload_add_notify ( + chain, IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD, data); + vec_free (data); + sa->unsupported_cp = 0; + } + else + { + ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NO_ADDITIONAL_SAS, + 0); } } @@ -2631,6 +2963,7 @@ ikev2_retransmit_sa_init_one (ikev2_sa_t * sa, ike_header_t * ike, /* req is retransmit */ if (sa->state == IKEV2_STATE_SA_INIT) { + sa->stats.n_init_retransmit++; tmp = (ike_header_t *) sa->last_sa_init_res_packet_data; u32 slen = clib_net_to_host_u32 (tmp->length); ike->ispi = tmp->ispi; @@ -2678,13 +3011,11 @@ ikev2_retransmit_sa_init (ike_header_t * ike, ip_address_t iaddr, u32 res; ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data (); - /* *INDENT-OFF* */ pool_foreach (sa, ptd->sas) { res = ikev2_retransmit_sa_init_one (sa, ike, iaddr, raddr, rlen); if (res) return res; } - /* *INDENT-ON* */ /* req is not retransmit */ return 0; @@ -2699,7 +3030,7 @@ ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike) u32 msg_id = clib_net_to_host_u32 (ike->msgid); /* new req */ - if (msg_id > sa->last_msg_id) + if (msg_id > sa->last_msg_id || sa->last_msg_id == ~0) { sa->last_msg_id = msg_id; return 0; @@ -2708,6 +3039,7 @@ ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike) /* retransmitted req */ if (msg_id == sa->last_msg_id) { + sa->stats.n_retransmit++; ike_header_t *tmp = (ike_header_t *) sa->last_res_packet_data; u32 slen = clib_net_to_host_u32 (tmp->length); ike->ispi = tmp->ispi; @@ -2761,8 +3093,8 @@ ikev2_del_sa_init (u64 ispi) sizeof (ispi)); } -static_always_inline void -ikev2_rewrite_v6_addrs (ikev2_sa_t * sa, ip6_header_t * ih) +static void +ikev2_rewrite_v6_addrs (ikev2_sa_t *sa, ip6_header_t *ih) { if (sa->is_initiator) { @@ -2776,8 +3108,8 @@ ikev2_rewrite_v6_addrs (ikev2_sa_t * sa, ip6_header_t * ih) } } -static_always_inline void -ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih) +static void +ikev2_rewrite_v4_addrs (ikev2_sa_t *sa, ip4_header_t *ih) { if (sa->is_initiator) { @@ -2791,9 +3123,9 @@ ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih) } } -static_always_inline void -ikev2_set_ip_address (ikev2_sa_t * sa, const void *iaddr, - const void *raddr, const int af) +static void +ikev2_set_ip_address (ikev2_sa_t *sa, const void *iaddr, const void *raddr, + const ip_address_family_t af) { ip_address_set (&sa->raddr, raddr, af); ip_address_set (&sa->iaddr, iaddr, af); @@ -2833,7 +3165,20 @@ ikev2_generate_sa_init_data_and_log (ikev2_sa_t * sa) ikev2_elog_error (IKEV2_GENERATE_SA_INIT_OK_ERR_UNSUPP_STR); } -static_always_inline uword +static void +ikev2_update_stats (vlib_main_t *vm, u32 node_index, ikev2_stats_t *s) +{ + vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_KEEPALIVE, + s->n_keepalives); + vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_REKEY_REQ, + s->n_rekey_req); + vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_INIT_SA_REQ, + s->n_sa_init_req); + vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_IKE_AUTH_REQ, + s->n_sa_auth_req); +} + +static uword ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, u8 is_ip4, u8 natt) { @@ -2842,8 +3187,13 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; u16 nexts[VLIB_FRAME_SIZE], *next = nexts; ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data (); + ikev2_stats_t _stats, *stats = &_stats; int res; + /* no NAT traversal for ip6 */ + ASSERT (!natt || is_ip4); + + clib_memset_u16 (stats, 0, sizeof (stats[0]) / sizeof (u16)); from = vlib_frame_vector_args (frame); vlib_get_buffers (vm, from, bufs, n_left); b = bufs; @@ -2925,6 +3275,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, if (ike_hdr_is_initiator (ike0)) { + sa0->stats.n_sa_init_req++; + stats->n_sa_init_req++; if (ike0->rspi == 0) { if (is_ip4) @@ -2972,7 +3324,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, || sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE) { ike0->flags = IKEV2_HDR_FLAG_RESPONSE; - slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats); if (~0 == slen) vlib_node_increment_counter (vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, @@ -3023,8 +3376,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, ike0->msgid = clib_net_to_host_u32 (sai->last_init_msg_id); sa0->last_init_msg_id = sai->last_init_msg_id + 1; - slen = - ikev2_generate_message (b0, sa0, ike0, 0, udp0); + slen = ikev2_generate_message (b0, sa0, ike0, 0, + udp0, stats); if (~0 == slen) vlib_node_increment_counter (vm, node->node_index, @@ -3075,28 +3428,33 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, sa0->dst_port = clib_net_to_host_u16 (udp0->src_port); res = ikev2_process_auth_req (vm, sa0, ike0, rlen); if (res) - ikev2_sa_auth (sa0); + ikev2_sa_auth (sa0, vm); else vlib_node_increment_counter (vm, node->node_index, IKEV2_ERROR_MALFORMED_PACKET, 1); if (sa0->state == IKEV2_STATE_AUTHENTICATED) { ikev2_initial_contact_cleanup (ptd, sa0); + p = hash_get (ptd->sa_by_rspi, + clib_net_to_host_u64 (ike0->rspi)); ikev2_sa_match_ts (sa0); if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE) - ikev2_create_tunnel_interface (vm, sa0, - &sa0->childs[0], - p[0], 0, 0); + ikev2_create_tunnel_interface (vm, sa0, &sa0->childs[0], + p[0], 0, 0, 0); } if (sa0->is_initiator) { + sa0->last_msg_id = ~0; ikev2_del_sa_init (sa0->ispi); } else { + sa0->stats.n_sa_auth_req++; + stats->n_sa_auth_req++; ike0->flags = IKEV2_HDR_FLAG_RESPONSE; - slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats); if (~0 == slen) vlib_node_increment_counter (vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, @@ -3168,7 +3526,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, if (ike_hdr_is_request (ike0)) { ike0->flags = IKEV2_HDR_FLAG_RESPONSE; - slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats); if (~0 == slen) vlib_node_increment_counter (vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, @@ -3206,11 +3565,12 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, goto dispatch0; } - if (sa0->rekey) + if (vec_len (sa0->rekey) > 0) { - if (sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE) + if (!sa0->rekey[0].notify_type && + sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE) { - if (sa0->childs) + if (vec_len (sa0->childs) > 0) ikev2_sa_free_all_child_sa (&sa0->childs); ikev2_child_sa_t *child; vec_add2 (sa0->childs, child, 1); @@ -3220,7 +3580,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, child->tsi = sa0->rekey[0].tsi; child->tsr = sa0->rekey[0].tsr; ikev2_create_tunnel_interface (vm, sa0, child, p[0], - child - sa0->childs, 1); + child - sa0->childs, 1, + sa0->rekey[0].kex); } if (ike_hdr_is_response (ike0)) { @@ -3228,14 +3589,71 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, } else { + stats->n_rekey_req++; + sa0->stats.n_rekey_req++; ike0->flags = IKEV2_HDR_FLAG_RESPONSE; - slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0); + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats); if (~0 == slen) vlib_node_increment_counter (vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, 1); } } + else if (vec_len (sa0->new_child) > 0) + { + ikev2_child_sa_t *c; + vec_add2 (sa0->childs, c, 1); + memset (c, 0, sizeof (*c)); + c->r_proposals = sa0->new_child[0].r_proposal; + c->i_proposals = sa0->new_child[0].i_proposal; + c->tsi = sa0->new_child[0].tsi; + c->tsr = sa0->new_child[0].tsr; + ikev2_create_tunnel_interface (vm, sa0, c, p[0], + c - sa0->childs, 0, + sa0->new_child[0].kex); + if (ike_hdr_is_request (ike0)) + { + ike0->flags = IKEV2_HDR_FLAG_RESPONSE; + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0, 0); + if (~0 == slen) + vlib_node_increment_counter ( + vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, 1); + } + } + else if (vec_len (sa0->sa_rekey) > 0) + { + if (!sa0->sa_rekey[0].notify_type) + { + ikev2_sa_t *sar, *tmp = 0; + pool_get (ptd->sas, tmp); + sa0 = pool_elt_at_index (ptd->sas, p[0]); + /* swap old/new SAs to keep index and inherit IPsec SA */ + clib_memcpy_fast (tmp, sa0, sizeof (*tmp)); + sar = sa0; + sa0 = tmp; + hash_set (ptd->sa_by_rspi, sa0->rspi, sa0 - ptd->sas); + p = hash_get (ptd->sa_by_rspi, sa0->rspi); + ikev2_complete_sa_rekey (sar, sa0, &sa0->sa_rekey[0]); + hash_set (ptd->sa_by_rspi, sar->rspi, sar - ptd->sas); + } + if (ike_hdr_is_response (ike0)) + { + vec_free (sa0->sa_rekey); + } + else + { + stats->n_rekey_req++; + sa0->stats.n_rekey_req++; + ike0->flags = IKEV2_HDR_FLAG_RESPONSE; + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats); + if (~0 == slen) + vlib_node_increment_counter ( + vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, 1); + } + } } } else @@ -3318,6 +3736,7 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, b += 1; } + ikev2_update_stats (vm, node->node_index, stats); vlib_node_increment_counter (vm, node->node_index, IKEV2_ERROR_PROCESSED, frame->n_vectors); vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); @@ -3343,7 +3762,6 @@ ikev2_ip6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) return ikev2_node_internal (vm, node, frame, 0 /* is_ip4 */, 0); } -/* *INDENT-OFF* */ VLIB_REGISTER_NODE (ikev2_node_ip4,static) = { .function = ikev2_ip4, .name = "ikev2-ip4", @@ -3351,8 +3769,8 @@ VLIB_REGISTER_NODE (ikev2_node_ip4,static) = { .format_trace = format_ikev2_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(ikev2_error_strings), - .error_strings = ikev2_error_strings, + .n_errors = IKEV2_N_ERROR, + .error_counters = ikev2_error_counters, .n_next_nodes = IKEV2_IP4_N_NEXT, .next_nodes = { @@ -3368,8 +3786,8 @@ VLIB_REGISTER_NODE (ikev2_node_ip4_natt,static) = { .format_trace = format_ikev2_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(ikev2_error_strings), - .error_strings = ikev2_error_strings, + .n_errors = IKEV2_N_ERROR, + .error_counters = ikev2_error_counters, .n_next_nodes = IKEV2_IP4_N_NEXT, .next_nodes = { @@ -3385,8 +3803,8 @@ VLIB_REGISTER_NODE (ikev2_node_ip6,static) = { .format_trace = format_ikev2_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(ikev2_error_strings), - .error_strings = ikev2_error_strings, + .n_errors = IKEV2_N_ERROR, + .error_counters = ikev2_error_counters, .n_next_nodes = IKEV2_IP6_N_NEXT, .next_nodes = { @@ -3394,7 +3812,6 @@ VLIB_REGISTER_NODE (ikev2_node_ip6,static) = { [IKEV2_NEXT_IP6_ERROR_DROP] = "error-drop", }, }; -/* *INDENT-ON* */ // set ikev2 proposals when vpp is used as initiator static clib_error_t * @@ -3635,55 +4052,23 @@ 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) +static 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); - } + ipsec_register_udp_port (port, 0 /* is_ip4 */); + ipsec_register_udp_port (port, 1 /* is_ip4 */); p->ipsec_over_udp_port = port; return 0; } -static_always_inline void -ikev2_unregister_udp_port (ikev2_profile_t * p) +static void +ikev2_unregister_udp_port (ikev2_profile_t *p) { - ikev2_main_t *km = &ikev2_main; - uword *v; - if (p->ipsec_over_udp_port == IPSEC_UDP_PORT_NONE) return; - v = hash_get (km->udp_ports, p->ipsec_over_udp_port); - if (!v) - return; - - v[0]--; - - if (v[0] == 0) - { - udp_unregister_dst_port (km->vlib_main, p->ipsec_over_udp_port, 1); - hash_unset (km->udp_ports, p->ipsec_over_udp_port); - } - + ipsec_unregister_udp_port (p->ipsec_over_udp_port, 0 /* is_ip4 */); + ipsec_unregister_udp_port (p->ipsec_over_udp_port, 1 /* is_ip4 */); p->ipsec_over_udp_port = IPSEC_UDP_PORT_NONE; } @@ -3722,7 +4107,7 @@ ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm, ike0->flags = 0; ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id); sa->last_init_msg_id += 1; - len = ikev2_generate_message (b0, sa, ike0, 0, 0); + len = ikev2_generate_message (b0, sa, ike0, 0, 0, 0); if (~0 == len) return; @@ -3768,12 +4153,10 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p) u32 *sai; u32 *del_sai = 0; - /* *INDENT-OFF* */ pool_foreach (sa, km->sais) { if (pi == sa->profile_index) vec_add1 (del_sai, sa - km->sais); } - /* *INDENT-ON* */ vec_foreach (sai, del_sai) { @@ -3786,12 +4169,10 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p) vec_foreach (tkm, km->per_thread_data) { - /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas) { if (sa->profile_index != ~0 && pi == sa->profile_index) vec_add1 (del_sai, sa - tkm->sas); } - /* *INDENT-ON* */ vec_foreach (sai, del_sai) { @@ -3805,6 +4186,12 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p) vec_free (del_sai); } +static void +ikev2_profile_responder_free (ikev2_responder_t *r) +{ + vec_free (r->hostname); +} + static void ikev2_profile_free (ikev2_profile_t * p) { @@ -3814,16 +4201,57 @@ ikev2_profile_free (ikev2_profile_t * p) if (p->auth.key) EVP_PKEY_free (p->auth.key); + ikev2_profile_responder_free (&p->responder); + vec_free (p->loc_id.data); vec_free (p->rem_id.data); } +static void +ikev2_bind (vlib_main_t *vm, ikev2_main_t *km) +{ + if (0 == km->bind_refcount) + { + udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip4.index, 1); + udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip6.index, 0); + udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip4.index, 1); + udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip6.index, 0); + + vlib_punt_register (km->punt_hdl, + ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0], + "ikev2-ip4-natt"); + } + + km->bind_refcount++; +} + +static void +ikev2_unbind (vlib_main_t *vm, ikev2_main_t *km) +{ + km->bind_refcount--; + if (0 == km->bind_refcount) + { + vlib_punt_unregister (km->punt_hdl, + ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0], + "ikev2-ip4-natt"); + + udp_unregister_dst_port (vm, IKEV2_PORT_NATT, 0); + udp_unregister_dst_port (vm, IKEV2_PORT_NATT, 1); + udp_unregister_dst_port (vm, IKEV2_PORT, 0); + udp_unregister_dst_port (vm, IKEV2_PORT, 1); + } +} + +static void ikev2_lazy_init (ikev2_main_t *km); + clib_error_t * ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) { ikev2_main_t *km = &ikev2_main; ikev2_profile_t *p; + ikev2_lazy_init (km); + if (is_add) { if (ikev2_profile_index_by_name (name)) @@ -3837,6 +4265,8 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add) p->tun_itf = ~0; uword index = p - km->profiles; mhash_set_mem (&km->profile_index_by_name, name, &index, 0); + + ikev2_bind (vm, km); } else { @@ -3844,6 +4274,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_unbind (vm, km); + ikev2_unregister_udp_port (p); ikev2_cleanup_profile_sessions (km, p); @@ -3935,8 +4367,8 @@ ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data, return 0; } -static_always_inline void -ikev2_set_ts_type (ikev2_ts_t * ts, const ip_address_t * addr) +static void +ikev2_set_ts_type (ikev2_ts_t *ts, const ip_address_t *addr) { if (ip_addr_version (addr) == AF_IP4) ts->ts_type = TS_IPV4_ADDR_RANGE; @@ -3944,9 +4376,9 @@ ikev2_set_ts_type (ikev2_ts_t * ts, const ip_address_t * addr) ts->ts_type = TS_IPV6_ADDR_RANGE; } -static_always_inline void -ikev2_set_ts_addrs (ikev2_ts_t * ts, const ip_address_t * start, - const ip_address_t * end) +static void +ikev2_set_ts_addrs (ikev2_ts_t *ts, const ip_address_t *start, + const ip_address_t *end) { ip_address_copy (&ts->start_addr, start); ip_address_copy (&ts->end_addr, end); @@ -3991,6 +4423,27 @@ ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id, return 0; } +clib_error_t * +ikev2_set_profile_responder_hostname (vlib_main_t *vm, u8 *name, u8 *hostname, + 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->responder.is_resolved = 0; + p->responder.sw_if_index = sw_if_index; + p->responder.hostname = vec_dup (hostname); + + return 0; +} clib_error_t * ikev2_set_profile_responder (vlib_main_t * vm, u8 * name, @@ -4007,6 +4460,7 @@ ikev2_set_profile_responder (vlib_main_t * vm, u8 * name, return r; } + p->responder.is_resolved = 1; p->responder.sw_if_index = sw_if_index; ip_address_copy (&p->responder.addr, &addr); @@ -4021,15 +4475,15 @@ ikev2_set_profile_ike_transforms (vlib_main_t * vm, u8 * name, u32 crypto_key_size) { 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; - } + return clib_error_return (0, "unknown profile %v", name); + + if ((IKEV2_TRANSFORM_INTEG_TYPE_NONE != integ_alg) + + (IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16 == crypto_alg) != + 1) + return clib_error_return (0, "invalid cipher + integrity algorithm"); p->ike_ts.crypto_alg = crypto_alg; p->ike_ts.integ_alg = integ_alg; @@ -4086,9 +4540,7 @@ 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; @@ -4102,10 +4554,6 @@ ikev2_set_profile_ipsec_udp_port (vlib_main_t * vm, u8 * name, u16 port, } else { - v = hash_get (km->udp_ports, port); - if (!v) - return VNET_API_ERROR_IKE_NO_PORT; - if (p->ipsec_over_udp_port == IPSEC_UDP_PORT_NONE) return VNET_API_ERROR_INVALID_VALUE; @@ -4181,6 +4629,38 @@ ikev2_get_if_address (u32 sw_if_index, ip_address_family_t af, return 0; } +static clib_error_t * +ikev2_resolve_responder_hostname (vlib_main_t *vm, ikev2_responder_t *r) +{ + ikev2_main_t *km = &ikev2_main; + dns_cache_entry_t *ep = 0; + dns_pending_request_t _t0, *t0 = &_t0; + dns_resolve_name_t _rn, *rn = &_rn; + u8 *name; + int rv; + + if (!km->dns_resolve_name_ptr) + return clib_error_return (0, "cannot load symbols from dns plugin"); + + t0->request_type = DNS_API_PENDING_NAME_TO_IP; + /* VPP main curse: IKEv2 uses only non-NULL terminated vectors internally + * whereas DNS resolver expects a NULL-terminated C-string */ + name = vec_dup (r->hostname); + vec_terminate_c_string (name); + rv = ((__typeof__ (dns_resolve_name) *) km->dns_resolve_name_ptr) (name, &ep, + t0, rn); + vec_free (name); + if (rv < 0) + return clib_error_return (0, "dns lookup failure"); + + if (ep == 0) + return 0; + + ip_address_copy (&r->addr, &rn->address); + r->is_resolved = 1; + return 0; +} + clib_error_t * ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) { @@ -4191,7 +4671,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) ike_header_t *ike0; u32 bi0 = 0; int len = sizeof (ike_header_t), valid_ip = 0; - ip_address_t if_ip = ip_address_initializer; + ip_address_t src_if_ip = ip_address_initializer; p = ikev2_profile_index_by_name (name); @@ -4201,15 +4681,26 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) return r; } - if (p->responder.sw_if_index == ~0 - || ip_address_is_zero (&p->responder.addr)) + if (p->responder.sw_if_index == ~0 || + (ip_address_is_zero (&p->responder.addr) && + vec_len (p->responder.hostname) == 0)) { r = clib_error_return (0, "responder not set for profile %v", name); return r; } - if (ikev2_get_if_address (p->responder.sw_if_index, - ip_addr_version (&p->responder.addr), &if_ip)) + if (!p->responder.is_resolved) + { + /* try to resolve using dns plugin + * success does not mean we have resolved the name */ + r = ikev2_resolve_responder_hostname (vm, &p->responder); + if (r) + return r; + } + + if (p->responder.is_resolved && + ikev2_get_if_address (p->responder.sw_if_index, + ip_addr_version (&p->responder.addr), &src_if_ip)) { valid_ip = 1; } @@ -4227,7 +4718,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) proposals[0].protocol_id = IKEV2_PROTOCOL_IKE; /* Add and then cleanup proposal data */ - ikev2_payload_add_sa (chain, proposals); + ikev2_payload_add_sa (chain, proposals, 0); ikev2_sa_free_proposal_vector (&proposals); sa.is_initiator = 1; @@ -4261,12 +4752,12 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) sa.childs[0].i_proposals[0].protocol_id = IKEV2_PROTOCOL_ESP; RAND_bytes ((u8 *) & sa.childs[0].i_proposals[0].spi, sizeof (sa.childs[0].i_proposals[0].spi)); + sa.childs[0].i_proposals[0].spi &= 0xffffffff; /* Add NAT detection notification messages (mandatory) */ - u8 *nat_detection_sha1 = - ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa.ispi), - clib_host_to_net_u64 (sa.rspi), - &if_ip, clib_host_to_net_u16 (IKEV2_PORT)); + u8 *nat_detection_sha1 = ikev2_compute_nat_sha1 ( + clib_host_to_net_u64 (sa.ispi), clib_host_to_net_u64 (sa.rspi), &src_if_ip, + clib_host_to_net_u16 (IKEV2_PORT)); ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP, nat_detection_sha1); @@ -4320,7 +4811,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) vec_add (sa.last_sa_init_req_packet_data, ike0, len); /* add data to the SA then add it to the pool */ - ip_address_copy (&sa.iaddr, &if_ip); + ip_address_copy (&sa.iaddr, &src_if_ip); ip_address_copy (&sa.raddr, &p->responder.addr); sa.i_id.type = p->loc_id.type; sa.i_id.data = vec_dup (p->loc_id.data); @@ -4343,8 +4834,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) if (valid_ip) { - ikev2_send_ike (vm, &if_ip, &p->responder.addr, bi0, len, - IKEV2_PORT, sa.dst_port, sa.sw_if_index); + ikev2_send_ike (vm, &src_if_ip, &p->responder.addr, bi0, len, IKEV2_PORT, + sa.dst_port, sa.sw_if_index); ikev2_elog_exchange ("ispi %lx rspi %lx IKEV2_EXCHANGE_SA_INIT sent to ", @@ -4352,14 +4843,6 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) ip_addr_v4 (&p->responder.addr).as_u32, ip_addr_version (&p->responder.addr) == AF_IP4); } - else - { - r = - clib_error_return (0, "interface %U does not have any IP address!", - format_vnet_sw_if_index_name, vnet_get_main (), - p->responder.sw_if_index); - return r; - } return 0; } @@ -4392,7 +4875,7 @@ ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, sa->del->spi = csa->i_proposals->spi; ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id); sa->last_init_msg_id += 1; - len = ikev2_generate_message (b0, sa, ike0, 0, 0); + len = ikev2_generate_message (b0, sa, ike0, 0, 0, 0); if (~0 == len) return; @@ -4421,7 +4904,6 @@ ikev2_initiate_delete_child_sa (vlib_main_t * vm, u32 ispi) ikev2_sa_t *sa; if (fchild) break; - /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas) { fchild = ikev2_sa_get_child(sa, ispi, IKEV2_PROTOCOL_ESP, 1); if (fchild) @@ -4430,7 +4912,6 @@ ikev2_initiate_delete_child_sa (vlib_main_t * vm, u32 ispi) break; } } - /* *INDENT-ON* */ } if (!fchild || !fsa) @@ -4461,7 +4942,6 @@ ikev2_initiate_delete_ike_sa (vlib_main_t * vm, u64 ispi) ikev2_sa_t *sa; if (fsa) break; - /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas) { if (sa->ispi == ispi) { @@ -4470,7 +4950,6 @@ ikev2_initiate_delete_ike_sa (vlib_main_t * vm, u64 ispi) break; } } - /* *INDENT-ON* */ } if (!fsa) @@ -4512,13 +4991,15 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, ikev2_rekey_t *rekey; vec_reset_length (sa->rekey); vec_add2 (sa->rekey, rekey, 1); + rekey->kex = 0; ikev2_sa_proposal_t *proposals = vec_dup (csa->i_proposals); /*need new ispi */ RAND_bytes ((u8 *) & proposals[0].spi, sizeof (proposals[0].spi)); + proposals[0].spi &= 0xffffffff; rekey->spi = proposals[0].spi; rekey->ispi = csa->i_proposals->spi; - len = ikev2_generate_message (b0, sa, ike0, proposals, 0); + len = ikev2_generate_message (b0, sa, ike0, proposals, 0, 0); if (~0 == len) return; @@ -4544,7 +5025,6 @@ ikev2_initiate_rekey_child_sa (vlib_main_t * vm, u32 ispi) ikev2_sa_t *sa; if (fchild) break; - /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas) { fchild = ikev2_sa_get_child(sa, ispi, IKEV2_PROTOCOL_ESP, 1); if (fchild) @@ -4553,7 +5033,6 @@ ikev2_initiate_rekey_child_sa (vlib_main_t * vm, u32 ispi) break; } } - /* *INDENT-ON* */ } if (!fchild || !fsa) @@ -4586,12 +5065,10 @@ ikev2_sa_del (ikev2_profile_t * p, u32 sw_if_index) vec_foreach (tkm, km->per_thread_data) { - /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas) { if (ikev2_sa_sw_if_match (sa, sw_if_index)) vec_add1 (sa_vec, sa); } - /* *INDENT-ON* */ vec_foreach (sap, sa_vec) { @@ -4601,12 +5078,10 @@ ikev2_sa_del (ikev2_profile_t * p, u32 sw_if_index) } vec_free (sa_vec); - /* *INDENT-OFF* */ pool_foreach (sa, km->sais) { if (ikev2_sa_sw_if_match (sa, sw_if_index)) vec_add1 (ispi_vec, sa->ispi); } - /* *INDENT-ON* */ vec_foreach (ispi, ispi_vec) { @@ -4625,12 +5100,10 @@ ikev2_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) if (is_add) return 0; - /* *INDENT-OFF* */ pool_foreach (p, km->profiles) { if (p->responder.sw_if_index == sw_if_index) ikev2_sa_del (p, sw_if_index); } - /* *INDENT-ON* */ return 0; } @@ -4641,64 +5114,24 @@ clib_error_t * ikev2_init (vlib_main_t * vm) { ikev2_main_t *km = &ikev2_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - int thread_id; clib_memset (km, 0, sizeof (ikev2_main_t)); + + km->log_level = IKEV2_LOG_ERROR; + km->log_class = vlib_log_register_class ("ikev2", 0); + 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_aligned (km->per_thread_data, tm->n_vlib_mains - 1, - CLIB_CACHE_LINE_BYTES); - for (thread_id = 0; thread_id < tm->n_vlib_mains; thread_id++) - { - ikev2_main_per_thread_data_t *ptd = - vec_elt_at_index (km->per_thread_data, thread_id); - - ptd->sa_by_rspi = hash_create (0, sizeof (uword)); - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - ptd->evp_ctx = EVP_CIPHER_CTX_new (); - ptd->hmac_ctx = HMAC_CTX_new (); -#else - EVP_CIPHER_CTX_init (&ptd->_evp_ctx); - ptd->evp_ctx = &ptd->_evp_ctx; - HMAC_CTX_init (&(ptd->_hmac_ctx)); - ptd->hmac_ctx = &ptd->_hmac_ctx; -#endif - } - - 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, IKEV2_PORT, ikev2_node_ip4.index, 1); - udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip6.index, 0); - udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip4.index, 1); - udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip6.index, 0); - - vlib_punt_hdl_t punt_hdl = vlib_punt_client_register ("ikev2-ip4-natt"); - vlib_punt_register (punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0], - "ikev2-ip4-natt"); - ikev2_cli_reference (); - - km->log_level = IKEV2_LOG_ERROR; - km->log_class = vlib_log_register_class ("ikev2", 0); return 0; } -/* *INDENT-OFF* */ -VLIB_INIT_FUNCTION (ikev2_init) = -{ - .runs_after = VLIB_INITS("ipsec_init", "ipsec_punt_init"), +VLIB_INIT_FUNCTION (ikev2_init) = { + .runs_after = VLIB_INITS ("ipsec_init", "ipsec_punt_init"), }; -/* *INDENT-ON* */ static u8 ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa, @@ -4769,14 +5202,12 @@ ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa, ip_addr_bytes (&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); @@ -4857,7 +5288,6 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) ikev2_sa_t *sa; if (fchild) break; - /* *INDENT-OFF* */ pool_foreach (sa, tkm->sas) { fchild = ikev2_sa_get_child(sa, ipsec_sa->spi, IKEV2_PROTOCOL_ESP, 1); if (fchild) @@ -4866,7 +5296,6 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) break; } } - /* *INDENT-ON* */ } vlib_get_combined_counter (&ipsec_sa_counters, ipsec_sa->stat_index, &counts); @@ -4884,15 +5313,44 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa) } static void -ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa) +ikev2_process_pending_sa_init_one (vlib_main_t *vm, ikev2_main_t *km, + ikev2_sa_t *sa) { ikev2_profile_t *p; u32 bi0; u8 *nat_sha, *np; + p = pool_elt_at_index (km->profiles, sa->profile_index); + + if (!p->responder.is_resolved) + { + clib_error_t *r = ikev2_resolve_responder_hostname (vm, &p->responder); + if (r) + { + clib_error_free (r); + return; + } + + if (!p->responder.is_resolved) + return; + + ip_address_copy (&sa->raddr, &p->responder.addr); + + /* update nat detection destination hash */ + np = ikev2_find_ike_notify_payload ( + (ike_header_t *) sa->last_sa_init_req_packet_data, + IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP); + if (np) + { + nat_sha = ikev2_compute_nat_sha1 ( + clib_host_to_net_u64 (sa->ispi), clib_host_to_net_u64 (sa->rspi), + &sa->raddr, clib_host_to_net_u16 (sa->dst_port)); + clib_memcpy_fast (np, nat_sha, vec_len (nat_sha)); + vec_free (nat_sha); + } + } if (ip_address_is_zero (&sa->iaddr)) { - p = pool_elt_at_index (km->profiles, sa->profile_index); if (!ikev2_get_if_address (p->responder.sw_if_index, ip_addr_version (&p->responder.addr), &sa->iaddr)) @@ -4929,22 +5387,20 @@ ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa) } static void -ikev2_process_pending_sa_init (ikev2_main_t * km) +ikev2_process_pending_sa_init (vlib_main_t *vm, 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; - ikev2_process_pending_sa_init_one (km, sa); + ikev2_process_pending_sa_init_one (vm, km, sa); })); - /* *INDENT-ON* */ } static void @@ -4972,7 +5428,7 @@ ikev2_send_informational_request (ikev2_sa_t * sa) ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id); ike0->flags = 0; sa->last_init_msg_id += 1; - len = ikev2_generate_message (b0, sa, ike0, 0, 0); + len = ikev2_generate_message (b0, sa, ike0, 0, 0, 0); if (~0 == len) return; @@ -5002,8 +5458,8 @@ ikev2_disable_dpd (void) km->dpd_disabled = 1; } -static_always_inline int -ikev2_mngr_process_responder_sas (ikev2_sa_t * sa) +static int +ikev2_mngr_process_responder_sas (ikev2_sa_t *sa) { ikev2_main_t *km = &ikev2_main; vlib_main_t *vm = km->vlib_main; @@ -5030,11 +5486,13 @@ 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; + /* lazy init will wake it up */ + vlib_process_wait_for_event (vm); + while (1) { vlib_process_wait_for_event_or_clock (vm, 2); @@ -5047,34 +5505,38 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, 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->state != IKEV2_STATE_AUTHENTICATED) - continue; + if (sa->state == IKEV2_STATE_SA_INIT) + { + if (vec_len (sa->childs) > 0) + vec_add1 (to_be_deleted, sa - tkm->sas); + } + else if (sa->state != IKEV2_STATE_AUTHENTICATED) + continue; - 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; + 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) - ikev2_mngr_process_child_sa(sa, c, del_old_ids); + vec_foreach (c, sa->childs) + ikev2_mngr_process_child_sa (sa, c, del_old_ids); - if (!km->dpd_disabled && ikev2_mngr_process_responder_sas (sa)) - vec_add1 (to_be_deleted, sa - tkm->sas); - } - /* *INDENT-ON* */ + if (!km->dpd_disabled && ikev2_mngr_process_responder_sas (sa)) + vec_add1 (to_be_deleted, sa - tkm->sas); + } vec_foreach (sai, to_be_deleted) { sa = pool_elt_at_index (tkm->sas, sai[0]); - u8 reinitiate = (sa->is_initiator && sa->profile_index != ~0); + const u32 profile_index = sa->profile_index; + const int reinitiate = (sa->is_initiator && profile_index != ~0); vec_foreach (c, sa->childs) { ikev2_delete_tunnel_interface (km->vnet_main, sa, c); @@ -5086,7 +5548,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, if (reinitiate) { - p = pool_elt_at_index (km->profiles, sa->profile_index); + p = pool_elt_at_index (km->profiles, profile_index); if (p) { clib_error_t *e = ikev2_initiate_sa_init (vm, p->name); @@ -5103,18 +5565,16 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, /* process ipsec sas */ ipsec_sa_t *sa; - /* *INDENT-OFF* */ - pool_foreach (sa, im->sad) { - ikev2_mngr_process_ipsec_sa(sa); - } - /* *INDENT-ON* */ + pool_foreach (sa, ipsec_sa_pool) + { + ikev2_mngr_process_ipsec_sa (sa); + } - ikev2_process_pending_sa_init (km); + ikev2_process_pending_sa_init (vm, km); } return 0; } -/* *INDENT-OFF* */ VLIB_REGISTER_NODE (ikev2_mngr_process_node, static) = { .function = ikev2_mngr_process_fn, .type = VLIB_NODE_TYPE_PROCESS, @@ -5122,11 +5582,60 @@ VLIB_REGISTER_NODE (ikev2_mngr_process_node, static) = { "ikev2-manager-process", }; +static void +ikev2_lazy_init (ikev2_main_t *km) +{ + vlib_thread_main_t *tm = vlib_get_thread_main (); + int thread_id; + + if (km->lazy_init_done) + return; + + ikev2_crypto_init (km); + + mhash_init_vec_string (&km->profile_index_by_name, sizeof (uword)); + + 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; thread_id++) + { + ikev2_main_per_thread_data_t *ptd = + vec_elt_at_index (km->per_thread_data, thread_id); + + ptd->sa_by_rspi = hash_create (0, sizeof (uword)); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + ptd->evp_ctx = EVP_CIPHER_CTX_new (); + ptd->hmac_ctx = HMAC_CTX_new (); +#else + EVP_CIPHER_CTX_init (&ptd->_evp_ctx); + ptd->evp_ctx = &ptd->_evp_ctx; + HMAC_CTX_init (&(ptd->_hmac_ctx)); + ptd->hmac_ctx = &ptd->_hmac_ctx; +#endif + } + + km->sa_by_ispi = hash_create (0, sizeof (uword)); + km->sw_if_indices = hash_create (0, 0); + + km->punt_hdl = vlib_punt_client_register ("ikev2"); + + km->dns_resolve_name_ptr = + vlib_get_plugin_symbol ("dns_plugin.so", "dns_resolve_name"); + if (!km->dns_resolve_name_ptr) + ikev2_log_error ("cannot load symbols from dns plugin"); + + /* wake up ikev2 process */ + vlib_process_signal_event (vlib_get_first_main (), + ikev2_mngr_process_node.index, 0, 0); + + km->lazy_init_done = 1; +} + VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, .description = "Internet Key Exchange (IKEv2) Protocol", }; -/* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON