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(&params, 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(&params,
+       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(&params, 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, &params, 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

Reply via email to