From: Adrian Lutan <[email protected]> The implementation supports insertion and removal of ESP header, trailer and ICV, and optionally use of ESN (Extended Sequence Number) feature. Note: use of ESP ciphering without authentication is not supported as it represents a security issue and is not mandatory by RFC 4303.
Signed-off-by: Adrian Lutan <[email protected]> --- .../linux-generic/include/odp_crypto_internal.h | 20 +- platform/linux-generic/odp_crypto.c | 319 ++++++++++++++++++++- 2 files changed, 334 insertions(+), 5 deletions(-) diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h index 7b104af..de9e853 100644 --- a/platform/linux-generic/include/odp_crypto_internal.h +++ b/platform/linux-generic/include/odp_crypto_internal.h @@ -25,7 +25,16 @@ typedef struct odp_crypto_generic_session odp_crypto_generic_session_t; typedef odp_crypto_alg_err_t (*crypto_func_t)(odp_crypto_op_params_t *params, odp_crypto_generic_session_t *session); - +/** + * Function prototypes for ESP protocol computation.They are called in + * "odp_crypto_operation" function when proto-esp option is active. + */ +typedef +void (*before_func)(odp_crypto_op_params_t *params, + odp_crypto_generic_session_t *session); +typedef +void (*after_func)(odp_crypto_op_params_t *params, + odp_crypto_generic_session_t *session); /** * Per crypto session data structure */ @@ -35,6 +44,14 @@ struct odp_crypto_generic_session { odp_bool_t do_cipher_first; odp_queue_t compl_queue; odp_pool_t output_pool; + odp_atomic_u64_t seq_no; + odp_ipsec_params_t ipsec_params; + enum odp_ipsec_mode ipsec_mode; + enum odp_ipsec_proto ipsec_proto; + struct { + before_func before; + after_func after; + } in_crypto_func; struct { odp_cipher_alg_t alg; struct { @@ -58,6 +75,7 @@ struct odp_crypto_generic_session { } cipher; struct { odp_auth_alg_t alg; + int icv_len; union { struct { uint8_t key[16]; diff --git a/platform/linux-generic/odp_crypto.c b/platform/linux-generic/odp_crypto.c index 69306b5..178a1f1 100644 --- a/platform/linux-generic/odp_crypto.c +++ b/platform/linux-generic/odp_crypto.c @@ -18,6 +18,10 @@ #include <odp/api/random.h> #include <odp_packet_internal.h> +#include <odp/helper/ipsec.h> +#include <odp/helper/eth.h> +#include <odp/helper/ip.h> + #include <string.h> #include <openssl/des.h> @@ -27,8 +31,26 @@ #define MAX_SESSIONS 32 +#define ipv4_data_p(ip) ((uint8_t *)((odph_ipv4hdr_t *)ip + 1)) +#define ipv4_data_len(ip) (odp_be_to_cpu_16(ip->tot_len) -\ + sizeof(odph_ipv4hdr_t)) +#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b) + typedef struct odp_crypto_global_s odp_crypto_global_t; +/** + * Dummy function that is called when proto-ipsec option is not selected. + */ +static void void_func(odp_crypto_op_params_t *params ODP_UNUSED, + odp_crypto_generic_session_t *session ODP_UNUSED) +{ +} + +static inline void ipv4_adjust_len(odph_ipv4hdr_t *ip, int adj) +{ + ip->tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(ip->tot_len) + adj); +} + struct odp_crypto_global_s { odp_spinlock_t lock; odp_crypto_generic_session_t *free; @@ -61,6 +83,8 @@ static void free_session(odp_crypto_generic_session_t *session) { odp_spinlock_lock(&global->lock); + if (session->ipsec_params.out_hdr) + free(session->ipsec_params.out_hdr); session->next = global->free; global->free = session; odp_spinlock_unlock(&global->lock); @@ -629,6 +653,13 @@ odp_crypto_session_create(odp_crypto_session_params_t *params, session->cipher.iv.len = params->iv.length; session->auth.alg = params->auth_alg; session->output_pool = params->output_pool; + /* When a session is created, is considered that proto-esp option + * is off. If it is on, in "odp_crypto_session_config_ipsec" function + * will be configured ipsec_proto value, before and after functions. + */ + session->ipsec_proto = -1; + session->in_crypto_func.before = &void_func; + session->in_crypto_func.after = &void_func; /* Process based on cipher */ switch (params->cipher_alg) { @@ -709,6 +740,235 @@ int odp_crypto_session_destroy(odp_crypto_session_t session) return 0; } +static inline int locate_ipsec_headers(odph_ipv4hdr_t *ip, + odph_esphdr_t **esp_p) +{ + uint8_t *in = ipv4_data_p(ip); + + if (ODPH_IPPROTO_ESP == ip->proto) { + *esp_p = (odph_esphdr_t *)in; + in += sizeof(odph_esphdr_t); + } else { + *esp_p = NULL; + } + return in - (ipv4_data_p(ip)); +} + +/** + * Function that prepares the headers for ESP encryption and authentication + * before doing the crypto operation + */ +static void encode_before_crypto(odp_crypto_op_params_t *params, + odp_crypto_generic_session_t *session) +{ + int hdr_len = 0; + int trl_len = 0; + odph_esphdr_t *esp; + odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr + (params->pkt, NULL); + uint16_t ip_data_len = ipv4_data_len(ip); + uint8_t *ip_data = ipv4_data_p(ip); + + if (ODP_IPSEC_MODE_TUNNEL == session->ipsec_mode) { + hdr_len += sizeof(odph_ipv4hdr_t); + ip_data = (uint8_t *)ip; + ip_data_len += sizeof(odph_ipv4hdr_t); + } + esp = (odph_esphdr_t *)(ip_data + hdr_len); + hdr_len += sizeof(odph_esphdr_t) + session->cipher.iv.len; + + if (!odp_packet_push_tail(params->pkt, hdr_len)) + abort(); + memmove(ip_data + hdr_len, ip_data, ip_data_len); + + ip_data += hdr_len; + + if (esp) { + uint32_t encrypt_len; + odph_esptrl_t *esp_t; + uint8_t *icv; + uint8_t *buf = odp_packet_data(params->pkt); + + encrypt_len = ESP_ENCODE_LEN(ip_data_len + + sizeof(*esp_t), 8); + trl_len = encrypt_len - ip_data_len; + + if (!odp_packet_push_tail(params->pkt, trl_len + + session->auth.icv_len)) + abort(); + + esp->spi = odp_cpu_to_be_32(session->ipsec_params.spi); + esp->seq_no = odp_cpu_to_be_32((uint32_t) + odp_atomic_fetch_inc_u64 + (&session->seq_no)); + + memcpy(esp + 1, session->cipher.iv.data, + session->cipher.iv.len); + + esp_t = (odph_esptrl_t *)(ip_data + encrypt_len) - 1; + esp_t->pad_len = trl_len - sizeof(*esp_t); + + icv = (uint8_t *)(esp_t + 1); + + if (ODP_IPSEC_MODE_TUNNEL == session->ipsec_mode) + esp_t->next_header = ODPH_IPV4; + else + esp_t->next_header = ip->proto; + ip->proto = ODPH_IPPROTO_ESP; + + params->cipher_range.offset = ip_data - buf; + params->cipher_range.length = encrypt_len; + + params->auth_range.offset = ((uint8_t *)(ip + 1)) - buf; + params->auth_range.length = (uint8_t *) + ((odph_esptrl_t *)esp_t + 1) - + (uint8_t *)esp; + params->hash_result_offset = icv - buf; + /* In case of ESN */ + if (session->ipsec_params.esn) { + uint8_t *tail; + /* Take the high part of 64 bits sequence number. */ + uint32_t esn = odp_cpu_to_be_32((uint32_t) + (odp_atomic_load_u64 + (&session->seq_no) >> + 32)); + odp_packet_push_tail(params->pkt, sizeof(uint32_t)); + /* Authenticated data includes the ESN (4 bytes) */ + tail = (uint8_t *)(esp_t + 1); + *((uint32_t *)(intptr_t)tail) = esn; + params->auth_range.length += sizeof(uint32_t); + } + } + /* Set IPv4 length before authentication */ + ipv4_adjust_len(ip, hdr_len + trl_len + session->auth.icv_len); + /* Header processing */ + if (ODP_IPSEC_MODE_TUNNEL == session->ipsec_mode) { + odph_ipv4hdr_t *out_hdr_ptr = (odph_ipv4hdr_t *) + session->ipsec_params.out_hdr; + + ip->proto = out_hdr_ptr->proto; + ip->src_addr = out_hdr_ptr->src_addr; + ip->dst_addr = out_hdr_ptr->dst_addr; + ip->ttl = out_hdr_ptr->ttl; + ip->id = out_hdr_ptr->id++; + if (!ip->id) { + /* re-init tunnel hdr id */ + if (odp_random_data((uint8_t *)&out_hdr_ptr->id, + sizeof(uint16_t), 1) != + sizeof(out_hdr_ptr->id)) + abort(); + } + } + ip->chksum = 0; + odph_ipv4_csum_update(params->out_pkt); +} + +/** + * Function that prepares the headers for ESP encryption and authentication + * after doing the crypto operation + */ +static void encode_after_crypto(odp_crypto_op_params_t *params, + odp_crypto_generic_session_t *session + ODP_UNUSED) +{ + odp_packet_pull_tail(params->out_pkt, sizeof(uint32_t)); +} + +/** + * Function that prepares the headers for ESP decryption and authentication + * before doing the crypto operation + */ +static void decode_before_crypto(odp_crypto_op_params_t *params, + odp_crypto_generic_session_t *session) +{ + int hdr_len = 0; + odph_esphdr_t *esp; + odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr + (params->pkt, NULL); + hdr_len = locate_ipsec_headers(ip, &esp); + hdr_len += session->cipher.iv.len; + + if (esp) { + uint16_t ip_data_len = ipv4_data_len(ip); + uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len); + uint8_t *buf = odp_packet_data(params->pkt); + + params->auth_range.offset = ((uint8_t *)(ip + 1)) - buf; + params->auth_range.length = ip_data_len - session->auth.icv_len; + params->hash_result_offset = (eop - buf) - + session->auth.icv_len; + + params->cipher_range.offset = ipv4_data_p(ip) + hdr_len - buf; + params->cipher_range.length = ipv4_data_len(ip) - + hdr_len - session->auth.icv_len; + params->override_iv_ptr = esp->iv; + /* Compute ICV with ESN if present */ + if (session->ipsec_params.esn) { + uint8_t *icv = eop - session->auth.icv_len; + uint32_t esn; + + /* Increment seq number and take the high part of it */ + odp_atomic_fetch_inc_u64(&session->seq_no); + esn = odp_cpu_to_be_32((uint32_t)(odp_atomic_load_u64 + (&session->seq_no) >> + 32)); + if (!odp_packet_push_tail(params->pkt, + sizeof(uint32_t))) + abort(); + memmove(icv + sizeof(uint32_t), icv, + session->auth.icv_len); + *((uint32_t *)(intptr_t)icv) = esn; + params->auth_range.length += sizeof(uint32_t); + params->hash_result_offset += sizeof(uint32_t); + } + } +} + +/** + * Function that prepares the headers for ESP decryption and authentication + * after doing the crypto operation + */ +static void decode_after_crypto(odp_crypto_op_params_t *params, + odp_crypto_generic_session_t *session) +{ + int trl_len = 0; + int hdr_len = 0; + odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr + (params->out_pkt, NULL); + uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len) + - session->auth.icv_len; + odph_esptrl_t *esp_t = (odph_esptrl_t *)(eop) - 1; + odph_esphdr_t *esp; + + if (session->ipsec_params.esn) { + if (!odp_packet_pull_tail(params->out_pkt, sizeof(uint32_t))) + abort(); + } + hdr_len = locate_ipsec_headers(ip, &esp); + hdr_len += session->cipher.iv.len; + ip->proto = esp_t->next_header; + trl_len += esp_t->pad_len + sizeof(*esp_t) + session->auth.icv_len; + + if (ODPH_IPV4 == ip->proto) { + odph_ethhdr_t *eth; + + odp_packet_pull_head(params->out_pkt, sizeof(*ip) + hdr_len); + odp_packet_pull_tail(params->out_pkt, trl_len); + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(params->out_pkt, NULL); + eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4); + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(params->out_pkt, NULL); + } else { + ipv4_adjust_len(ip, -(hdr_len + trl_len)); + ip->chksum = 0; + odph_ipv4_csum_update(params->out_pkt); + /* Correct the packet length and move payload into position */ + memmove(ipv4_data_p(ip), ipv4_data_p(ip) + hdr_len, + odp_be_to_cpu_16(ip->tot_len)); + if (!odp_packet_pull_tail(params->out_pkt, hdr_len + trl_len)) + abort(); + } +} + int odp_crypto_operation(odp_crypto_op_params_t *params, odp_bool_t *posted, @@ -720,6 +980,8 @@ odp_crypto_operation(odp_crypto_op_params_t *params, odp_crypto_op_result_t local_result; session = (odp_crypto_generic_session_t *)(intptr_t)params->session; + /* Call the function for processing the headers before encode/decode */ + session->in_crypto_func.before(params, session); /* Resolve output buffer */ if (ODP_PACKET_INVALID == params->out_pkt && @@ -758,6 +1020,8 @@ odp_crypto_operation(odp_crypto_op_params_t *params, local_result.ok = (rc_cipher == ODP_CRYPTO_ALG_ERR_NONE) && (rc_auth == ODP_CRYPTO_ALG_ERR_NONE); + /* Call the function for processing the headers after encode/decode */ + session->in_crypto_func.after(params, session); /* If specified during creation post event to completion queue */ if (ODP_QUEUE_INVALID != session->compl_queue) { @@ -890,10 +1154,57 @@ odp_crypto_compl_free(odp_crypto_compl_t completion_event) ODP_EVENT_PACKET); } -int odp_crypto_session_config_ipsec(odp_crypto_session_t session ODP_UNUSED, - enum odp_ipsec_mode ipsec_mode ODP_UNUSED, - enum odp_ipsec_proto ipsec_proto ODP_UNUSED, - odp_ipsec_params_t *ipsec_params ODP_UNUSED) +int odp_crypto_session_config_ipsec(odp_crypto_session_t session, + enum odp_ipsec_mode ipsec_mode, + enum odp_ipsec_proto ipsec_proto, + odp_ipsec_params_t *ipsec_params) { + odp_crypto_generic_session_t *ses; + + ses = (odp_crypto_generic_session_t *)(intptr_t)session; + if (!memcpy(&ses->ipsec_params, ipsec_params, + sizeof(odp_ipsec_params_t))) + abort(); + /* Initialize ESP sequence number with 1 */ + odp_atomic_init_u64(&ses->seq_no, 1); + + ses->ipsec_mode = ipsec_mode; + ses->ipsec_proto = ipsec_proto; + + /* Find the ICV length for a specific authentication algorithm */ + if (ODP_AUTH_ALG_MD5_96 == ses->auth.alg) + ses->auth.icv_len = 12; + else if (ODP_AUTH_ALG_SHA256_128 == ses->auth.alg) + ses->auth.icv_len = 16; + /* Set functions in case of encrypt */ + if (ODP_IPSEC_ESP == ipsec_proto && ODP_CRYPTO_OP_ENCODE == ses->op) { + ses->in_crypto_func.before = &encode_before_crypto; + if (ses->ipsec_params.esn) + ses->in_crypto_func.after = &encode_after_crypto; + else + ses->in_crypto_func.after = &void_func; + } + /* Set functions in case of decrypt */ + if (ODP_IPSEC_ESP == ipsec_proto && ODP_CRYPTO_OP_DECODE == ses->op) { + ses->in_crypto_func.before = &decode_before_crypto; + ses->in_crypto_func.after = &decode_after_crypto; + } + + if (ODP_IPSEC_MODE_TUNNEL == ses->ipsec_mode) { + odph_ipv4hdr_t *out_hdr_ptr; + + ses->ipsec_params.out_hdr = malloc(sizeof(odph_ipv4hdr_t)); + if (!ses->ipsec_params.out_hdr) + abort(); + out_hdr_ptr = (odph_ipv4hdr_t *)ipsec_params->out_hdr; + /* Set Outer header non configurable fields (encap) */ + out_hdr_ptr->proto = ODPH_IPPROTO_ESP; + out_hdr_ptr->ttl = 64; /* Default TTL */ + + if (!memcpy(ses->ipsec_params.out_hdr, ipsec_params->out_hdr, + sizeof(odph_ipv4hdr_t))) + abort(); + } + return 0; } -- 2.1.0 _______________________________________________ lng-odp mailing list [email protected] https://lists.linaro.org/mailman/listinfo/lng-odp
