+ 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->ipsec_over_udp_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->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);
+ }
+
+ p->ipsec_over_udp_port = IPSEC_UDP_PORT_NONE;
+}
+
+static void
+ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm,
+ ikev2_main_per_thread_data_t * tkm,
+ ikev2_sa_t * sa)
+{
+ ikev2_main_t *km = &ikev2_main;
+ ip4_address_t *src, *dst;
+
+ /* Create the Initiator notification for IKE SA removal */
+ ike_header_t *ike0;
+ u32 bi0 = 0;
+ int len;
+
+ bi0 = ikev2_get_new_ike_header_buff (vm, &ike0);
+ if (!bi0)
+ {
+ ikev2_log_error ("buffer alloc failure");
+ return;
+ }
+
+ ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL;
+ ike0->ispi = clib_host_to_net_u64 (sa->ispi);
+ ike0->rspi = clib_host_to_net_u64 (sa->rspi);
+ vec_resize (sa->del, 1);
+ sa->del->protocol_id = IKEV2_PROTOCOL_IKE;
+ sa->del->spi = sa->ispi;
+ 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, 0);
+
+ if (sa->is_initiator)
+ {
+ src = &sa->iaddr;
+ dst = &sa->raddr;
+ }
+ else
+ {
+ dst = &sa->iaddr;
+ src = &sa->raddr;
+ }
+
+ ikev2_send_ike (vm, src, dst, bi0, len,
+ ikev2_get_port (sa), sa->dst_port, 0);
+
+ /* delete local SA */
+ ikev2_child_sa_t *c;
+ vec_foreach (c, sa->childs)
+ ikev2_delete_tunnel_interface (km->vnet_main, sa, c);
+
+ u64 rspi = sa->rspi;
+ ikev2_sa_free_all_vec (sa);
+ uword *p = hash_get (tkm->sa_by_rspi, rspi);
+ if (p)
+ {
+ hash_unset (tkm->sa_by_rspi, rspi);
+ pool_put (tkm->sas, sa);
+ }
+}
+
+static void
+ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p)
+{
+ ikev2_main_per_thread_data_t *tkm;
+ ikev2_sa_t *sa;
+ u32 pi = p - km->profiles;
+ u32 *sai;
+ u32 *del_sai = 0;
+
+ 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)
+ {
+ sa = pool_elt_at_index (tkm->sas, sai[0]);
+ ikev2_initiate_delete_ike_sa_internal (km->vlib_main, tkm, sa);
+ }
+
+ vec_reset_length (del_sai);
+ }
+
+ vec_free (del_sai);
+}
+
+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;
+
+ if (is_add)
+ {
+ if (ikev2_profile_index_by_name (name))
+ return clib_error_return (0, "policy %v already exists", name);
+
+ pool_get (km->profiles, p);
+ clib_memset (p, 0, sizeof (*p));
+ p->name = vec_dup (name);
+ p->ipsec_over_udp_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);
+ }
+ else
+ {
+ p = ikev2_profile_index_by_name (name);
+ if (!p)