+ return pool_elt_at_index (km->profiles, p[0]);
+}
+
+
+static void
+ikev2_send_ike (vlib_main_t * vm, ip_address_t * src, ip_address_t * dst,
+ u32 bi0, u32 len, u16 src_port, u16 dst_port, u32 sw_if_index)
+{
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ udp_header_t *udp0;
+ vlib_buffer_t *b0;
+ vlib_frame_t *f;
+ u32 *to_next;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ vlib_buffer_advance (b0, -sizeof (udp_header_t));
+ udp0 = vlib_buffer_get_current (b0);
+ udp0->dst_port = clib_host_to_net_u16 (dst_port);
+ udp0->src_port = clib_host_to_net_u16 (src_port);
+ udp0->length = clib_host_to_net_u16 (len + sizeof (udp_header_t));
+ udp0->checksum = 0;
+
+ if (ip_addr_version (dst) == AF_IP4)
+ {
+ vlib_buffer_advance (b0, -sizeof (ip4_header_t));
+ ip40 = vlib_buffer_get_current (b0);
+ ip40->ip_version_and_header_length = 0x45;
+ ip40->tos = 0;
+ ip40->fragment_id = 0;
+ ip40->flags_and_fragment_offset = 0;
+ ip40->ttl = 0xff;
+ ip40->protocol = IP_PROTOCOL_UDP;
+ ip40->dst_address.as_u32 = ip_addr_v4 (dst).as_u32;
+ ip40->src_address.as_u32 = ip_addr_v4 (src).as_u32;
+ b0->current_length =
+ len + sizeof (ip4_header_t) + sizeof (udp_header_t);
+ ip40->length = clib_host_to_net_u16 (b0->current_length);
+ ip40->checksum = ip4_header_checksum (ip40);
+ }
+ else
+ {
+ vlib_buffer_advance (b0, -sizeof (ip6_header_t));
+ ip60 = vlib_buffer_get_current (b0);
+
+ b0->current_length = len + sizeof (*ip60) + sizeof (udp_header_t);
+ ip60->ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 (0x6 << 28);
+ ip60->payload_length =
+ clib_host_to_net_u16 (b0->current_length - sizeof (*ip60));
+ ip60->protocol = IP_PROTOCOL_UDP;
+ ip60->hop_limit = 0xff;
+ clib_memcpy_fast (ip60->src_address.as_u8, ip_addr_v6 (src).as_u8,
+ sizeof (ip60->src_address));
+ clib_memcpy_fast (ip60->dst_address.as_u8, ip_addr_v6 (dst).as_u8,
+ sizeof (ip60->src_address));
+ }
+
+ b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = sw_if_index;
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
+
+ u32 next_index = (ip_addr_version (dst) == AF_IP4) ?
+ ip4_lookup_node.index : ip6_lookup_node.index;
+
+ /* send the request */
+ f = vlib_get_frame_to_node (vm, next_index);
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, next_index, f);
+
+}
+
+static u32
+ikev2_get_new_ike_header_buff (vlib_main_t * vm, vlib_buffer_t ** b)
+{
+ u32 bi0;
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ {
+ *b = 0;
+ return 0;
+ }
+ *b = vlib_get_buffer (vm, bi0);
+ return bi0;
+}
+
+clib_error_t *
+ikev2_set_local_key (vlib_main_t * vm, u8 * file)
+{
+ ikev2_main_t *km = &ikev2_main;
+
+ if (km->pkey)
+ EVP_PKEY_free (km->pkey);
+ km->pkey = ikev2_load_key_file (file);
+ if (km->pkey == NULL)
+ return clib_error_return (0, "load key '%s' failed", 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->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;
+ ip_address_t *src, *dst;
+ vlib_buffer_t *b0;
+
+ /* 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, &b0);
+ if (!bi0)
+ {
+ ikev2_log_error ("buffer alloc failure");
+ return;
+ }
+
+ ike0 = vlib_buffer_get_current (b0);
+ 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 (b0, sa, ike0, 0, 0);
+ if (~0 == len)
+ return;
+
+ 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);