+ ip46_address_t local_ip = ip46_address_initializer;
+ ip46_address_t remote_ip = ip46_address_initializer;
+ ipsec_key_t crypto_key, integ_key;
+ ipsec_sa_flags_t flags;
+ ip46_type_t local_ip_type, remote_ip_type;
+ ipip_transport_t transport;
+ u32 fib_index;
+
+ local_ip_type = ip_address_decode (&mp->local_ip, &local_ip);
+ remote_ip_type = ip_address_decode (&mp->remote_ip, &remote_ip);
+ transport = (IP46_TYPE_IP6 == local_ip_type ?
+ IPIP_TRANSPORT_IP6 : IPIP_TRANSPORT_IP4);
+
+ if (local_ip_type != remote_ip_type)
+ {
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ goto done;
+ }
+
+ flags = IPSEC_SA_FLAG_NONE;
+
+ if (mp->udp_encap)
+ flags |= IPSEC_SA_FLAG_UDP_ENCAP;
+ if (mp->esn)
+ flags |= IPSEC_SA_FLAG_USE_ESN;
+ if (mp->anti_replay)
+ flags |= IPSEC_SA_FLAG_USE_ANTI_REPLAY;
+
+ ipsec_mk_key (&crypto_key, mp->remote_crypto_key,
+ mp->remote_crypto_key_len);
+ ipsec_mk_key (&integ_key, mp->remote_integ_key, mp->remote_integ_key_len);
+ ipsec_mk_key (&crypto_key, mp->local_crypto_key, mp->local_crypto_key_len);
+ ipsec_mk_key (&integ_key, mp->local_integ_key, mp->local_integ_key_len);
+
+ fib_index =
+ fib_table_find (fib_proto_from_ip46 (local_ip_type),
+ ntohl (mp->tx_table_id));
+
+ if (~0 == fib_index)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_FIB;
+ goto done;
+ }
+
+ if (mp->is_add)
+ {
+ // remote = input, local = output
+ /* create an ip-ip tunnel, then the two SA, then bind them */
+ rv = ipip_add_tunnel (transport,
+ (mp->renumber ? ntohl (mp->show_instance) : ~0),
+ &local_ip,
+ &remote_ip, fib_index,
+ TUNNEL_ENCAP_DECAP_FLAG_NONE, IP_DSCP_CS0,
+ TUNNEL_MODE_P2P, &sw_if_index);
+
+ if (rv)
+ goto done;
+
+ rv = ipsec_sa_add_and_lock (ipsec_tun_mk_input_sa_id (sw_if_index),
+ ntohl (mp->remote_spi),
+ IPSEC_PROTOCOL_ESP,
+ mp->crypto_alg,
+ &crypto_key,
+ mp->integ_alg,
+ &integ_key,
+ (flags | IPSEC_SA_FLAG_IS_INBOUND),
+ ntohl (mp->tx_table_id),
+ mp->salt, &remote_ip, &local_ip, NULL,
+ IPSEC_UDP_PORT_NONE);
+
+ if (rv)
+ goto done;
+
+ rv = ipsec_sa_add_and_lock (ipsec_tun_mk_output_sa_id (sw_if_index),
+ ntohl (mp->local_spi),
+ IPSEC_PROTOCOL_ESP,
+ mp->crypto_alg,
+ &crypto_key,
+ mp->integ_alg,
+ &integ_key,
+ flags,
+ ntohl (mp->tx_table_id),
+ mp->salt, &local_ip, &remote_ip, NULL,
+ IPSEC_UDP_PORT_NONE);
+
+ if (rv)
+ goto done;
+
+ rv = ipsec_tun_protect_update_one (sw_if_index, NULL,
+ ipsec_tun_mk_output_sa_id
+ (sw_if_index),
+ ipsec_tun_mk_input_sa_id
+ (sw_if_index));
+ if (rv)
+ goto done;
+
+ /* the SAs are locked as a result of being used for proection,
+ * they cannot be removed from the API, since they cannot be refered
+ * to by the API. unlock them now, so that if the tunnel is rekeyed
+ * they-ll disapper
+ */
+ ipsec_sa_unlock_id (ipsec_tun_mk_input_sa_id (sw_if_index));
+ ipsec_sa_unlock_id (ipsec_tun_mk_output_sa_id (sw_if_index));
+ }
+ else
+ {
+ /* *INDENT-OFF* */
+ ipip_tunnel_key_t key = {
+ .transport = transport,
+ .fib_index = fib_index,
+ .src = local_ip,
+ .dst = remote_ip
+ };
+ /* *INDENT-ON* */
+
+ ipip_tunnel_t *t = ipip_tunnel_db_find (&key);
+
+ if (NULL != t)
+ {
+ rv = ipsec_tun_protect_del (t->sw_if_index, NULL);
+ ipip_del_tunnel (t->sw_if_index);
+ }
+ else
+ rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+ }
+