+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->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);
+}
+