diff options
Diffstat (limited to 'src/plugins/odp/ipsec/ipsec.c')
-rw-r--r-- | src/plugins/odp/ipsec/ipsec.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/plugins/odp/ipsec/ipsec.c b/src/plugins/odp/ipsec/ipsec.c new file mode 100644 index 00000000..339240f6 --- /dev/null +++ b/src/plugins/odp/ipsec/ipsec.c @@ -0,0 +1,263 @@ +/* + * decap.c : IPSec tunnel support + * + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vnet/vnet.h> +#include <vnet/api_errno.h> +#include <vnet/ip/ip.h> +#include <vnet/interface.h> + +#include <odp/ipsec/ipsec.h> +#include <odp/ipsec/esp.h> + +static int +add_del_sa_sess (u32 sa_index, u8 is_add) +{ + odp_crypto_main_t *ocm = &odp_crypto_main; + odp_crypto_worker_main_t *cwm; + + vec_foreach (cwm, ocm->workers) + { + sa_data_t *sa_sess_data; + u8 is_outbound; + + for (is_outbound = 0; is_outbound < 2; is_outbound++) + { + if (is_add) + { + pool_get (cwm->sa_sess_d[is_outbound], sa_sess_data); + memset (sa_sess_data, 0, sizeof (sa_sess_data[0])); + } + else + { + + sa_sess_data = + pool_elt_at_index (cwm->sa_sess_d[is_outbound], sa_index); + + if (sa_sess_data->sess) + { + if (odp_crypto_session_destroy (sa_sess_data->sess)) + { + clib_warning ("failed to free session"); + return -1; + } + } + + pool_put (cwm->sa_sess_d[is_outbound], sa_sess_data); + } + } + } + + return 0; +} + +int +create_sess (ipsec_sa_t * sa, sa_data_t * sa_sess_data, int is_outbound) +{ + odp_crypto_ses_create_err_t ses_create_rc; + odp_crypto_session_param_t crypto_params; + odp_crypto_session_param_init (&crypto_params); + + esp_main_t *em = &odp_esp_main; + int trunc_size = em->esp_integ_algs[sa->integ_alg].trunc_size; + + const int max_auth_capa_amount = 8; + odp_crypto_auth_capability_t capa[max_auth_capa_amount]; + int actual_capa_amount; + + crypto_params.auth_cipher_text = 1; + + /* Synchronous mode */ + crypto_params.pref_mode = ODP_CRYPTO_SYNC; + crypto_params.compl_queue = ODP_QUEUE_INVALID; + crypto_params.output_pool = ODP_POOL_INVALID; + + if (sa->crypto_alg == IPSEC_CRYPTO_ALG_AES_CBC_128) + { + crypto_params.cipher_alg = ODP_CIPHER_ALG_AES_CBC; + crypto_params.cipher_key.length = sa->crypto_key_len; + } + else + { + crypto_params.cipher_alg = ODP_CIPHER_ALG_NULL; + } + + switch (sa->integ_alg) + { + case IPSEC_INTEG_ALG_SHA_512_256: + crypto_params.auth_alg = ODP_AUTH_ALG_SHA512_HMAC; + break; + case IPSEC_INTEG_ALG_SHA_256_128: + crypto_params.auth_alg = ODP_AUTH_ALG_SHA256_HMAC; + break; + case IPSEC_INTEG_ALG_SHA1_96: + crypto_params.auth_alg = ODP_AUTH_ALG_SHA1_HMAC; + break; + default: + crypto_params.auth_alg = ODP_AUTH_ALG_NULL; + break; + } + + actual_capa_amount = odp_crypto_auth_capability (crypto_params.auth_alg, + capa, + max_auth_capa_amount); + int picked_capa = -1; + int i; + + for (i = 0; i < actual_capa_amount; i++) + { + if (capa[i].digest_len >= trunc_size && + capa[i].key_len >= sa->crypto_key_len) + { + picked_capa = i; + break; + } + } + + if (picked_capa == -1) + { + if (actual_capa_amount) + clib_warning + ("Failed to get matching capabilities, algorithm appears to be supported but key or digest length incompatible\n"); + else + clib_warning + ("Failed to get matching capabilities, algorithm probably not supported\n"); + return -1; + } + + sa_sess_data->key_size = capa[picked_capa].key_len; + sa_sess_data->digest_size = capa[picked_capa].digest_len; + crypto_params.auth_key.length = sa_sess_data->key_size; + crypto_params.auth_digest_len = sa_sess_data->digest_size; + + memset (sa->integ_key + sa->integ_key_len, 0, + sa_sess_data->key_size - sa->integ_key_len); + + if (is_outbound) + crypto_params.op = ODP_CRYPTO_OP_ENCODE; + else + crypto_params.op = ODP_CRYPTO_OP_DECODE; + + crypto_params.cipher_key.data = sa->crypto_key; + crypto_params.auth_key.data = sa->integ_key; + crypto_params.iv.data = sa_sess_data->iv_data; + const int IV_LEN = 16; + sa_sess_data->iv_len = IV_LEN; + crypto_params.iv.length = IV_LEN; + + { + int size = crypto_params.iv.length; + int ret = + odp_random_data (crypto_params.iv.data, size, ODP_RANDOM_CRYPTO); + + if (ret != size) + { + clib_error_return (0, "failed to get random from ODP"); + return -1; + } + } + + if (odp_crypto_session_create + (&crypto_params, &sa_sess_data->sess, &ses_create_rc)) + { + clib_warning ("Unable to create session\n"); + clib_warning ("%d\n", ses_create_rc); + return VNET_API_ERROR_SYSCALL_ERROR_1; + } + + if (ODP_CRYPTO_SESSION_INVALID == sa_sess_data->sess) + { + clib_warning ("ODP_CRYPTO_SESSION_INVALID\n"); + return VNET_API_ERROR_SYSCALL_ERROR_1; + } + + if (ODP_CRYPTO_SES_CREATE_ERR_NONE != ses_create_rc) + { + clib_warning ("Session creation returned some errors\n"); + return VNET_API_ERROR_SYSCALL_ERROR_1; + } + + return 0; +} + +clib_error_t * +odp_ipsec_check_support (ipsec_sa_t * sa) +{ + // TODO maybe we should check what crypto is available or something? + return 0; +} + +clib_error_t * +ipsec_init (vlib_main_t * vm) +{ + if (!enable_odp_crypto) + return 0; + ipsec_main_t *im = &ipsec_main; + odp_crypto_main_t *ocm = &odp_crypto_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + vlib_node_t *ipsec_node, *crypto_node, *error_node; + + memset (im, 0, sizeof (im[0])); + + im->vnet_main = vnet_get_main (); + im->vlib_main = vm; + + im->spd_index_by_spd_id = hash_create (0, sizeof (uword)); + im->sa_index_by_sa_id = hash_create (0, sizeof (uword)); + im->spd_index_by_sw_if_index = hash_create (0, sizeof (uword)); + + vec_validate_aligned (im->empty_buffers, tm->n_vlib_mains - 1, + CLIB_CACHE_LINE_BYTES); + + error_node = vlib_get_node_by_name (vm, (u8 *) "error-drop"); + ASSERT (error_node); + im->error_drop_node_index = error_node->index; + + + ipsec_node = vlib_get_node_by_name (vm, (u8 *) "ipsec-output-ip4"); + ASSERT (ipsec_node); + crypto_node = vlib_get_node_by_name (vm, (u8 *) "odp-crypto-esp-encrypt"); + ASSERT (crypto_node); + im->esp_encrypt_node_index = crypto_node->index; + im->esp_encrypt_next_index = + vlib_node_add_next (vm, ipsec_node->index, crypto_node->index); + + ipsec_node = vlib_get_node_by_name (vm, (u8 *) "ipsec-input-ip4"); + ASSERT (ipsec_node); + crypto_node = vlib_get_node_by_name (vm, (u8 *) "odp-crypto-esp-decrypt"); + ASSERT (crypto_node); + im->esp_decrypt_node_index = crypto_node->index; + im->esp_decrypt_next_index = + vlib_node_add_next (vm, ipsec_node->index, crypto_node->index); + + im->cb.check_support_cb = odp_ipsec_check_support; + im->cb.add_del_sa_sess_cb = add_del_sa_sess; + + vec_alloc (ocm->workers, tm->n_vlib_mains); + _vec_len (ocm->workers) = tm->n_vlib_mains; + + esp_init (); + + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |