diff options
-rw-r--r-- | src/plugins/ikev2/ikev2.c | 121 | ||||
-rw-r--r-- | src/plugins/ikev2/ikev2_priv.h | 5 |
2 files changed, 115 insertions, 11 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index a647784e31c..dfa697fc714 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -1496,6 +1496,8 @@ typedef struct 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; } ikev2_add_ipsec_tunnel_args_t; static void @@ -1535,6 +1537,17 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a) return; } + u32 *sas_in = NULL; + vec_add1 (sas_in, a->remote_sa_id); + if (a->is_rekey) + { + /* 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, @@ -1548,8 +1561,6 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a) a->salt_remote, &a->remote_ip, &a->local_ip, NULL); - u32 *sas_in = NULL; - vec_add1 (sas_in, a->remote_sa_id); rv |= ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in); } @@ -1558,7 +1569,7 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, u32 thread_index, ikev2_sa_t * sa, ikev2_child_sa_t * child, u32 sa_index, - u32 child_index) + u32 child_index, u8 is_rekey) { ikev2_main_t *km = &ikev2_main; ipsec_crypto_alg_t encr_type; @@ -1595,6 +1606,7 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, } a.flags = IPSEC_SA_FLAG_USE_ANTI_REPLAY; + a.is_rekey = is_rekey; tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_ESN); if (tr && tr->esn_type) @@ -1749,9 +1761,34 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, child->local_sa_id = a.local_sa_id = ikev2_mk_local_sa_id (sa_index, child_index, thread_index); - child->remote_sa_id = - a.remote_sa_id = - ikev2_mk_remote_sa_id (sa_index, child_index, thread_index); + + 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); vl_api_rpc_call_main_thread (ikev2_add_tunnel_from_main, @@ -1768,6 +1805,15 @@ typedef struct 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) { @@ -1807,6 +1853,7 @@ ikev2_del_tunnel_from_main (ikev2_del_ipsec_tunnel_args_t * a) 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); @@ -2430,7 +2477,7 @@ ikev2_node_fn (vlib_main_t * vm, ikev2_create_tunnel_interface (km->vnet_main, thread_index, sa0, &sa0->childs[0], - p[0], 0); + p[0], 0, 0); } if (sa0->is_initiator) @@ -2561,7 +2608,8 @@ ikev2_node_fn (vlib_main_t * vm, ikev2_create_tunnel_interface (km->vnet_main, thread_index, sa0, child, p[0], - child - sa0->childs); + child - sa0->childs, + 1); } if (sa0->is_initiator) { @@ -3545,9 +3593,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; @@ -3590,6 +3638,48 @@ 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; } @@ -3675,9 +3765,18 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, /* *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); } })); /* *INDENT-ON* */ diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h index 5afd065b55f..8fddb7d401c 100644 --- a/src/plugins/ikev2/ikev2_priv.h +++ b/src/plugins/ikev2/ikev2_priv.h @@ -423,6 +423,11 @@ typedef struct u8 is_tun_itf_set; u32 tun_itf; + f64 old_id_expiration; + u32 current_remote_id_mask; + u32 old_remote_id; + u8 old_remote_id_present; + ikev2_child_sa_t *childs; } ikev2_sa_t; |