+static u32
+ikev2_mk_local_sa_id (u32 sai, u32 ci, u32 ti)
+{
+ return (0x80000000 | (ti << 24) | (sai << 12) | ci);
+}
+
+static u32
+ikev2_mk_remote_sa_id (u32 sai, u32 ci, u32 ti)
+{
+ return (0xc0000000 | (ti << 24) | (sai << 12) | ci);
+}
+
+typedef struct
+{
+ u32 sw_if_index;
+ u32 salt_local;
+ u32 salt_remote;
+ u32 local_sa_id;
+ u32 remote_sa_id;
+ ipsec_sa_flags_t flags;
+ u32 local_spi;
+ 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;
+ ipsec_key_t loc_ckey, rem_ckey, loc_ikey, rem_ikey;
+ u8 is_rekey;
+ u32 old_remote_sa_id;
+ u16 dst_port;
+} ikev2_add_ipsec_tunnel_args_t;
+
+static void
+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;
+
+ 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,
+ TUNNEL_ENCAP_DECAP_FLAG_NONE, IP_DSCP_CS0,
+ TUNNEL_MODE_P2P, &sw_if_index);
+
+ if (rv == VNET_API_ERROR_IF_ALREADY_EXISTS)
+ {
+ if (hash_get (km->sw_if_indices, sw_if_index))
+ /* interface is managed by IKE; proceed with updating SAs */
+ rv = 0;
+ }
+ hash_set1 (km->sw_if_indices, sw_if_index);
+ }
+ else
+ {
+ sw_if_index = a->sw_if_index;
+ vnet_sw_interface_admin_up (vnet_get_main (), sw_if_index);
+ }
+
+ if (rv)
+ {
+ ikev2_elog_peers (IKEV2_LOG_ERROR, "installing ipip tunnel failed! "
+ "loc:%d.%d.%d.%d rem:%d.%d.%d.%d",
+ a->local_ip.ip4.as_u32, a->remote_ip.ip4.as_u32);
+ return;
+ }
+
+ u32 *sas_in = NULL;
+ vec_add1 (sas_in, a->remote_sa_id);
+ if (a->is_rekey)
+ {
+ ipsec_tun_protect_del (sw_if_index, NULL);
+
+ /* 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,
+ &a->loc_ckey, a->integ_type, &a->loc_ikey,
+ a->flags, 0, a->salt_local, &a->local_ip,
+ &a->remote_ip, NULL, a->dst_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), 0,
+ a->salt_remote, &a->remote_ip,
+ &a->local_ip, NULL, a->dst_port);
+
+ rv |= ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in);
+}