new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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
Signed-off-by: Robbie King <robking@cisco.com> --- 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