Signed-off-by: Robbie King <[email protected]>
---
 example/ipsec/odp_ipsec_stream.c |  534 ++++++++++++++++++++++++++++++++++++++
 example/ipsec/odp_ipsec_stream.h |  133 ++++++++++
 2 files changed, 667 insertions(+), 0 deletions(-)
 create mode 100644 example/ipsec/odp_ipsec_stream.c
 create mode 100644 example/ipsec/odp_ipsec_stream.h

diff --git a/example/ipsec/odp_ipsec_stream.c b/example/ipsec/odp_ipsec_stream.c
new file mode 100644
index 0000000..02de790
--- /dev/null
+++ b/example/ipsec/odp_ipsec_stream.c
@@ -0,0 +1,534 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/des.h>
+#include <openssl/rand.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+
+#include <odp.h>
+#include <odp_align.h>
+#include <odp_crypto.h>
+#include <odp_packet.h>
+#include <helper/odp_packet_helper.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+#include <helper/odp_icmp.h>
+
+#include <odp_ipsec_stream.h>
+#include <odp_ipsec_loop_db.h>
+
+#define STREAM_MAGIC 0xBABE01234567CAFE
+
+#define LOOP_DEQ_MULTIPLE     0     /**< enable multi packet dequeue */
+
+/**
+ * Stream packet header
+ */
+typedef struct ODP_PACKED stream_pkt_hdr_s {
+       uint64be_t magic;    /**< Stream magic value for verification */
+       uint8_t    data[0];  /**< Incrementing data stream */
+} stream_pkt_hdr_t;
+
+stream_db_t *stream_db;
+
+void init_stream_db(void)
+{
+       stream_db = odp_shm_reserve("stream_db",
+                                   sizeof(stream_db_t),
+                                   ODP_CACHE_LINE_SIZE);
+       if (stream_db == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(stream_db, 0, sizeof(*stream_db));
+}
+
+int create_stream_db_entry(char *input)
+{
+       int pos;
+       char *local, *str, *save;
+       stream_db_entry_t *entry = &stream_db->array[stream_db->index];
+
+       /* Verify we have a good entry */
+       if (MAX_DB <= stream_db->index)
+               return -1;
+
+       /* Make a local copy */
+       local = malloc(strlen(input) + 1);
+       if (local == NULL)
+               return -1;
+       strcpy(local, input);
+
+       /* count the number of tokens separated by ',' */
+       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
+               char *token = strtok_r(str, ":", &save);
+
+               /* Check for no more tokens */
+               if (token == NULL)
+                       break;
+
+               /* Parse based on postion */
+               switch (pos) {
+               case 0:
+                       parse_ipv4_string(token, &entry->src_ip, NULL);
+                       break;
+               case 1:
+                       parse_ipv4_string(token, &entry->dst_ip, NULL);
+                       break;
+               case 2:
+                       entry->input.loop = loop_if_index(token);
+                       if (entry->input.loop < 0) {
+                               ODP_ERR("Error: stream must have input loop\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
+               case 3:
+                       entry->output.loop = loop_if_index(token);
+                       break;
+               case 4:
+                       entry->count = atoi(token);
+                       break;
+               case 5:
+                       entry->length = atoi(token);
+                       if (entry->length < sizeof(stream_pkt_hdr_t))
+                               entry->length = 0;
+                       else
+                               entry->length -= sizeof(stream_pkt_hdr_t);
+                       break;
+               default:
+                       return -1;
+               }
+       }
+
+       /* Verify all positions filled */
+       if (6 != pos)
+               return -1;
+
+       /* Add stream to the list */
+       entry->id = stream_db->index++;
+       entry->next = stream_db->list;
+       stream_db->list = entry;
+
+       return 0;
+}
+
+void resolve_stream_db(void)
+{
+       stream_db_entry_t *stream = NULL;
+
+       /* For each stream look for input and output IPsec entries */
+       for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+               ipsec_cache_entry_t *entry;
+
+               /* Lookup input entry */
+               entry = find_ipsec_cache_entry_in(stream->src_ip,
+                                                 stream->dst_ip,
+                                                 NULL,
+                                                 NULL);
+               stream->input.entry = entry;
+
+               /* Lookup output entry */
+               entry = find_ipsec_cache_entry_out(stream->src_ip,
+                                                  stream->dst_ip,
+                                                  0);
+               stream->output.entry = entry;
+       }
+}
+
+odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
+                               uint8_t *dmac,
+                               odp_buffer_pool_t pkt_pool)
+{
+       ipsec_cache_entry_t *entry = stream->input.entry;
+       odp_buffer_t         bfr;
+       odp_packet_t         pkt;
+       uint8_t             *base;
+       uint8_t             *data;
+       odp_ethhdr_t        *eth;
+       odp_ipv4hdr_t       *ip;
+       odp_ahhdr_t         *ah = NULL;
+       odp_esphdr_t        *esp = NULL;
+       odp_icmphdr_t       *icmp;
+       stream_pkt_hdr_t    *test;
+       uint                 i;
+
+       /* Get buffer */
+       bfr = odp_buffer_alloc(pkt_pool);
+       if (ODP_BUFFER_INVALID == bfr)
+               return ODP_PACKET_INVALID;
+       pkt = odp_packet_from_buffer(bfr);
+       odp_packet_init(pkt);
+       base = odp_packet_start(pkt);
+       data = odp_packet_start(pkt);
+
+       /* Ethernet */
+       odp_packet_set_inflag_eth(pkt, 1);
+       odp_packet_set_l2_offset(pkt, data - base);
+       eth = (odp_ethhdr_t *)data;
+       data += sizeof(*eth);
+
+       memset((char *)eth->src.addr, (0x80 | stream->id), ODP_ETHADDR_LEN);
+       memcpy((char *)eth->dst.addr, dmac, ODP_ETHADDR_LEN);
+       eth->type = odp_cpu_to_be_16(ODP_ETHTYPE_IPV4);
+
+       /* IPv4 */
+       odp_packet_set_inflag_ipv4(pkt, 1);
+       odp_packet_set_l3_offset(pkt, data - base);
+       ip = (odp_ipv4hdr_t *)data;
+       data += sizeof(*ip);
+       odp_packet_set_l4_offset(pkt, data - base);
+
+       /* Wait until almost finished to fill in mutable fields */
+       memset((char *)ip, 0, sizeof(*ip));
+       ip->ver_ihl = 0x45;
+       ip->proto = ODP_IPPROTO_ICMP;
+       ip->id = odp_cpu_to_be_16(stream->id);
+       ip->src_addr = odp_cpu_to_be_32(stream->src_ip);
+       ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip);
+
+       /* AH (if specified) */
+       if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg)) {
+               if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg)
+                       abort();
+
+               ah = (odp_ahhdr_t *)data;
+               data += sizeof(*ah);
+               data += entry->ah.icv_len;
+
+               memset((char *)ah, 0, sizeof(*ah) + entry->ah.icv_len);
+               ah->ah_len = 1 + (entry->ah.icv_len / 4);
+               ah->spi = odp_cpu_to_be_32(entry->ah.spi);
+               ah->seq_no = odp_cpu_to_be_32(stream->input.ah_seq++);
+       }
+
+       /* ESP (if specified) */
+       if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg)) {
+               if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
+                       abort();
+
+               esp = (odp_esphdr_t *)data;
+               data += sizeof(*esp);
+               data += entry->esp.iv_len;
+
+               esp->spi = odp_cpu_to_be_32(entry->esp.spi);
+               esp->seq_no = odp_cpu_to_be_32(stream->input.esp_seq++);
+               RAND_bytes(esp->iv, 8);
+       }
+
+       /* ICMP header so we can see it on wireshark */
+       icmp = (odp_icmphdr_t *)data;
+       data += sizeof(*icmp);
+       icmp->type = ICMP_ECHO;
+       icmp->code = 0;
+       icmp->un.echo.id = odp_cpu_to_be_16(0x1234);
+       icmp->un.echo.sequence = odp_cpu_to_be_16(stream->created);
+
+       /* Packet payload of incrementing bytes */
+       test = (stream_pkt_hdr_t *)data;
+       data += sizeof(*test);
+       test->magic = odp_cpu_to_be_64(STREAM_MAGIC);
+       for (i = 0; i < stream->length; i++)
+               *data++ = (uint8_t)i;
+
+       /* Close ICMP */
+       icmp->chksum = 0;
+       icmp->chksum = odp_chksum(icmp, data - (uint8_t *)icmp);
+
+       /* Close ESP if specified */
+       if (esp) {
+               int payload_len = data - (uint8_t *)icmp;
+               int encrypt_len;
+               odp_esptrl_t *esp_t;
+               DES_key_schedule ks1, ks2, ks3;
+               uint8_t iv[8];
+
+               memcpy(iv, esp->iv, sizeof(iv));
+
+               encrypt_len = ESP_ENCODE_LEN(payload_len + sizeof(*esp_t),
+                                            entry->esp.block_len);
+               memset(data, 0, encrypt_len - payload_len);
+               data += encrypt_len - payload_len;
+
+               esp_t = (odp_esptrl_t *)(data) - 1;
+               esp_t->pad_len = encrypt_len - payload_len - sizeof(*esp_t);
+               esp_t->next_header = ip->proto;
+               ip->proto = ODP_IPPROTO_ESP;
+
+               DES_set_key((DES_cblock *)&entry->esp.key.data[0], &ks1);
+               DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2);
+               DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3);
+
+               DES_ede3_cbc_encrypt((uint8_t *)icmp,
+                                    (uint8_t *)icmp,
+                                    encrypt_len,
+                                    &ks1,
+                                    &ks2,
+                                    &ks3,
+                                    (DES_cblock *)iv,
+                                    1);
+       }
+
+       /* Since ESP can pad we can now fix IP length */
+       ip->tot_len = odp_cpu_to_be_16(data - (uint8_t *)ip);
+       odp_packet_set_len(pkt, data - base);
+
+       /* Close AH if specified */
+       if (ah) {
+               uint8_t hash[EVP_MAX_MD_SIZE];
+               uint32_t hash_len = 12;
+               int auth_len = data - (uint8_t *)ip;
+
+               ah->next_header = ip->proto;
+               ip->proto = ODP_IPPROTO_AH;
+
+               HMAC(EVP_md5(),
+                    entry->ah.key.data,
+                    16,
+                    (uint8_t *)ip,
+                    auth_len,
+                    hash,
+                    &hash_len);
+
+               memcpy(ah->icv, hash, 12);
+       }
+
+       /* Now fill in final IP header fields */
+       ip->ttl = 64;
+       ip->tos = 0;
+       ip->frag_offset = 0;
+       ip->chksum = 0;
+       odp_ipv4_csum_update(pkt);
+       return pkt;
+}
+
+bool verify_ipv4_packet(stream_db_entry_t *stream,
+                       odp_packet_t pkt)
+{
+       ipsec_cache_entry_t *entry = stream->output.entry;
+       uint8_t             *data;
+       odp_ipv4hdr_t       *ip;
+       odp_ahhdr_t         *ah = NULL;
+       odp_esphdr_t        *esp = NULL;
+       int                  hdr_len;
+       odp_icmphdr_t       *icmp;
+       stream_pkt_hdr_t    *test;
+
+       /* Basic IPv4 verify (add checksum verification) */
+       data = odp_packet_l3(pkt);
+       ip = (odp_ipv4hdr_t *)data;
+       data += sizeof(*ip);
+       if (0x45 != ip->ver_ihl)
+               return FALSE;
+       if (stream->src_ip != odp_be_to_cpu_32(ip->src_addr))
+               return FALSE;
+       if (stream->dst_ip != odp_be_to_cpu_32(ip->dst_addr))
+               return FALSE;
+
+       /* Find IPsec headers if any and compare against entry */
+       hdr_len = locate_ipsec_headers(ip, &ah, &esp);
+       if (ah) {
+               if (!entry)
+                       return FALSE;
+               if (ODP_AUTH_ALG_NULL == entry->ah.alg)
+                       return FALSE;
+               if (odp_be_to_cpu_32(ah->spi) != entry->ah.spi)
+                       return FALSE;
+               if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg)
+                       abort();
+       } else {
+               if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg))
+                       return FALSE;
+       }
+       if (esp) {
+               if (!entry)
+                       return FALSE;
+               if (ODP_CIPHER_ALG_NULL == entry->esp.alg)
+                       return FALSE;
+               if (odp_be_to_cpu_32(esp->spi) != entry->esp.spi)
+                       return FALSE;
+               if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
+                       abort();
+               hdr_len += entry->esp.iv_len;
+       } else {
+               if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg))
+                       return FALSE;
+       }
+       data += hdr_len;
+
+       /* Verify authentication (if present) */
+       if (ah) {
+               uint8_t  ip_tos;
+               uint8_t  ip_ttl;
+               uint16_t ip_frag_offset;
+               uint8_t  icv[12];
+               uint8_t  hash[EVP_MAX_MD_SIZE];
+               uint32_t hash_len = 12;
+
+               /* Save/clear mutable fields */
+               ip_tos = ip->tos;
+               ip_ttl = ip->ttl;
+               ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+               ip->tos = 0;
+               ip->ttl = 0;
+               ip->frag_offset = 0;
+               ip->chksum = 0;
+               memcpy(icv, ah->icv, 12);
+               memset(ah->icv, 0, 12);
+
+               /* Calculate HMAC and compare */
+               HMAC(EVP_md5(),
+                    entry->ah.key.data,
+                    entry->ah.key.length,
+                    (uint8_t *)ip,
+                    odp_be_to_cpu_16(ip->tot_len),
+                    hash,
+                    &hash_len);
+
+               if (0 != memcmp(icv, hash, sizeof(icv)))
+                       return FALSE;
+
+               ip->proto = ah->next_header;
+               ip->tos = ip_tos;
+               ip->ttl = ip_ttl;
+               ip->frag_offset = odp_cpu_to_be_16(ip_frag_offset);
+       }
+
+       /* Decipher if present */
+       if (esp) {
+               odp_esptrl_t *esp_t;
+               DES_key_schedule ks1, ks2, ks3;
+               uint8_t iv[8];
+               int encrypt_len = ipv4_data_len(ip) - hdr_len;
+
+               memcpy(iv, esp->iv, sizeof(iv));
+
+               DES_set_key((DES_cblock *)&entry->esp.key.data[0], &ks1);
+               DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2);
+               DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3);
+
+               DES_ede3_cbc_encrypt((uint8_t *)data,
+                                    (uint8_t *)data,
+                                    encrypt_len,
+                                    &ks1,
+                                    &ks2,
+                                    &ks3,
+                                    (DES_cblock *)iv,
+                                    0);
+
+               esp_t = (odp_esptrl_t *)(data + encrypt_len) - 1;
+               ip->proto = esp_t->next_header;
+       }
+
+       /* Verify ICMP packet */
+       if (ODP_IPPROTO_ICMP != ip->proto)
+               return FALSE;
+
+       /* Verify ICMP header */
+       icmp = (odp_icmphdr_t *)data;
+       data += sizeof(*icmp);
+       if (ICMP_ECHO != icmp->type)
+               return FALSE;
+       if (0x1234 != odp_be_to_cpu_16(icmp->un.echo.id))
+               return FALSE;
+
+       /* Now check our packet */
+       test = (stream_pkt_hdr_t *)data;
+       if (STREAM_MAGIC != odp_be_to_cpu_64(test->magic))
+               return FALSE;
+
+       return TRUE;
+}
+
+int create_stream_db_inputs(void)
+{
+       int created = 0;
+       odp_buffer_pool_t pkt_pool;
+       stream_db_entry_t *stream = NULL;
+
+       /* Lookup the packet pool */
+       pkt_pool = odp_buffer_pool_lookup("packet_pool");
+       if (pkt_pool == ODP_BUFFER_POOL_INVALID) {
+               ODP_ERR("Error: pkt_pool not found\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* For each stream create corresponding input packets */
+       for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+               int count;
+               uint8_t *dmac = query_loopback_db_mac(stream->input.loop);
+               odp_queue_t queue = query_loopback_db_inq(stream->input.loop);
+
+               for (count = stream->count; count > 0; count--) {
+                       odp_packet_t pkt;
+
+                       pkt = create_ipv4_packet(stream, dmac, pkt_pool);
+                       if (ODP_PACKET_INVALID == pkt) {
+                               printf("Packet buffers exhausted\n");
+                               break;
+                       }
+                       stream->created++;
+                       odp_queue_enq(queue, pkt);
+
+                       /* Count this stream when we create first packet */
+                       if (1 == stream->created)
+                               created++;
+               }
+       }
+
+       return created;
+}
+
+bool verify_stream_db_outputs(void)
+{
+       bool done = TRUE;
+       stream_db_entry_t *stream = NULL;
+
+       /* For each stream look for output packets */
+       for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+               int idx;
+               int count;
+               odp_queue_t queue;
+               odp_buffer_t buf_tbl[32];
+
+               queue = query_loopback_db_outq(stream->output.loop);
+
+               if (ODP_QUEUE_INVALID == queue)
+                       continue;
+
+               for (;;) {
+#if LOOP_DEQ_MULTIPLE
+                       count = odp_queue_deq_multi(queue, buf_tbl, 32);
+#else
+                       buf_tbl[0] = odp_queue_deq(queue);
+                       count = (buf_tbl[0] != ODP_BUFFER_INVALID) ? 1 : 0;
+#endif
+                       if (!count)
+                               break;
+                       for (idx = 0; idx < count; idx++) {
+                               bool good;
+                               odp_packet_t pkt;
+
+                               pkt = odp_packet_from_buffer(buf_tbl[idx]);
+
+                               good = verify_ipv4_packet(stream, pkt);
+                               if (good)
+                                       stream->verified++;
+                               odp_packet_free(pkt);
+                       }
+               }
+
+               printf("Stream %d %d\n", stream->created, stream->verified);
+
+               if (stream->created != stream->verified)
+                       done = FALSE;
+       }
+       return done;
+}
+
diff --git a/example/ipsec/odp_ipsec_stream.h b/example/ipsec/odp_ipsec_stream.h
new file mode 100644
index 0000000..d3a0dd4
--- /dev/null
+++ b/example/ipsec/odp_ipsec_stream.h
@@ -0,0 +1,133 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef ODP_IPSEC_STREAM_H_
+#define ODP_IPSEC_STREAM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp.h>
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_cache.h>
+
+/**
+ * Stream database entry structure
+ */
+typedef struct stream_db_entry_s {
+       struct stream_db_entry_s *next; /**< Next entry on list */
+       int              id;            /**< Stream ID */
+       uint32_t         src_ip;        /**< Source IPv4 address */
+       uint32_t         dst_ip;        /**< Destination IPv4 address */
+       int              count;         /**< Packet count */
+       uint             length;        /**< Packet payload length */
+       uint32_t         created;       /**< Number successfully created */
+       uint32_t         verified;      /**< Number successfully verified */
+       struct {
+               int      loop;          /**< Input loop interface index */
+               uint32_t ah_seq;        /**< AH sequence number if present */
+               uint32_t esp_seq;       /**< ESP sequence number if present */
+               ipsec_cache_entry_t *entry;  /**< IPsec to apply on input */
+       } input;
+       struct {
+               int      loop;          /**< Output loop interface index */
+               ipsec_cache_entry_t *entry;  /**t IPsec to verify on output */
+       } output;
+} stream_db_entry_t;
+
+/**
+ * Stream database
+ */
+typedef struct stream_db_s {
+       uint32_t           index;          /**< Index of next available entry */
+       stream_db_entry_t *list;           /**< List of active entries */
+       stream_db_entry_t  array[MAX_DB];  /**< Entry storage */
+} stream_db_t;
+
+extern stream_db_t *stream_db;
+
+/** Initialize stream database global control structure */
+void init_stream_db(void);
+
+/**
+ * Create an stream DB entry
+ *
+ * String is of the format "SrcIP:DstIP:InInt:OutIntf:Count:Length"
+ *
+ * @param input  Pointer to string describing stream
+ *
+ * @return 0 if successful else -1
+ */
+int create_stream_db_entry(char *input);
+
+/**
+ * Resolve the stream DB against the IPsec input and output caches
+ *
+ * For each stream, look the source and destination IP address up in the
+ * input and output IPsec caches.  If a hit is found, store the hit in
+ * the stream DB to be used when creating packets.
+ */
+void resolve_stream_db(void);
+
+/**
+ * Create IPv4 packet for stream
+ *
+ * Create one ICMP test packet based on the stream structure.  If an input
+ * IPsec cache entry is associated with the stream, build a packet that should
+ * successfully match that entry and be correctly decoded by it.
+ *
+ * @param stream    Stream DB entry
+ * @param dmac      Destination MAC address to use
+ * @param pkt_pool  Packet buffer pool to allocate from
+ *
+ * @return packet else ODP_PACKET_INVALID
+ */
+odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
+                               uint8_t *dmac,
+                               odp_buffer_pool_t pkt_pool);
+
+/**
+ * Verify an IPv4 packet received on a loop output queue
+ *
+ * TODO: Better error checking, add counters, add tracing,
+ *       add order verification
+ *
+ * @param stream  Stream to verify the packet against
+ * @param pkt     Packet to verify
+ *
+ * @return TRUE if packet verifies else FALSE
+ */
+bool verify_ipv4_packet(stream_db_entry_t *stream,
+                       odp_packet_t pkt);
+
+/**
+ * Create input packets based on the stream DB
+ *
+ * Create input packets based on the configured streams and enqueue them
+ * into loop interface input queues.  Once packet processing starts these
+ * packets will be remomved and processed as if they had come from a normal
+ * packet interface.
+ *
+ * @return number of streams successfully processed
+ */
+int create_stream_db_inputs(void);
+
+/**
+ * Verify stream DB outputs
+ *
+ * For each stream, poll the output loop interface queue and verify
+ * any packets found on it
+ *
+ * @return TRUE if all packets on all streams verified else FALSE
+ */
+bool verify_stream_db_outputs(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
1.7.7.6


_______________________________________________
lng-odp mailing list
[email protected]
http://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to