From: Adrian Lutan <[email protected]> This patch adds the IPSEC ESP protocol offload support in odp_ipsec application. The protocol offloading enablement and configuration are specified in the IPSec policy configuration line as follows: -p SrcSubNet:DstSubNet:in|out:proto-esp[:par1=val1[,parN=valN] where : proto-esp enable ESP offloading (cipher and authentication) esn = 0,1 Set to 1 to enable use of 64-bit Sequence Numbers (ESN). Default 0 to use 32-bit ESN. Note: The application supports this option only. The rest of the protocol offloading options (structure odp_ipsec_params) – auto_iv, ip_csum, etc are not enabled.
Signed-off-by: Grigore Ion <[email protected]> Signed-off-by: Adrian Lutan <[email protected]> --- example/ipsec/odp_ipsec.c | 318 ++++++++++++++++++++---------------- example/ipsec/odp_ipsec_cache.c | 41 ++++- example/ipsec/odp_ipsec_cache.h | 3 + example/ipsec/odp_ipsec_misc.h | 59 +++++++ example/ipsec/odp_ipsec_sp_db.c | 80 ++++++--- example/ipsec/odp_ipsec_sp_db.h | 4 +- example/ipsec/odp_ipsec_stream.c | 79 ++++++++- platform/linux-generic/odp_crypto.c | 9 + 8 files changed, 425 insertions(+), 168 deletions(-) diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c index 2e93fcd..85cd767 100644 --- a/example/ipsec/odp_ipsec.c +++ b/example/ipsec/odp_ipsec.c @@ -141,6 +141,7 @@ typedef struct { buffer start */ uint16_t ah_offset; /**< Offset of AH header from buffer start */ uint16_t esp_offset; /**< Offset of ESP header from buffer start */ + enum odp_ipsec_proto proto; /**< IPSEC protocol */ /* Input only */ uint32_t src_ip; /**< SA source IP address */ @@ -397,6 +398,7 @@ void ipsec_init_post(crypto_api_mode_e api_mode) tun, api_mode, entry->input, + &entry->params, completionq, out_pool)) { EXAMPLE_ERR("Error: IPSec cache entry failed.\n" @@ -637,13 +639,11 @@ pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt, odp_bool_t *skip, odp_crypto_op_result_t *result) { - uint8_t *buf = odp_packet_data(pkt); odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); int hdr_len; odph_ahhdr_t *ah = NULL; odph_esphdr_t *esp = NULL; ipsec_cache_entry_t *entry; - odp_crypto_op_params_t params; odp_bool_t posted = 0; /* Default to skip IPsec */ @@ -660,50 +660,60 @@ pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt, if (!entry) return PKT_CONTINUE; - /* Account for configured ESP IV length in packet */ - hdr_len += entry->esp.iv_len; - - /* Initialize parameters block */ - memset(¶ms, 0, sizeof(params)); - params.ctx = ctx; - params.session = entry->state.session; - params.pkt = pkt; - params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID; - - /*Save everything to context */ - ctx->ipsec.ip_tos = ip->tos; - ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset); - ctx->ipsec.ip_ttl = ip->ttl; - ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0; - ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; - ctx->ipsec.hdr_len = hdr_len; - ctx->ipsec.trl_len = 0; + ctx->ipsec.proto = entry->params.proto; + ctx->ipsec.params.ctx = ctx; + ctx->ipsec.params.session = entry->state.session; + ctx->ipsec.params.pkt = pkt; + ctx->ipsec.params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID; ctx->ipsec.src_ip = entry->src_ip; ctx->ipsec.dst_ip = entry->dst_ip; - /*If authenticating, zero the mutable fields build the request */ - if (ah) { - ip->chksum = 0; - ip->tos = 0; - ip->frag_offset = 0; - ip->ttl = 0; + if (ODP_IPSEC_ESP != ctx->ipsec.proto) { + uint8_t *buf = odp_packet_data(pkt); - params.auth_range.offset = ((uint8_t *)ip) - buf; - params.auth_range.length = odp_be_to_cpu_16(ip->tot_len); - params.hash_result_offset = ah->icv - buf; - } + /* Account for configured ESP IV length in packet */ + hdr_len += entry->esp.iv_len; - /* If deciphering build request */ - if (esp) { - params.cipher_range.offset = ipv4_data_p(ip) + hdr_len - buf; - params.cipher_range.length = ipv4_data_len(ip) - hdr_len; - params.override_iv_ptr = esp->iv; + /*Save everything to context */ + ctx->ipsec.ip_tos = ip->tos; + ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset); + ctx->ipsec.ip_ttl = ip->ttl; + ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0; + ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; + ctx->ipsec.hdr_len = hdr_len; + ctx->ipsec.trl_len = 0; + ctx->ipsec.src_ip = entry->src_ip; + ctx->ipsec.dst_ip = entry->dst_ip; + + /* If authenticating, zero the mutable fields build the + * request */ + if (ah) { + ip->chksum = 0; + ip->tos = 0; + ip->frag_offset = 0; + ip->ttl = 0; + + ctx->ipsec.params.auth_range.offset = + ((uint8_t *)ip) - buf; + ctx->ipsec.params.auth_range.length = + odp_be_to_cpu_16(ip->tot_len); + ctx->ipsec.params.hash_result_offset = ah->icv - buf; + } + + /* If deciphering build request */ + if (esp) { + ctx->ipsec.params.cipher_range.offset = + ipv4_data_p(ip) + hdr_len - buf; + ctx->ipsec.params.cipher_range.length = + ipv4_data_len(ip) - hdr_len; + ctx->ipsec.params.override_iv_ptr = esp->iv; + } } /* Issue crypto request */ *skip = FALSE; ctx->state = PKT_STATE_IPSEC_IN_FINISH; - if (odp_crypto_operation(¶ms, + if (odp_crypto_operation(&ctx->ipsec.params, &posted, result)) { abort(); @@ -725,7 +735,6 @@ pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt, odp_crypto_op_result_t *result) { odph_ipv4hdr_t *ip; - int hdr_len = ctx->ipsec.hdr_len; int trl_len = 0; /* Check crypto result */ @@ -737,62 +746,67 @@ pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt, } ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); - /* - * Finish auth - */ - if (ctx->ipsec.ah_offset) { - uint8_t *buf = odp_packet_data(pkt); - odph_ahhdr_t *ah; - - ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf); - ip->proto = ah->next_header; - } + if (ODP_IPSEC_ESP != ctx->ipsec.proto) { + int hdr_len = ctx->ipsec.hdr_len; - /* - * Finish cipher by finding ESP trailer and processing - * - * NOTE: ESP authentication ICV not supported - */ - if (ctx->ipsec.esp_offset) { - uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len); - odph_esptrl_t *esp_t = (odph_esptrl_t *)(eop) - 1; + /* + * Finish auth + */ + if (ctx->ipsec.ah_offset) { + uint8_t *buf = odp_packet_data(pkt); + odph_ahhdr_t *ah; - ip->proto = esp_t->next_header; - trl_len += esp_t->pad_len + sizeof(*esp_t); - } + ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf); + ip->proto = ah->next_header; + } - /* We have a tunneled IPv4 packet */ - if (ip->proto == ODPH_IPV4) { - odp_packet_pull_head(pkt, sizeof(*ip) + hdr_len); - odp_packet_pull_tail(pkt, trl_len); - odph_ethhdr_t *eth; + /* + * Finish cipher by finding ESP trailer and processing + * + * NOTE: ESP authentication ICV not supported + */ + if (ctx->ipsec.esp_offset) { + uint8_t *eop = (uint8_t *)(ip) + + odp_be_to_cpu_16(ip->tot_len); + odph_esptrl_t *esp_t = (odph_esptrl_t *)(eop) - 1; - eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); - eth->type = ODPH_ETHTYPE_IPV4; - ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + ip->proto = esp_t->next_header; + trl_len += esp_t->pad_len + sizeof(*esp_t); + } - /* Check inbound policy */ - if ((ip->src_addr != ctx->ipsec.src_ip || - ip->dst_addr != ctx->ipsec.dst_ip)) - return PKT_DROP; + /* We have a tunneled IPv4 packet */ + if (ip->proto == ODPH_IPV4) { + odp_packet_pull_head(pkt, sizeof(*ip) + hdr_len); + odp_packet_pull_tail(pkt, trl_len); + odph_ethhdr_t *eth; - return PKT_CONTINUE; + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4); + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + } else { + /* Finalize the IPv4 header */ + ipv4_adjust_len(ip, -(hdr_len + trl_len)); + ip->ttl = ctx->ipsec.ip_ttl; + ip->tos = ctx->ipsec.ip_tos; + ip->frag_offset = + odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset); + ip->chksum = 0; + odph_ipv4_csum_update(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)); + odp_packet_pull_tail(pkt, hdr_len + trl_len); + return PKT_CONTINUE; + } } - /* Finalize the IPv4 header */ - ipv4_adjust_len(ip, -(hdr_len + trl_len)); - ip->ttl = ctx->ipsec.ip_ttl; - ip->tos = ctx->ipsec.ip_tos; - ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset); - ip->chksum = 0; - odph_ipv4_csum_update(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)); - odp_packet_pull_tail(pkt, hdr_len + trl_len); - + /* Check inbound policy */ + if (odp_be_to_cpu_32(ip->src_addr) != ctx->ipsec.src_ip || + odp_be_to_cpu_32(ip->dst_addr) != ctx->ipsec.dst_ip) + return PKT_DROP; /* Fall through to next state */ return PKT_CONTINUE; } @@ -817,16 +831,8 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, pkt_ctx_t *ctx, odp_bool_t *skip) { - uint8_t *buf = odp_packet_data(pkt); odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); - uint16_t ip_data_len = ipv4_data_len(ip); - uint8_t *ip_data = ipv4_data_p(ip); ipsec_cache_entry_t *entry; - odp_crypto_op_params_t params; - int hdr_len = 0; - int trl_len = 0; - odph_ahhdr_t *ah = NULL; - odph_esphdr_t *esp = NULL; /* Default to skip IPsec */ *skip = TRUE; @@ -838,18 +844,30 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, if (!entry) return PKT_CONTINUE; + ctx->ipsec.proto = entry->params.proto; + + ctx->ipsec.params.session = entry->state.session; + ctx->ipsec.params.ctx = ctx; + ctx->ipsec.params.pkt = pkt; + ctx->ipsec.params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID; + if (ODP_IPSEC_ESP == ctx->ipsec.proto) { + *skip = FALSE; + return PKT_CONTINUE; + } + + uint8_t *buf = odp_packet_data(pkt); + uint16_t ip_data_len = ipv4_data_len(ip); + uint8_t *ip_data = ipv4_data_p(ip); + int hdr_len = 0; + int trl_len = 0; + odph_ahhdr_t *ah = NULL; + odph_esphdr_t *esp = NULL; + /* Save IPv4 stuff */ ctx->ipsec.ip_tos = ip->tos; ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset); ctx->ipsec.ip_ttl = ip->ttl; - /* Initialize parameters block */ - memset(¶ms, 0, sizeof(params)); - params.session = entry->state.session; - params.ctx = ctx; - params.pkt = pkt; - params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID; - if (entry->mode == IPSEC_SA_MODE_TUNNEL) { hdr_len += sizeof(odph_ipv4hdr_t); ip_data = (uint8_t *)ip; @@ -874,6 +892,9 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, /* tunnel addresses */ ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip); ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip); + /* Recompute IP checksum after IP header update */ + ip->chksum = 0; + odph_ipv4_csum_update(pkt); } /* For cipher, compute encrypt length, build headers and request */ @@ -897,8 +918,8 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, esp_t->next_header = ip->proto; ip->proto = ODPH_IPPROTO_ESP; - params.cipher_range.offset = ip_data - buf; - params.cipher_range.length = encrypt_len; + ctx->ipsec.params.cipher_range.offset = ip_data - buf; + ctx->ipsec.params.cipher_range.length = encrypt_len; } /* For authentication, build header clear mutables and build request */ @@ -917,10 +938,10 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, ip->frag_offset = 0; ip->ttl = 0; - params.auth_range.offset = ((uint8_t *)ip) - buf; - params.auth_range.length = + ctx->ipsec.params.auth_range.offset = ((uint8_t *)ip) - buf; + ctx->ipsec.params.auth_range.length = odp_be_to_cpu_16(ip->tot_len) + (hdr_len + trl_len); - params.hash_result_offset = ah->icv - buf; + ctx->ipsec.params.hash_result_offset = ah->icv - buf; } /* Set IPv4 length before authentication */ @@ -938,7 +959,6 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, ctx->ipsec.ah_seq = &entry->state.ah_seq; ctx->ipsec.esp_seq = &entry->state.esp_seq; ctx->ipsec.tun_hdr_id = &entry->state.tun_hdr_id; - memcpy(&ctx->ipsec.params, ¶ms, sizeof(params)); *skip = FALSE; @@ -963,32 +983,37 @@ pkt_disposition_e do_ipsec_out_seq(odp_packet_t pkt, uint8_t *buf = odp_packet_data(pkt); odp_bool_t posted = 0; - /* We were dispatched from atomic queue, assign sequence numbers */ - if (ctx->ipsec.ah_offset) { - odph_ahhdr_t *ah; + if (ODP_IPSEC_ESP != ctx->ipsec.proto) { + /* We were dispatched from atomic queue, assign sequence + * numbers */ + if (ctx->ipsec.ah_offset) { + odph_ahhdr_t *ah; - ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf); - ah->seq_no = odp_cpu_to_be_32((*ctx->ipsec.ah_seq)++); - } - if (ctx->ipsec.esp_offset) { - odph_esphdr_t *esp; + ah = (odph_ahhdr_t *)(ctx->ipsec.ah_offset + buf); + ah->seq_no = odp_cpu_to_be_32((*ctx->ipsec.ah_seq)++); + } + if (ctx->ipsec.esp_offset) { + odph_esphdr_t *esp; - esp = (odph_esphdr_t *)(ctx->ipsec.esp_offset + buf); - esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++); - } - if (ctx->ipsec.tun_hdr_offset) { - odph_ipv4hdr_t *ip; - int ret; - - ip = (odph_ipv4hdr_t *)(ctx->ipsec.tun_hdr_offset + buf); - ip->id = odp_cpu_to_be_16((*ctx->ipsec.tun_hdr_id)++); - if (!ip->id) { - /* re-init tunnel hdr id */ - ret = odp_random_data((uint8_t *)ctx->ipsec.tun_hdr_id, - sizeof(*ctx->ipsec.tun_hdr_id), - 1); - if (ret != sizeof(*ctx->ipsec.tun_hdr_id)) - abort(); + esp = (odph_esphdr_t *)(ctx->ipsec.esp_offset + buf); + esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++); + } + if (ctx->ipsec.tun_hdr_offset) { + odph_ipv4hdr_t *ip; + + ip = (odph_ipv4hdr_t *) + (ctx->ipsec.tun_hdr_offset + buf); + ip->id = odp_cpu_to_be_16((*ctx->ipsec.tun_hdr_id)++); + if (!ip->id) { + /* re-init tunnel hdr id */ + int ret; + uint8_t *pid; + + pid = (uint8_t *)ctx->ipsec.tun_hdr_id; + ret = odp_random_data(pid, sizeof(uint16_t), 1); + if (ret != sizeof(*ctx->ipsec.tun_hdr_id)) + abort(); + } } } @@ -1014,8 +1039,6 @@ pkt_disposition_e do_ipsec_out_finish(odp_packet_t pkt, pkt_ctx_t *ctx, odp_crypto_op_result_t *result) { - odph_ipv4hdr_t *ip; - /* Check crypto result */ if (!result->ok) { if (!is_crypto_compl_status_ok(&result->cipher_status)) @@ -1023,15 +1046,17 @@ pkt_disposition_e do_ipsec_out_finish(odp_packet_t pkt, if (!is_crypto_compl_status_ok(&result->auth_status)) return PKT_DROP; } - ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); - - /* Finalize the IPv4 header */ - ip->ttl = ctx->ipsec.ip_ttl; - ip->tos = ctx->ipsec.ip_tos; - ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset); - ip->chksum = 0; - odph_ipv4_csum_update(pkt); - + if (ODP_IPSEC_ESP != ctx->ipsec.proto) { + odph_ipv4hdr_t *ip = + (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + + /* Finalize the IPv4 header */ + ip->ttl = ctx->ipsec.ip_ttl; + ip->tos = ctx->ipsec.ip_tos; + ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset); + ip->chksum = 0; + odph_ipv4_csum_update(pkt); + } /* Fall through to next state */ return PKT_CONTINUE; } @@ -1152,6 +1177,8 @@ void *pktio_thread(void *arg EXAMPLE_UNUSED) ctx->state = PKT_STATE_TRANSMIT; } else { ctx->state = PKT_STATE_IPSEC_OUT_SEQ; + if (ODP_IPSEC_ESP == ctx->ipsec.proto) + break; if (odp_queue_enq(seqnumq, ev)) rc = PKT_DROP; } @@ -1343,6 +1370,7 @@ main(int argc, char *argv[]) */ if (stream_count) { odp_bool_t done; + do { done = verify_stream_db_outputs(); sleep(1); @@ -1554,7 +1582,7 @@ static void usage(char *progname) "\n" "Routing / IPSec OPTIONS:\n" " -r, --route SubNet:Intf:NextHopMAC\n" - " -p, --policy SrcSubNet:DstSubNet:(in|out):(ah|esp|both)\n" + " -p, --policy SrcSubNet:DstSubNet:(in|out):(ah|esp|both|proto-esp)\n" " -e, --esp SrcIP:DstIP:(3des|null):SPI:Key192\n" " -a, --ah SrcIP:DstIP:(sha256|md5|null):SPI:Key(256|128)\n" "\n" @@ -1573,6 +1601,12 @@ static void usage(char *progname) "Optional OPTIONS\n" " -c, --count <number> CPU count.\n" " -h, --help Display help and exit.\n" + "Optional proto-esp parameters:\n" + " proto-esp[:par1=v1[,par2=v2] ...] set named parameter value.\n" + " Protocol specific named parameters list :\n" + " esn - Use extended sequence numbers. Allowed values : 0(default), 1\n" + " Example:\n" + " -p 192.168.111.0/24:192.168.222.0/24:out:proto-esp:esn=1\n" " environment variables: ODP_PKTIO_DISABLE_NETMAP\n" " ODP_PKTIO_DISABLE_SOCKET_MMAP\n" " ODP_PKTIO_DISABLE_SOCKET_MMSG\n" diff --git a/example/ipsec/odp_ipsec_cache.c b/example/ipsec/odp_ipsec_cache.c index 2bd44cf..69888cd 100644 --- a/example/ipsec/odp_ipsec_cache.c +++ b/example/ipsec/odp_ipsec_cache.c @@ -14,6 +14,7 @@ #include <odp/helper/ipsec.h> #include <odp_ipsec_cache.h> +#include <odp_ipsec_misc.h> /** Global pointer to ipsec_cache db */ ipsec_cache_t *ipsec_cache; @@ -41,6 +42,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, tun_db_entry_t *tun, crypto_api_mode_e api_mode, odp_bool_t in, + proto_params_t *proto_param, odp_queue_t completionq, odp_pool_t out_pool) { @@ -106,8 +108,8 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, /* Generate an IV */ if (params.iv.length) { int32_t size = params.iv.length; - int32_t ret = odp_random_data(params.iv.data, size, 1); + if (ret != size) return -1; } @@ -154,6 +156,43 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, return -1; } } + if (ODP_IPSEC_ESP == proto_param->proto) { + enum odp_ipsec_mode ipsec_mode; + odp_ipsec_params_t ipsec_params; + odph_ipv4hdr_t ip; + + entry->params.proto = ODP_IPSEC_ESP; + memset(&ipsec_params, 0, sizeof(odp_ipsec_params_t)); + /* SPI value (encap) */ + ipsec_params.spi = cipher_sa->spi; + /* Extended sequence numbers (encap/decap) */ + ipsec_params.esn = proto_param->esn; + entry->params.esn = proto_param->esn; + /* Outer header size (encap/decap) */ + ipsec_params.out_hdr_size = sizeof(odph_ipv4hdr_t); + if (tun) { + /* Tunnel mode */ + ipsec_mode = ODP_IPSEC_MODE_TUNNEL; + /* Outer header (encap) */ + ipsec_params.out_hdr = (uint8_t *)&ip; + /* Outer header type (encap) */ + ipsec_params.out_hdr_type = ODP_IPSEC_OUTHDR_IPV4; + memset(&ip, 0, sizeof(odph_ipv4hdr_t)); + /* Set Outer header configurable fields (encap) */ + ip.src_addr = odp_cpu_to_be_32(tun->tun_src_ip); + ip.dst_addr = odp_cpu_to_be_32(tun->tun_dst_ip); + ip.ttl = 64; /* Default TTL */ + ip.id = odp_cpu_to_be_16(entry->state.tun_hdr_id); + } else { + ipsec_mode = ODP_IPSEC_MODE_TRANSPORT; + } + if (odp_crypto_session_config_ipsec(session, ipsec_mode, + proto_param->proto, + &ipsec_params)) + return -1; + } else { + entry->params.proto = -1; + } entry->mode = mode; /* Initialize state */ diff --git a/example/ipsec/odp_ipsec_cache.h b/example/ipsec/odp_ipsec_cache.h index 7a4b95c..de0b339 100644 --- a/example/ipsec/odp_ipsec_cache.h +++ b/example/ipsec/odp_ipsec_cache.h @@ -59,6 +59,7 @@ typedef struct ipsec_cache_entry_s { uint8_t iv[MAX_IV_LEN]; /**< ESP IV storage */ odp_u16be_t tun_hdr_id; /**< Tunnel header IP ID */ } state; + proto_params_t params; /**< IPSEC protocol parameters */ } ipsec_cache_entry_t; /** @@ -85,6 +86,7 @@ void init_ipsec_cache(void); * @param tun Tunnel DB entry pointer * @param api_mode Crypto API mode for testing * @param in Direction (input versus output) + * @param proto_param IPSEC protocol parameters * @param completionq Completion queue * @param out_pool Output buffer pool * @@ -95,6 +97,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, tun_db_entry_t *tun, crypto_api_mode_e api_mode, odp_bool_t in, + proto_params_t *proto_param, odp_queue_t completionq, odp_pool_t out_pool); diff --git a/example/ipsec/odp_ipsec_misc.h b/example/ipsec/odp_ipsec_misc.h index 6a49dcb..2a047f9 100644 --- a/example/ipsec/odp_ipsec_misc.h +++ b/example/ipsec/odp_ipsec_misc.h @@ -74,6 +74,14 @@ typedef struct ip_addr_range_s { } ip_addr_range_t; /** + * IPSEC protocol configurable parameters + */ +typedef struct proto_params { + enum odp_ipsec_proto proto; /** IPSEC protocol */ + odp_bool_t esn; /** Use extended sequence numbers */ +} proto_params_t; + +/** * Parse text string representing a key into ODP key structure * * @param keystring Pointer to key string to convert @@ -337,6 +345,57 @@ odp_bool_t is_crypto_compl_status_ok(odp_crypto_compl_status_t *status) return TRUE; } +/** + * Get named parameter value + * + * @param param Pointer to name of the parameter + * @param input Pointer to the input string containing the named + * parameters in the form par_1=val[,par_n=val] + * + * @return decimal value of the parameter if successful else -1 + */ +static inline int get_named_parameter(const char *param, char *input) +{ + int val; + char *p, fparam[MAX_STRING + 2], *rp; + + if (strlen(param) >= MAX_STRING) + return -1; + sprintf(fparam, "%s=", param); + p = strstr(input, fparam); + if (!p) + return -1; + p += strlen(fparam); + rp = strchr(p, ','); + if (rp) + *rp = '\0'; + val = atoi(p); + if (rp) + *rp = ','; + return val; +} + +/** + * Parse text string representing IPSEC protocol named parameters + * + * @param params Pointer to the IPSEC parameters structure to populate + * @param input Pointer to the input string to parse + * + * @return 0 if successful else -1 + */ +static inline +int parse_ipsec_proto_named_params(proto_params_t *params, char *input) +{ + int val; + + val = get_named_parameter("esn", input); + if (val < 0) { + printf("ERROR: getting \"esn\" parameter value\n"); + return -1; + } + params->esn = (val) ? TRUE : FALSE; + return 0; +} #ifdef __cplusplus } diff --git a/example/ipsec/odp_ipsec_sp_db.c b/example/ipsec/odp_ipsec_sp_db.c index ed631c7..94626cf 100644 --- a/example/ipsec/odp_ipsec_sp_db.c +++ b/example/ipsec/odp_ipsec_sp_db.c @@ -41,7 +41,7 @@ void init_sp_db(void) int create_sp_db_entry(char *input) { - int pos = 0; + int pos = 0, ret = 0; char *local; char *str; char *save; @@ -62,8 +62,10 @@ int create_sp_db_entry(char *input) str = local; save = NULL; + memset(&entry->params, 0, sizeof(proto_params_t)); + entry->params.proto = -1; /* Parse tokens separated by ':' */ - while (NULL != (token = strtok_r(str, ":", &save))) { + while (!ret && NULL != (token = strtok_r(str, ":", &save))) { str = NULL; /* reset str for subsequent strtok_r calls */ /* Parse token based on its position */ @@ -92,11 +94,30 @@ int create_sp_db_entry(char *input) } else if (0 == strcmp(token, "both")) { entry->esp = TRUE; entry->ah = TRUE; + } else if (0 == strcmp(token, "proto-esp")) { + entry->esp = TRUE; + entry->ah = TRUE; + entry->params.proto = ODP_IPSEC_ESP; + } else { + printf("ERROR: \"%s\" Unsupported\n", token); + free(local); + return -1; } break; + case 4: + if (ODP_IPSEC_ESP == entry->params.proto) { + if (parse_ipsec_proto_named_params( + &entry->params, token)) { + free(local); + return -1; + } + break; + } + /* Fall on the next, in the non protocol case */ default: printf("ERROR: extra token \"%s\" at position %d\n", token, pos); + ret = -1; break; } @@ -105,21 +126,28 @@ int create_sp_db_entry(char *input) } /* Verify we parsed exactly the number of tokens we expected */ - if (4 != pos) { - printf("ERROR: \"%s\" contains %d tokens, expected 4\n", - input, - pos); - free(local); - return -1; + if (ret < 0) { + if (ODP_IPSEC_ESP != entry->params.proto && 4 != pos) + printf("ERROR: \"%s\" contains %d tokens, expected 4\n", + input, pos); + else if (ODP_IPSEC_ESP == entry->params.proto && + (4 != pos || 5 != pos)) + printf("ERROR: \"%s\" contains %d tokens, " + "expected 4 or 5\n", + input, pos); + } else { + if (ODP_IPSEC_ESP == entry->params.proto) { + /* Default values of IPSEC protocol parameters */ + if (4 == pos) + entry->params.esn = FALSE; + } + /* Add route to the list */ + sp_db->index++; + entry->next = sp_db->list; + sp_db->list = entry; } - - /* Add route to the list */ - sp_db->index++; - entry->next = sp_db->list; - sp_db->list = entry; - free(local); - return 0; + return ret; } void dump_sp_db_entry(sp_db_entry_t *entry) @@ -127,12 +155,22 @@ void dump_sp_db_entry(sp_db_entry_t *entry) char src_subnet_str[MAX_STRING]; char dst_subnet_str[MAX_STRING]; - printf(" %s %s %s %s:%s\n", - ipv4_subnet_str(src_subnet_str, &entry->src_subnet), - ipv4_subnet_str(dst_subnet_str, &entry->dst_subnet), - entry->input ? "in" : "out", - entry->esp ? "esp" : "none", - entry->ah ? "ah" : "none"); + if (ODP_IPSEC_ESP == entry->params.proto) { + printf(" %s %s %s %s\n", + ipv4_subnet_str(src_subnet_str, &entry->src_subnet), + ipv4_subnet_str(dst_subnet_str, &entry->dst_subnet), + entry->input ? "in" : "out", + "proto-esp"); + printf(" Extended sequence number : %s\n", + entry->params.esn ? "Enabled" : "Disabled"); + } else { + printf(" %s %s %s %s:%s\n", + ipv4_subnet_str(src_subnet_str, &entry->src_subnet), + ipv4_subnet_str(dst_subnet_str, &entry->dst_subnet), + entry->input ? "in" : "out", + entry->esp ? "esp" : "none", + entry->ah ? "ah" : "none"); + } } void dump_sp_db(void) diff --git a/example/ipsec/odp_ipsec_sp_db.h b/example/ipsec/odp_ipsec_sp_db.h index 735c20e..9c84996 100644 --- a/example/ipsec/odp_ipsec_sp_db.h +++ b/example/ipsec/odp_ipsec_sp_db.h @@ -23,6 +23,7 @@ typedef struct sp_db_entry_s { odp_bool_t input; /**< Direction when applied */ odp_bool_t esp; /**< Enable cipher (ESP) */ odp_bool_t ah; /**< Enable authentication (AH) */ + proto_params_t params; /**< IPSEC protocol parameters */ } sp_db_entry_t; /** @@ -43,7 +44,8 @@ void init_sp_db(void); /** * Create an SP DB entry * - * String is of the format "SrcSubNet:DstSubNet:(in|out):(ah|esp|both)" + * String is of the format : + * "SrcSubNet:DstSubNet:(in|out):(ah|esp|both|proto-esp)" * * @param input Pointer to string describing SP * diff --git a/example/ipsec/odp_ipsec_stream.c b/example/ipsec/odp_ipsec_stream.c index 4dc9acf..99c7d7c 100644 --- a/example/ipsec/odp_ipsec_stream.c +++ b/example/ipsec/odp_ipsec_stream.c @@ -174,7 +174,7 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, ipsec_cache_entry_t *entry = NULL; odp_packet_t pkt; uint8_t *base; - uint8_t *data; + uint8_t *data, *auth_data = NULL; odph_ethhdr_t *eth; odph_ipv4hdr_t *ip; odph_ipv4hdr_t *inner_ip = NULL; @@ -227,7 +227,8 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, } /* AH (if specified) */ - if (entry && (entry == stream->input.entry) && + if (entry && ODP_IPSEC_ESP != entry->params.proto && + (entry == stream->input.entry) && (ODP_AUTH_ALG_NULL != entry->ah.alg)) { if (entry->ah.alg != ODP_AUTH_ALG_MD5_96 && entry->ah.alg != ODP_AUTH_ALG_SHA256_128) @@ -249,6 +250,7 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg) abort(); + auth_data = data; esp = (odph_esphdr_t *)data; data += sizeof(*esp); data += entry->esp.iv_len; @@ -357,6 +359,42 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, memcpy(ah->icv, hash, 12); } + /* Append the ICV in the case of the IPSEC_ESP protocol */ + if (entry && ODP_IPSEC_ESP == entry->params.proto && + (entry == stream->input.entry) && + (ODP_AUTH_ALG_NULL != entry->ah.alg)) { + if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg) + abort(); + + uint8_t hash[EVP_MAX_MD_SIZE]; + int auth_len = data - auth_data; + + memset(&hash, 0, EVP_MAX_MD_SIZE); + + if (entry->params.esn) { + uint32_t esn = 0; + + /* Authenticated data includes the ESN (4 bytes) */ + if (!odp_packet_push_tail(pkt, sizeof(uint32_t))) + abort(); + *((uint32_t *)(intptr_t)data) = esn; + auth_len += sizeof(uint32_t); + } + HMAC(EVP_md5(), + entry->ah.key.data, + entry->ah.key.length, + auth_data, + auth_len, + hash, + NULL); + if (entry->params.esn) + odp_packet_pull_tail(pkt, sizeof(uint32_t)); + memcpy(data, hash, 12); + data += 12; + + ip->tot_len = odp_cpu_to_be_16(data - (uint8_t *)ip); + } + /* Correct set packet length offsets */ odp_packet_push_tail(pkt, data - base); odp_packet_l2_offset_set(pkt, (uint8_t *)eth - base); @@ -429,7 +467,8 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg) abort(); } else { - if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg)) + if (entry && ODP_IPSEC_ESP != entry->params.proto && + (ODP_AUTH_ALG_NULL != entry->ah.alg)) return FALSE; } if (esp) { @@ -492,6 +531,40 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, uint8_t iv[8]; int encrypt_len = ipv4_data_len(ip) - hdr_len; + /* Check ESP authentication if present */ + if (ODP_IPSEC_ESP == entry->params.proto) { + uint8_t hash[EVP_MAX_MD_SIZE]; + uint8_t icv[12]; + + encrypt_len -= entry->ah.icv_len; + memcpy(icv, data + encrypt_len, 12); + /* Authenticate with ESN if present */ + if (entry->params.esn) { + uint8_t *icv_p; + uint32_t esn = 0; + + if (!odp_packet_push_tail(pkt, + sizeof(uint32_t))) + abort(); + icv_p = data + encrypt_len; + *((uint32_t *)(intptr_t)icv_p) = esn; + encrypt_len += sizeof(uint32_t); + } + HMAC(EVP_md5(), + entry->ah.key.data, + entry->ah.key.length, + (uint8_t *)(ip + 1), + encrypt_len + hdr_len, + hash, + NULL); + if (0 != memcmp(icv, hash, entry->ah.icv_len)) + return FALSE; + if (entry->params.esn) { + odp_packet_pull_tail(pkt, sizeof(uint32_t)); + encrypt_len -= sizeof(uint32_t); + } + } + memcpy(iv, esp->iv, sizeof(iv)); DES_set_key((DES_cblock *)&entry->esp.key.data[0], &ks1); diff --git a/platform/linux-generic/odp_crypto.c b/platform/linux-generic/odp_crypto.c index 08b479d..69306b5 100644 --- a/platform/linux-generic/odp_crypto.c +++ b/platform/linux-generic/odp_crypto.c @@ -849,6 +849,7 @@ int32_t odp_random_data(uint8_t *buf, int32_t len, odp_bool_t use_entropy ODP_UNUSED) { int32_t rc; + rc = RAND_bytes(buf, len); return (1 == rc) ? len /*success*/: -1 /*failure*/; } @@ -888,3 +889,11 @@ odp_crypto_compl_free(odp_crypto_compl_t completion_event) odp_buffer_from_event((odp_event_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) +{ + return 0; +} -- 2.1.0 _______________________________________________ lng-odp mailing list [email protected] https://lists.linaro.org/mailman/listinfo/lng-odp
