DPDK-dev Archive on lore.kernel.org
 help / color / Atom feed
* [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD
@ 2019-08-13 15:13 Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Vladimir Medvedkin @ 2019-08-13 15:13 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger

According to RFC 4301 IPSec implementation needs an inbound SA database (SAD).
For each incoming inbound IPSec-protected packet (ESP or AH) it has to
perform a lookup within it’s SAD.
Lookup should be performed by:
Security Parameters Index (SPI) + destination IP (DIP) + source IP (SIP)
  or SPI + DIP
  or SPI only
and an implementation has to return the “longest” existing match.
These series extend DPDK IPsec library with SAD table implementation that:
- conforms to the RFC requirements above
- can scale up to millions of entries
- supports fast lookups
- supports incremental updates

Initial series provide an API to create/destroy SAD, and to
add/delete/lookup entries within given SAD table.
Under the hood it uses three librte_hash tables each of which contains
an entries for a specific SA type (either it is addressed by SPI only
or SPI+DIP or SPI+DIP+SIP) Also this patch series introduce test-sad
application to measure performance of the library. According to our
measurements on SKX for 1M entries average lookup cost is ~80 cycles,
average add cost ~500 cycles.

Next Steps:
- integration with ipsec-secgw

Vladimir Medvedkin (5):
  ipsec: add inbound SAD API
  ipsec: add SAD create/free API
  ipsec: add SAD add/delete/lookup implementation
  test/ipsec: add ipsec SAD autotests
  app: add test-sad application

 app/Makefile                           |   1 +
 app/meson.build                        |   3 +-
 app/test-sad/Makefile                  |  18 +
 app/test-sad/main.c                    | 420 ++++++++++++++++
 app/test-sad/meson.build               |   6 +
 app/test/Makefile                      |   1 +
 app/test/autotest_data.py              |   6 +
 app/test/meson.build                   |   1 +
 app/test/test_ipsec_sad.c              | 874 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/Makefile              |   4 +-
 lib/librte_ipsec/ipsec_sad.c           | 478 ++++++++++++++++++
 lib/librte_ipsec/meson.build           |   6 +-
 lib/librte_ipsec/rte_ipsec_sad.h       | 174 +++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 +
 14 files changed, 1994 insertions(+), 5 deletions(-)
 create mode 100644 app/test-sad/Makefile
 create mode 100644 app/test-sad/main.c
 create mode 100644 app/test-sad/meson.build
 create mode 100644 app/test/test_ipsec_sad.c
 create mode 100644 lib/librte_ipsec/ipsec_sad.c
 create mode 100644 lib/librte_ipsec/rte_ipsec_sad.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [dpdk-dev] [RFC 1/5] ipsec: add inbound SAD API
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
@ 2019-08-13 15:13 ` Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 2/5] ipsec: add SAD create/free API Vladimir Medvedkin
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Vladimir Medvedkin @ 2019-08-13 15:13 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger

Add inbound security association database (SAD) API
and stub implementation.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_ipsec/Makefile              |   2 +
 lib/librte_ipsec/ipsec_sad.c           |  50 ++++++++++
 lib/librte_ipsec/meson.build           |   4 +-
 lib/librte_ipsec/rte_ipsec_sad.h       | 174 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 ++
 5 files changed, 235 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_ipsec/ipsec_sad.c
 create mode 100644 lib/librte_ipsec/rte_ipsec_sad.h

diff --git a/lib/librte_ipsec/Makefile b/lib/librte_ipsec/Makefile
index 22f29d9..5aaab72 100644
--- a/lib/librte_ipsec/Makefile
+++ b/lib/librte_ipsec/Makefile
@@ -21,10 +21,12 @@ SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += esp_inb.c
 SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += esp_outb.c
 SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += sa.c
 SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += ses.c
+SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += ipsec_sad.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_IPSEC)-include += rte_ipsec.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_IPSEC)-include += rte_ipsec_group.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_IPSEC)-include += rte_ipsec_sa.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_IPSEC)-include += rte_ipsec_sad.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
new file mode 100644
index 0000000..cae46df
--- /dev/null
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <rte_errno.h>
+
+#include "rte_ipsec_sad.h"
+
+int
+rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
+		__rte_unused union rte_ipsec_sad_key *key,
+		__rte_unused int key_type, __rte_unused void *sa)
+{
+	return -ENOTSUP;
+}
+
+int
+rte_ipsec_sad_del(__rte_unused struct rte_ipsec_sad *sad,
+		__rte_unused union rte_ipsec_sad_key *key,
+		__rte_unused int key_type)
+{
+	return -ENOTSUP;
+}
+
+struct rte_ipsec_sad *
+rte_ipsec_sad_create(__rte_unused const char *name,
+		__rte_unused struct rte_ipsec_sad_conf *conf)
+{
+	return NULL;
+}
+
+struct rte_ipsec_sad *
+rte_ipsec_sad_find_existing(__rte_unused const char *name)
+{
+	return NULL;
+}
+
+void
+rte_ipsec_sad_free(__rte_unused struct rte_ipsec_sad *sad)
+{
+	return;
+}
+
+int
+rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
+		__rte_unused const union rte_ipsec_sad_key *keys[],
+		__rte_unused uint32_t n, __rte_unused void *sa[])
+{
+	return -ENOTSUP;
+}
diff --git a/lib/librte_ipsec/meson.build b/lib/librte_ipsec/meson.build
index 7ea0c7d..91b9867 100644
--- a/lib/librte_ipsec/meson.build
+++ b/lib/librte_ipsec/meson.build
@@ -3,8 +3,8 @@
 
 allow_experimental_apis = true
 
-sources = files('esp_inb.c', 'esp_outb.c', 'sa.c', 'ses.c')
+sources = files('esp_inb.c', 'esp_outb.c', 'sa.c', 'ses.c', 'ipsec_sad.c')
 
-headers = files('rte_ipsec.h', 'rte_ipsec_group.h', 'rte_ipsec_sa.h')
+headers = files('rte_ipsec.h', 'rte_ipsec_group.h', 'rte_ipsec_sa.h', 'rte_ipsec_sad.h')
 
 deps += ['mbuf', 'net', 'cryptodev', 'security']
diff --git a/lib/librte_ipsec/rte_ipsec_sad.h b/lib/librte_ipsec/rte_ipsec_sad.h
new file mode 100644
index 0000000..d7301f5
--- /dev/null
+++ b/lib/librte_ipsec/rte_ipsec_sad.h
@@ -0,0 +1,174 @@
+
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_IPSEC_SAD_H_
+#define _RTE_IPSEC_SAD_H_
+
+#include <rte_compat.h>
+
+/**
+ * @file rte_ipsec_sad.h
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * RTE IPsec security association database (SAD) support.
+ * It is not recommended to include this file directly,
+ * include <rte_ipsec.h> instead.
+ * Contains helper functions to lookup and maintain SAD
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rte_ipsec_sad;
+
+/** Type of key */
+enum {
+	RTE_IPSEC_SAD_SPI_ONLY = 0,
+	RTE_IPSEC_SAD_SPI_DIP,
+	RTE_IPSEC_SAD_SPI_DIP_SIP,
+	RTE_IPSEC_SAD_KEY_TYPE_MASK,
+};
+
+struct rte_ipsec_sadv4_key {
+	uint32_t spi;
+	uint32_t dip;
+	uint32_t sip;
+};
+
+struct rte_ipsec_sadv6_key {
+	uint32_t spi;
+	uint8_t dip[16];
+	uint8_t sip[16];
+};
+
+union rte_ipsec_sad_key {
+	struct rte_ipsec_sadv4_key	v4;
+	struct rte_ipsec_sadv6_key	v6;
+};
+
+#define RTE_IPSEC_SAD_FLAG_IPV4			0x1
+#define RTE_IPSEC_SAD_FLAG_IPV6			0x2
+/** Flag to support reader writer concurrency */
+#define RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY	0x4
+
+/** IPsec SAD configuration structure */
+struct rte_ipsec_sad_conf {
+	int		socket_id;
+	/** maximum number of SA for each type key */
+	uint32_t	max_sa[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	uint32_t	flags;
+};
+
+/**
+ * Add a rule into the SAD. Could be safely called with concurrent lookups
+ *  if RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY flag was configured on creation time.
+ *  While with this flag multi-reader - one-writer model Is MT safe,
+ *  multi-writer model is not and required extra synchronisation.
+ *
+ * @param sad
+ *   SAD object handle
+ * @param key
+ *   pointer to the key
+ * @param key_type
+ *   key type (spi only/spi+dip/spi+dip+sip)
+ * @param sa
+ *   Pointer associated with the key to save in a SAD
+ *   Must be 4 bytes aligned.
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_ipsec_sad_add(struct rte_ipsec_sad *sad, union rte_ipsec_sad_key *key,
+	int key_type, void *sa);
+
+/**
+ * Delete a rule from the SAD. Could be safely called with concurrent lookups
+ *  if RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY flag was configured on creation time.
+ *  While with this flag multi-reader - one-writer model Is MT safe,
+ *  multi-writer model is not and required extra synchronisation.
+ *
+ * @param sad
+ *   SAD object handle
+ * @param key
+ *   pointer to the key
+ * @param key_type
+ *   key type (spi only/spi+dip/spi+dip+sip)
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_ipsec_sad_del(struct rte_ipsec_sad *sad, union rte_ipsec_sad_key *key,
+	int key_type);
+/*
+ * Create SAD
+ *
+ * @param name
+ *  SAD name
+ * @param conf
+ *  Structure containing the configuration
+ * @return
+ *  Handle to SAD object on success
+ *  NULL otherwise with rte_errno set to an appropriate values.
+ */
+__rte_experimental
+struct rte_ipsec_sad *
+rte_ipsec_sad_create(const char *name, struct rte_ipsec_sad_conf *conf);
+
+/**
+ * Find an existing SAD object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the rib object as passed to rte_ipsec_sad_create()
+ * @return
+ *  Pointer to sad object or NULL if object not found with rte_errno
+ *  set appropriately. Possible rte_errno values include:
+ *   - ENOENT - required entry not available to return.
+ */
+__rte_experimental
+struct rte_ipsec_sad *
+rte_ipsec_sad_find_existing(const char *name);
+
+/**
+ * Free SAD object.
+ *
+ * @param sad
+ *   pointer to the SAD object
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_ipsec_sad_free(struct rte_ipsec_sad *sad);
+
+/**
+ * Lookup multiple keys in the SAD.
+ *
+ * @param sad
+ *   SAD object handle
+ * @param keys
+ *   Array of keys to be looked up in the SAD
+ * @param sa
+ *   Pointer assocoated with the keys.
+ *   If the lookup for the given key failed, then corresponding sa
+ *   will be NULL
+ * @param n
+ *   Number of elements in keys array to lookup.
+ *  @return
+ *   -EINVAL for incorrect arguments, otherwise 0
+ */
+__rte_experimental
+int
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+	const union rte_ipsec_sad_key *keys[],
+	uint32_t n, void *sa[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_IPSEC_SAD_H_ */
diff --git a/lib/librte_ipsec/rte_ipsec_version.map b/lib/librte_ipsec/rte_ipsec_version.map
index ee9f196..56c38ec 100644
--- a/lib/librte_ipsec/rte_ipsec_version.map
+++ b/lib/librte_ipsec/rte_ipsec_version.map
@@ -11,5 +11,12 @@ EXPERIMENTAL {
 	rte_ipsec_ses_from_crypto;
 	rte_ipsec_session_prepare;
 
+	rte_ipsec_sad_add;
+	rte_ipsec_sad_create;
+	rte_ipsec_sad_del;
+	rte_ipsec_sad_find_existing;
+	rte_ipsec_sad_free;
+	rte_ipsec_sad_lookup;
+
 	local: *;
 };
-- 
2.7.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [dpdk-dev] [RFC 2/5] ipsec: add SAD create/free API
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-08-13 15:13 ` Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Vladimir Medvedkin @ 2019-08-13 15:13 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger

Replace rte_ipsec_sad_create(), rte_ipsec_sad_free() and
rte_ipsec_sad_find_existing() API stubs with actual
implementation.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_ipsec/Makefile    |   2 +-
 lib/librte_ipsec/ipsec_sad.c | 221 +++++++++++++++++++++++++++++++++++++++++--
 lib/librte_ipsec/meson.build |   2 +-
 3 files changed, 216 insertions(+), 9 deletions(-)

diff --git a/lib/librte_ipsec/Makefile b/lib/librte_ipsec/Makefile
index 5aaab72..81fb999 100644
--- a/lib/librte_ipsec/Makefile
+++ b/lib/librte_ipsec/Makefile
@@ -10,7 +10,7 @@ CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR)
 CFLAGS += -DALLOW_EXPERIMENTAL_API
 LDLIBS += -lrte_eal -lrte_mempool -lrte_mbuf -lrte_net
-LDLIBS += -lrte_cryptodev -lrte_security
+LDLIBS += -lrte_cryptodev -lrte_security -lrte_hash
 
 EXPORT_MAP := rte_ipsec_version.map
 
diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
index cae46df..7797628 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -2,10 +2,41 @@
  * Copyright(c) 2019 Intel Corporation
  */
 
+#include <rte_eal_memconfig.h>
 #include <rte_errno.h>
+#include <rte_hash.h>
+#include <rte_jhash.h>
+#include <rte_malloc.h>
+#include <rte_random.h>
+#include <rte_rwlock.h>
+#include <rte_tailq.h>
 
 #include "rte_ipsec_sad.h"
 
+#define IPSEC_SAD_NAMESIZE	64
+#define SAD_PREFIX		"SAD_"
+/* "SAD_<name>" */
+#define SAD_FORMAT		SAD_PREFIX "%s"
+
+#define DEFAULT_HASH_FUNC	rte_jhash
+
+struct hash_cnt {
+	uint32_t cnt_2;
+	uint32_t cnt_3;
+};
+
+struct rte_ipsec_sad {
+	char name[IPSEC_SAD_NAMESIZE];
+	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	__extension__ struct hash_cnt cnt_arr[];
+};
+
+TAILQ_HEAD(rte_ipsec_sad_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_ipsec_sad_tailq = {
+	.name = "RTE_IPSEC_SAD",
+};
+EAL_REGISTER_TAILQ(rte_ipsec_sad_tailq)
+
 int
 rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
 		__rte_unused union rte_ipsec_sad_key *key,
@@ -23,22 +54,198 @@ rte_ipsec_sad_del(__rte_unused struct rte_ipsec_sad *sad,
 }
 
 struct rte_ipsec_sad *
-rte_ipsec_sad_create(__rte_unused const char *name,
-		__rte_unused struct rte_ipsec_sad_conf *conf)
+rte_ipsec_sad_create(const char *name, struct rte_ipsec_sad_conf *conf)
 {
-	return NULL;
+	char hash_name[RTE_HASH_NAMESIZE];
+	struct rte_tailq_entry *te;
+	struct rte_ipsec_sad_list *sad_list;
+	struct rte_ipsec_sad *sad, *tmp_sad = NULL;
+	struct rte_hash_parameters hash_params = {0};
+	int ret;
+	uint32_t sa_sum;
+
+	RTE_BUILD_BUG_ON(RTE_IPSEC_SAD_KEY_TYPE_MASK != 3);
+
+	if ((name == NULL) || (conf == NULL) ||
+			(conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY] == 0) ||
+			(conf->max_sa[RTE_IPSEC_SAD_SPI_DIP] == 0) ||
+			(conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] == 0) ||
+			/* check that either IPv4 or IPv6 type flags
+			 * are configured
+			 */
+			((!!(conf->flags & RTE_IPSEC_SAD_FLAG_IPV4) ^
+			!!(conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)) == 0)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	/** Init SAD*/
+	sa_sum = conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY] +
+		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP] +
+		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP];
+	sad = rte_zmalloc_socket(NULL, sizeof(*sad) +
+		(sizeof(struct hash_cnt) * sa_sum),
+		RTE_CACHE_LINE_SIZE, conf->socket_id);
+	if (sad == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+
+	ret = snprintf(sad->name, sizeof(sad->name), SAD_FORMAT, name);
+	if (ret < 0 || ret >= (int)sizeof(sad->name)) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	hash_params.hash_func = DEFAULT_HASH_FUNC;
+	hash_params.hash_func_init_val = rte_rand();
+	hash_params.socket_id = conf->socket_id;
+	hash_params.name = hash_name;
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY)
+		hash_params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
+
+	/** Init hash[RTE_IPSEC_SAD_SPI_ONLY] for SPI only */
+	ret = snprintf(hash_name, sizeof(hash_name),
+		"sad_%p_1", sad);
+	if (ret < 0 || ret >= (int)sizeof(hash_name)) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+	hash_params.key_len = sizeof(((struct rte_ipsec_sadv4_key *)0)->spi);
+	hash_params.entries = sa_sum;
+	sad->hash[RTE_IPSEC_SAD_SPI_ONLY] = rte_hash_create(&hash_params);
+	if (sad->hash[RTE_IPSEC_SAD_SPI_ONLY] == NULL) {
+		rte_ipsec_sad_free(sad);
+		return NULL;
+	}
+
+	/** Init hash_2 for SPI + DIP */
+	ret = snprintf(hash_name, sizeof(hash_name),
+		"sad_%p_2", sad);
+	if (ret < 0 || ret >= (int)sizeof(hash_name)) {
+		rte_errno = ENAMETOOLONG;
+		rte_ipsec_sad_free(sad);
+		return NULL;
+	}
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV4)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
+	hash_params.entries = conf->max_sa[RTE_IPSEC_SAD_SPI_DIP];
+	sad->hash[RTE_IPSEC_SAD_SPI_DIP] = rte_hash_create(&hash_params);
+	if (sad->hash[RTE_IPSEC_SAD_SPI_DIP] == NULL) {
+		rte_ipsec_sad_free(sad);
+		return NULL;
+	}
+
+	/** Init hash_3 for SPI + DIP + SIP */
+	ret = snprintf(hash_name, sizeof(hash_name),
+		"sad_%p_3", name);
+	if (ret < 0 || ret >= (int)sizeof(hash_name)) {
+		rte_errno = ENAMETOOLONG;
+		rte_ipsec_sad_free(sad);
+		return NULL;
+	}
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV4)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
+	hash_params.entries = conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP];
+	sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] = rte_hash_create(&hash_params);
+	if (sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] == NULL) {
+		rte_ipsec_sad_free(sad);
+		return NULL;
+	}
+
+	sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
+			rte_ipsec_sad_list);
+	rte_mcfg_tailq_write_lock();
+	/* guarantee there's no existing */
+	TAILQ_FOREACH(te, sad_list, next) {
+		tmp_sad = (struct rte_ipsec_sad *)te->data;
+		if (strncmp(name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
+			break;
+	}
+	if (te != NULL) {
+		rte_mcfg_tailq_write_unlock();
+		rte_errno = EEXIST;
+		rte_ipsec_sad_free(sad);
+		return NULL;
+	}
+
+	/* allocate tailq entry */
+	te = rte_zmalloc("IPSEC_SAD_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		rte_mcfg_tailq_write_unlock();
+		rte_errno = ENOMEM;
+		rte_ipsec_sad_free(sad);
+		return NULL;
+	}
+
+	te->data = (void *)sad;
+	TAILQ_INSERT_TAIL(sad_list, te, next);
+	rte_mcfg_tailq_write_unlock();
+	return sad;
 }
 
 struct rte_ipsec_sad *
-rte_ipsec_sad_find_existing(__rte_unused const char *name)
+rte_ipsec_sad_find_existing(const char *name)
 {
-	return NULL;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_ipsec_sad_list *sad_list;
+
+
+	sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
+		rte_ipsec_sad_list);
+
+	rte_mcfg_tailq_read_lock();
+	TAILQ_FOREACH(te, sad_list, next) {
+		sad = (struct rte_ipsec_sad *) te->data;
+		if (strncmp(name, sad->name, IPSEC_SAD_NAMESIZE) == 0)
+			break;
+	}
+	rte_mcfg_tailq_read_unlock();
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+
+	return sad;
 }
 
 void
-rte_ipsec_sad_free(__rte_unused struct rte_ipsec_sad *sad)
+rte_ipsec_sad_free(struct rte_ipsec_sad *sad)
 {
-	return;
+	struct rte_tailq_entry *te;
+	struct rte_ipsec_sad_list *sad_list;
+
+	if (sad == NULL)
+		return;
+
+	sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
+			rte_ipsec_sad_list);
+	rte_mcfg_tailq_write_lock();
+	TAILQ_FOREACH(te, sad_list, next) {
+		if (te->data == (void *)sad)
+			break;
+	}
+	if (te != NULL)
+		TAILQ_REMOVE(sad_list, te, next);
+
+	rte_mcfg_tailq_write_unlock();
+
+	rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_ONLY]);
+	rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP]);
+	rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP]);
+	rte_free(sad);
+	if (te != NULL)
+		rte_free(te);
 }
 
 int
diff --git a/lib/librte_ipsec/meson.build b/lib/librte_ipsec/meson.build
index 91b9867..7035852 100644
--- a/lib/librte_ipsec/meson.build
+++ b/lib/librte_ipsec/meson.build
@@ -7,4 +7,4 @@ sources = files('esp_inb.c', 'esp_outb.c', 'sa.c', 'ses.c', 'ipsec_sad.c')
 
 headers = files('rte_ipsec.h', 'rte_ipsec_group.h', 'rte_ipsec_sa.h', 'rte_ipsec_sad.h')
 
-deps += ['mbuf', 'net', 'cryptodev', 'security']
+deps += ['mbuf', 'net', 'cryptodev', 'security', 'hash']
-- 
2.7.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [dpdk-dev] [RFC 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 2/5] ipsec: add SAD create/free API Vladimir Medvedkin
@ 2019-08-13 15:13 ` Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 5/5] app: add test-sad application Vladimir Medvedkin
  4 siblings, 0 replies; 6+ messages in thread
From: Vladimir Medvedkin @ 2019-08-13 15:13 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger

Replace rte_ipsec_sad_add(), rte_ipsec_sad_del() and
rte_ipsec_sad_lookup() stubs with actual implementation.

It uses three librte_hash tables each of which contains
an entries for a specific SA type (either it is addressed by SPI only
or SPI+DIP or SPI+DIP+SIP)

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 lib/librte_ipsec/ipsec_sad.c | 245 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 233 insertions(+), 12 deletions(-)

diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
index 7797628..4bf2206 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -13,6 +13,13 @@
 
 #include "rte_ipsec_sad.h"
 
+/*
+ * Rules are stored in three hash tables depending on key_type.
+ * Each rule will also be stored in SPI_ONLY table.
+ * for each data entry within this table last two bits are reserved to
+ * indicate presence of entries with the same SPI in DIP and DIP+SIP tables.
+ */
+
 #define IPSEC_SAD_NAMESIZE	64
 #define SAD_PREFIX		"SAD_"
 /* "SAD_<name>" */
@@ -37,20 +44,158 @@ static struct rte_tailq_elem rte_ipsec_sad_tailq = {
 };
 EAL_REGISTER_TAILQ(rte_ipsec_sad_tailq)
 
+#define SET_BIT(ptr, bit)	(void *)((uintptr_t)(ptr) | (uintptr_t)(bit))
+#define CLEAR_BIT(ptr, bit)	(void *)((uintptr_t)(ptr) & ~(uintptr_t)(bit))
+#define GET_BIT(ptr, bit)	(void *)((uintptr_t)(ptr) & (uintptr_t)(bit))
+
+/*
+ * @internal helper function
+ * Add a rule of type SPI_DIP or SPI_DIP_SIP.
+ * Inserts a rule into an appropriate hash table,
+ * updates the value for a given SPI in SPI_ONLY hash table
+ * reflecting presence of more specific rule type in two LSBs.
+ * Updates a counter that reflects the number of rules whith the same SPI.
+ */
+static inline int
+add_specific(struct rte_ipsec_sad *sad, void *key,
+		int key_type, void *sa)
+{
+	void *tmp_val;
+	int ret, notexist;
+
+	ret = rte_hash_lookup(sad->hash[key_type], key);
+	notexist = (ret == -ENOENT);
+	ret = rte_hash_add_key_data(sad->hash[key_type], key, sa);
+	if (ret != 0)
+		return ret;
+	ret = rte_hash_lookup_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, &tmp_val);
+	if (ret < 0)
+		tmp_val = NULL;
+	tmp_val = SET_BIT(tmp_val, key_type);
+	ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, tmp_val);
+	if (ret != 0)
+		return ret;
+	ret = rte_hash_lookup(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key);
+	if (key_type == RTE_IPSEC_SAD_SPI_DIP)
+		sad->cnt_arr[ret].cnt_2 += notexist;
+	else
+		sad->cnt_arr[ret].cnt_3 += notexist;
+
+	return 0;
+}
+
 int
-rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
-		__rte_unused union rte_ipsec_sad_key *key,
-		__rte_unused int key_type, __rte_unused void *sa)
+rte_ipsec_sad_add(struct rte_ipsec_sad *sad, union rte_ipsec_sad_key *key,
+		int key_type, void *sa)
+{
+	void *tmp_val;
+	int ret;
+
+	if ((sad == NULL) || (key == NULL) || (sa == NULL) ||
+			/* sa must be 4 byte aligned */
+			(GET_BIT(sa, RTE_IPSEC_SAD_KEY_TYPE_MASK) != 0))
+		return -EINVAL;
+
+	/*
+	 * Rules are stored in three hash tables depending on key_type.
+	 * All rules will also have an entry in SPI_ONLY table, with entry
+	 * value's two LSB's also indicating presence of rule with this SPI
+	 * in other tables.
+	 */
+	switch (key_type) {
+	case(RTE_IPSEC_SAD_SPI_ONLY):
+		ret = rte_hash_lookup_data(sad->hash[key_type],
+			key, &tmp_val);
+		if (ret >= 0)
+			tmp_val = SET_BIT(sa, GET_BIT(tmp_val,
+				RTE_IPSEC_SAD_KEY_TYPE_MASK));
+		else
+			tmp_val = sa;
+		ret = rte_hash_add_key_data(sad->hash[key_type],
+			key, tmp_val);
+		return ret;
+	case(RTE_IPSEC_SAD_SPI_DIP):
+	case(RTE_IPSEC_SAD_SPI_DIP_SIP):
+		return add_specific(sad, key, key_type, sa);
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * @internal helper function
+ * Delete a rule of type SPI_DIP or SPI_DIP_SIP.
+ * Deletes an entry from an appropriate hash table and decrements
+ * an entry counter for given SPI.
+ * If entry to remove is the last one with given SPI within the table,
+ * then it will also update related entry in SPI_ONLY table.
+ * Removes an entry from SPI_ONLY hash table if there no rule left
+ * for this SPI in any table.
+ */
+static inline int
+del_specific(struct rte_ipsec_sad *sad, void *key, int key_type)
 {
-	return -ENOTSUP;
+	void *tmp_val;
+	int ret;
+	uint32_t *cnt;
+
+	ret = rte_hash_del_key(sad->hash[key_type], key);
+	if (ret < 0)
+		return ret;
+	ret = rte_hash_lookup_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, &tmp_val);
+	if (ret < 0)
+		return ret;
+	cnt = (key_type == RTE_IPSEC_SAD_SPI_DIP) ? &sad->cnt_arr[ret].cnt_2 :
+			&sad->cnt_arr[ret].cnt_3;
+	if (--(*cnt) != 0)
+		return 0;
+
+	tmp_val = CLEAR_BIT(tmp_val, key_type);
+	if (tmp_val == NULL)
+		ret = rte_hash_del_key(sad->hash[RTE_IPSEC_SAD_SPI_ONLY], key);
+	else
+		ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+			key, tmp_val);
+	if (ret < 0)
+		return ret;
+	return 0;
 }
 
 int
-rte_ipsec_sad_del(__rte_unused struct rte_ipsec_sad *sad,
-		__rte_unused union rte_ipsec_sad_key *key,
-		__rte_unused int key_type)
+rte_ipsec_sad_del(struct rte_ipsec_sad *sad, union rte_ipsec_sad_key *key,
+		int key_type)
 {
-	return -ENOTSUP;
+	void *tmp_val;
+	int ret;
+
+	if ((sad == NULL) || (key == NULL))
+		return -EINVAL;
+	switch (key_type) {
+	case(RTE_IPSEC_SAD_SPI_ONLY):
+		ret = rte_hash_lookup_data(sad->hash[key_type],
+			key, &tmp_val);
+		if (ret < 0)
+			return ret;
+		if (GET_BIT(tmp_val, RTE_IPSEC_SAD_KEY_TYPE_MASK) == 0) {
+			RTE_ASSERT((cnt_2 == 0) && (cnt_3 == 0));
+			ret = rte_hash_del_key(sad->hash[key_type],
+				key);
+		} else {
+			tmp_val = GET_BIT(tmp_val,
+				RTE_IPSEC_SAD_KEY_TYPE_MASK);
+			ret = rte_hash_add_key_data(sad->hash[key_type],
+				key, tmp_val);
+		}
+		return ret;
+	case(RTE_IPSEC_SAD_SPI_DIP):
+	case(RTE_IPSEC_SAD_SPI_DIP_SIP):
+		return del_specific(sad, key, key_type);
+	default:
+		return -EINVAL;
+	}
 }
 
 struct rte_ipsec_sad *
@@ -248,10 +393,86 @@ rte_ipsec_sad_free(struct rte_ipsec_sad *sad)
 		rte_free(te);
 }
 
+static int
+__ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], uint32_t n, void *sa[])
+{
+	const void *keys_2[RTE_HASH_LOOKUP_BULK_MAX];
+	const void *keys_3[RTE_HASH_LOOKUP_BULK_MAX];
+	void *vals_2[RTE_HASH_LOOKUP_BULK_MAX] = {NULL};
+	void *vals_3[RTE_HASH_LOOKUP_BULK_MAX] = {NULL};
+	uint32_t idx_2[RTE_HASH_LOOKUP_BULK_MAX];
+	uint32_t idx_3[RTE_HASH_LOOKUP_BULK_MAX];
+	uint64_t mask_1, mask_2, mask_3;
+	uint64_t map, map_spec;
+	uint32_t n_2 = 0;
+	uint32_t n_3 = 0;
+	uint32_t i;
+	int n_pkts = 0;
+
+	for (i = 0; i < n; i++)
+		sa[i] = NULL;
+
+	/*
+	 * Lookup keys in SPI only hash table first.
+	 */
+	rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		(const void **)keys, n, &mask_1, sa);
+	for (map = mask_1; map; map &= (map - 1)) {
+		i = rte_bsf64(map);
+		/*
+		 * if returned value indicates presence of a rule in other
+		 * tables save a key for further lookup.
+		 */
+		if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP_SIP) {
+			idx_3[n_3] = i;
+			keys_3[n_3++] = keys[i];
+		}
+		if ((uintptr_t)sa[i] & RTE_IPSEC_SAD_SPI_DIP) {
+			idx_2[n_2] = i;
+			keys_2[n_2++] = keys[i];
+		}
+		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
+	}
+
+	if (n_2 != 0) {
+		rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_DIP],
+			keys_2, n_2, &mask_2, vals_2);
+		for (map_spec = mask_2; map_spec; map_spec &= (map_spec - 1)) {
+			i = rte_bsf64(map_spec);
+			sa[idx_2[i]] = vals_2[i];
+		}
+	}
+	if (n_3 != 0) {
+		rte_hash_lookup_bulk_data(sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP],
+			keys_3, n_3, &mask_3, vals_3);
+		for (map_spec = mask_3; map_spec; map_spec &= (map_spec - 1)) {
+			i = rte_bsf64(map_spec);
+			sa[idx_3[i]] = vals_3[i];
+		}
+	}
+	for (i = 0; i < n; i++)
+		n_pkts += (sa[i] != NULL);
+
+	return n_pkts;
+}
+
 int
-rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *keys[],
-		__rte_unused uint32_t n, __rte_unused void *sa[])
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], uint32_t n, void *sa[])
 {
-	return -ENOTSUP;
+	uint32_t num, i = 0;
+	int n_pkts = 0;
+
+	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
+		return -EINVAL;
+
+	do {
+		num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
+		n_pkts += __ipsec_sad_lookup(sad,
+			&keys[i], num, &sa[i]);
+		i += num;
+	} while (i != n);
+
+	return n_pkts;
 }
-- 
2.7.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [dpdk-dev] [RFC 4/5] test/ipsec: add ipsec SAD autotests
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (2 preceding siblings ...)
  2019-08-13 15:13 ` [dpdk-dev] [RFC 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-08-13 15:13 ` Vladimir Medvedkin
  2019-08-13 15:13 ` [dpdk-dev] [RFC 5/5] app: add test-sad application Vladimir Medvedkin
  4 siblings, 0 replies; 6+ messages in thread
From: Vladimir Medvedkin @ 2019-08-13 15:13 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   1 +
 app/test/test_ipsec_sad.c | 874 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 882 insertions(+)
 create mode 100644 app/test/test_ipsec_sad.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 26ba6fe..e4da070 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -223,6 +223,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_BPF) += test_bpf.c
 SRCS-$(CONFIG_RTE_LIBRTE_RCU) += test_rcu_qsbr.c test_rcu_qsbr_perf.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec.c
+SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec_sad.c
 ifeq ($(CONFIG_RTE_LIBRTE_IPSEC),y)
 LDLIBS += -lrte_ipsec
 endif
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 7405149..a4f2882 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -518,6 +518,12 @@
         "Func":    default_autotest,
         "Report":  None,
     },
+    {
+        "Name":    "IPsec_SAD",
+        "Command": "ipsec_sad_autotest",
+        "Func":    default_autotest,
+        "Report":  None,
+    },
     #
     #Please always keep all dump tests at the end and together!
     #
diff --git a/app/test/meson.build b/app/test/meson.build
index ec40943..b13ec74 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -57,6 +57,7 @@ test_sources = files('commands.c',
 	'test_hash_readwrite_lf.c',
 	'test_interrupts.c',
 	'test_ipsec.c',
+	'test_ipsec_sad.c',
 	'test_kni.c',
 	'test_kvargs.c',
 	'test_latencystats.c',
diff --git a/app/test/test_ipsec_sad.c b/app/test/test_ipsec_sad.c
new file mode 100644
index 0000000..79d6673
--- /dev/null
+++ b/app/test/test_ipsec_sad.c
@@ -0,0 +1,874 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_ipsec_sad.h>
+#include <rte_memory.h>
+
+#include "test.h"
+#include "test_xmmt_ops.h"
+
+typedef int32_t (*rte_ipsec_sad_test)(void);
+
+static int32_t test_create_invalid(void);
+static int32_t test_multiple_create(void);
+static int32_t test_add_invalid(void);
+static int32_t test_delete_invalid(void);
+static int32_t test_lookup_invalid(void);
+static int32_t test_lookup_basic(void);
+static int32_t test_lookup_adv(void);
+static int32_t test_lookup_order(void);
+
+#define MAX_SA	100000
+#define PASS 0
+#define SPI	0xdead	/* spi to install */
+#define DIP	0xbeef	/* dip to install */
+#define SIP	0xf00d	/* sip to install */
+#define BAD	0xbad	/* some random value not installed into the table */
+
+/*
+ * Check that rte_ipsec_sad_create fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_create_invalid(void)
+{
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+	config.socket_id = SOCKET_ID_ANY;
+	config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+
+	/* name == NULL */
+	sad = rte_ipsec_sad_create(NULL, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* max_sa[SPI_ONLY] = 0 */
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = 0;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* max_sa[SPI_DIP] = 0 */
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* max_sa[SPI_DIP_SIP] = 0 */
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = 0;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+
+	/* socket_id < -1 is invalid */
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.socket_id = -2;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.socket_id = SOCKET_ID_ANY;
+
+	/* invalid flags */
+	config.flags = 0;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+	config.flags = RTE_IPSEC_SAD_FLAG_IPV4|RTE_IPSEC_SAD_FLAG_IPV6;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Create ipsec sad then delete it 10 times
+ * Use a slightly different max_sa each time
+ */
+int32_t
+test_multiple_create(void)
+{
+	int i;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+
+	config.socket_id = SOCKET_ID_ANY;
+	config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+
+	for (i = 0; i < 10; i++) {
+		config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA - i;
+		sad = rte_ipsec_sad_create(__func__, &config);
+		RTE_TEST_ASSERT_NOT_NULL(sad, "Failed to create SAD\n");
+		rte_ipsec_sad_free(sad);
+	}
+	return TEST_SUCCESS;
+}
+
+static int32_t
+__test_add_invalid(int ipv4, union rte_ipsec_sad_key *tuple)
+{
+	int status;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+	uint64_t tmp;
+	void *sa = &tmp;
+
+	/* sad == NULL*/
+	status = rte_ipsec_sad_add(NULL, tuple,
+			RTE_IPSEC_SAD_SPI_DIP_SIP, sa);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+	config.socket_id = SOCKET_ID_ANY;
+	if (ipv4)
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+	else
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV6;
+
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT_NOT_NULL(sad, "Failed to create SAD\n");
+
+	/* key == NULL*/
+	status = rte_ipsec_sad_add(sad, NULL, RTE_IPSEC_SAD_SPI_DIP_SIP, sa);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* len is incorrect*/
+	status = rte_ipsec_sad_add(sad, tuple,
+		RTE_IPSEC_SAD_SPI_DIP_SIP + 1, sa);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* sa == NULL*/
+	status = rte_ipsec_sad_add(sad, tuple,
+		RTE_IPSEC_SAD_SPI_DIP_SIP, NULL);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* sa is not aligned*/
+	status = rte_ipsec_sad_add(sad, tuple,
+	RTE_IPSEC_SAD_SPI_DIP_SIP, (void *)((uint8_t *)sa + 1));
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_free(sad);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_ipsec_sad_add fails gracefully
+ * for incorrect user input arguments
+ */
+int32_t
+test_add_invalid(void)
+{
+	int status;
+	struct rte_ipsec_sadv4_key tuple_v4 = {10, 20, 30};
+	struct rte_ipsec_sadv6_key tuple_v6 = {10, {20, }, {30, } };
+
+	status = __test_add_invalid(1, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_add_invalid(0, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+
+}
+
+static int32_t
+__test_delete_invalid(int ipv4, union rte_ipsec_sad_key *tuple)
+{
+	int status;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+
+	/* sad == NULL*/
+	status = rte_ipsec_sad_del(NULL, tuple, RTE_IPSEC_SAD_SPI_DIP_SIP);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+	config.socket_id = SOCKET_ID_ANY;
+	if (ipv4)
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+	else
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV6;
+
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT_NOT_NULL(sad, "Failed to create SAD\n");
+
+	/* key == NULL*/
+	status = rte_ipsec_sad_del(sad, NULL, RTE_IPSEC_SAD_SPI_DIP_SIP);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	/* len is incorrect */
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP_SIP + 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_free(sad);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_ipsec_sad_delete fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_delete_invalid(void)
+{
+	int status;
+	struct rte_ipsec_sadv4_key tuple_v4 = {SPI, DIP, SIP};
+	struct rte_ipsec_sadv6_key tuple_v6 = {SPI, {0xbe, 0xef, },
+			{0xf0, 0x0d, } };
+
+	status = __test_delete_invalid(1, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_delete_invalid(0, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_invalid(int ipv4, union rte_ipsec_sad_key *tuple)
+{
+	int status;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+	const union rte_ipsec_sad_key *key_arr[] = {tuple};
+	void *sa[1];
+
+	status = rte_ipsec_sad_lookup(NULL, key_arr, 1, sa);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+	config.socket_id = SOCKET_ID_ANY;
+	if (ipv4)
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+	else
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV6;
+
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT_NOT_NULL(sad, "Failed to create SAD\n");
+
+	status = rte_ipsec_sad_lookup(sad, NULL, 1, sa);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 1, NULL);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_free(sad);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check that rte_ipsec_sad_lookup fails gracefully for incorrect user input
+ * arguments
+ */
+int32_t
+test_lookup_invalid(void)
+{
+	int status;
+	struct rte_ipsec_sadv4_key tuple_v4 = {10, 20, 30};
+	struct rte_ipsec_sadv6_key tuple_v6 = {10, {20, }, {30, } };
+
+	status = __test_lookup_invalid(1,
+			(union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_invalid(0,
+			(union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_basic(int ipv4, union rte_ipsec_sad_key *tuple,
+	union rte_ipsec_sad_key *tuple_1)
+{
+	int status;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+	const union rte_ipsec_sad_key *key_arr[] = {tuple};
+
+	uint64_t tmp;
+	void *sa[1];
+
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+	config.socket_id = SOCKET_ID_ANY;
+	if (ipv4)
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+	else
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV6;
+
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT_NOT_NULL(sad, "Failed to create SAD\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 1, sa);
+	RTE_TEST_ASSERT((status == 0) && (sa[0] == NULL),
+		"Lookup returns an unexpected result\n");
+
+	sa[0] = &tmp;
+	status = rte_ipsec_sad_add(sad, tuple, RTE_IPSEC_SAD_SPI_ONLY, sa[0]);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 1, sa);
+	RTE_TEST_ASSERT((status == 1) && (sa[0] == &tmp),
+		"Lookup returns an unexpected result\n");
+
+	key_arr[0] = tuple_1;
+	status = rte_ipsec_sad_lookup(sad, key_arr, 1, sa);
+	RTE_TEST_ASSERT((status == 1) && (sa[0] == &tmp),
+		"Lookup returns an unexpected result\n");
+	key_arr[0] = tuple;
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_ONLY);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 1, sa);
+	RTE_TEST_ASSERT((status == 0) && (sa[0] == NULL),
+		"Lookup returns an unexpected result\n");
+
+	rte_ipsec_sad_free(sad);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Lookup missing key, then add it as RTE_IPSEC_SAD_SPI_ONLY, lookup it again,
+ * lookup different key with the same SPI, then delete it and repeat lookup
+ */
+int32_t
+test_lookup_basic(void)
+{
+	int status;
+	struct rte_ipsec_sadv4_key tuple_v4 = {SPI, DIP, SIP};
+	struct rte_ipsec_sadv4_key tuple_v4_1 = {SPI, BAD, BAD};
+	struct rte_ipsec_sadv6_key tuple_v6 = {SPI, {0xbe, 0xef, },
+			{0xf0, 0x0d, } };
+	struct rte_ipsec_sadv6_key tuple_v6_1 = {SPI, {0x0b, 0xad, },
+			{0x0b, 0xad, } };
+
+	status = __test_lookup_basic(1, (union rte_ipsec_sad_key *)&tuple_v4,
+			(union rte_ipsec_sad_key *)&tuple_v4_1);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_basic(0, (union rte_ipsec_sad_key *)&tuple_v6,
+			(union rte_ipsec_sad_key *)&tuple_v6_1);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_adv(int ipv4, union rte_ipsec_sad_key *tuple,
+	const union rte_ipsec_sad_key **key_arr)
+{
+	int status;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+	uint64_t tmp1, tmp2, tmp3;
+	void *install_sa;
+	void *sa[4];
+
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+	config.socket_id = SOCKET_ID_ANY;
+	if (ipv4)
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+	else
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV6;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT_NOT_NULL(sad, "Failed to create SAD\n");
+
+	/* lookup with empty table */
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 0, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	/* lookup with one RTE_IPSEC_SAD_SPI_ONLY rule */
+	install_sa = &tmp1;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_ONLY, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_ONLY);
+	RTE_TEST_ASSERT(status == 0, "Failde to delete a rule\n");
+
+	/* lookup with one RTE_IPSEC_SAD_SPI_DIP rule */
+	install_sa = &tmp2;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 2, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	/* lookup with one RTE_IPSEC_SAD_SPI_DIP_SIP rule */
+	install_sa = &tmp3;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP_SIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 1, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP_SIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	/* lookup with two RTE_IPSEC_SAD_ONLY and RTE_IPSEC_SAD_DIP rules */
+	install_sa = &tmp1;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_ONLY, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+	install_sa = &tmp2;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_ONLY);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	/* lookup with two RTE_IPSEC_SAD_ONLY and RTE_IPSEC_SAD_DIP_SIP rules */
+	install_sa = &tmp1;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_ONLY, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+	install_sa = &tmp3;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP_SIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_ONLY);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP_SIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	/* lookup with two RTE_IPSEC_SAD_DIP and RTE_IPSEC_SAD_DIP_SIP rules */
+	install_sa = &tmp2;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+	install_sa = &tmp3;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP_SIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 2, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP_SIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	/*
+	 * lookup with three RTE_IPSEC_SAD_DIP, RTE_IPSEC_SAD_DIP and
+	 * RTE_IPSEC_SAD_DIP_SIP rules
+	 */
+	install_sa = &tmp1;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_ONLY, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+	install_sa = &tmp2;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+	install_sa = &tmp3;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP_SIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 4, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[3] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_ONLY);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP_SIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	rte_ipsec_sad_free(sad);
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Lookup different keys in a table with:
+ *  - RTE_IPSEC_SAD_SPI_ONLY
+ *  - RTE_IPSEC_SAD_SPI_DIP
+ *  - RTE_IPSEC_SAD_SPI_SIP
+ *  - RTE_IPSEC_SAD_SPI_ONLY/RTE_IPSEC_SAD_SPI_DIP
+ *  - RTE_IPSEC_SAD_SPI_ONLY/RTE_IPSEC_SAD_SPI_DIP_SIP
+ *  - RTE_IPSEC_SAD_SPI_DIP/RTE_IPSEC_SAD_SPI_DIP_SIP
+ *  - RTE_IPSEC_SAD_SPI_ONLY/RTE_IPSEC_SAD_SPI_DIP/RTE_IPSEC_SAD_SPI_DIP_SIP
+ * length of rule installed.
+ */
+int32_t
+test_lookup_adv(void)
+{
+	int status;
+	/* key to install*/
+	struct rte_ipsec_sadv4_key tuple_v4 = {SPI, DIP, SIP};
+	struct rte_ipsec_sadv4_key tuple_v4_1 = {SPI, DIP, BAD};
+	struct rte_ipsec_sadv4_key tuple_v4_2 = {SPI, BAD, SIP};
+	struct rte_ipsec_sadv4_key tuple_v4_3 = {BAD, DIP, SIP};
+
+	/* key to install*/
+	struct rte_ipsec_sadv6_key tuple_v6 = {SPI, {0xbe, 0xef, },
+			{0xf0, 0x0d, } };
+	struct rte_ipsec_sadv6_key tuple_v6_1 = {SPI, {0xbe, 0xef, },
+			{0x0b, 0xad, } };
+	struct rte_ipsec_sadv6_key tuple_v6_2 = {SPI, {0x0b, 0xad, },
+			{0xf0, 0x0d, } };
+	struct rte_ipsec_sadv6_key tuple_v6_3 = {BAD, {0xbe, 0xef, },
+			{0xf0, 0x0d, } };
+
+	const union rte_ipsec_sad_key *key_arr[] = {
+				(union rte_ipsec_sad_key *)&tuple_v4,
+				(union rte_ipsec_sad_key *)&tuple_v4_1,
+				(union rte_ipsec_sad_key *)&tuple_v4_2,
+				(union rte_ipsec_sad_key *)&tuple_v4_3
+					};
+
+	status = __test_lookup_adv(1, (union rte_ipsec_sad_key *)&tuple_v4,
+			key_arr);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	key_arr[0] = (union rte_ipsec_sad_key *)&tuple_v6;
+	key_arr[1] = (union rte_ipsec_sad_key *)&tuple_v6_1;
+	key_arr[2] = (union rte_ipsec_sad_key *)&tuple_v6_2;
+	key_arr[3] = (union rte_ipsec_sad_key *)&tuple_v6_3;
+	status = __test_lookup_adv(0, (union rte_ipsec_sad_key *)&tuple_v6,
+			key_arr);
+
+	return status;
+}
+
+
+static int32_t
+__test_lookup_order(int ipv4, union rte_ipsec_sad_key *tuple,
+	union rte_ipsec_sad_key *tuple_1, union rte_ipsec_sad_key *tuple_2)
+{
+	int status;
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_ipsec_sad_conf config;
+	const union rte_ipsec_sad_key *key_arr[] = {tuple, tuple_1, tuple_2,};
+	uint64_t tmp1, tmp2, tmp3;
+	void *install_sa;
+	void *sa[3];
+
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = MAX_SA;
+	config.socket_id = SOCKET_ID_ANY;
+	if (ipv4)
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV4;
+	else
+		config.flags = RTE_IPSEC_SAD_FLAG_IPV6;
+	sad = rte_ipsec_sad_create(__func__, &config);
+	RTE_TEST_ASSERT_NOT_NULL(sad, "Failed to create SAD\n");
+
+	/* install RTE_IPSEC_SAD_SPI_ONLY */
+	install_sa = &tmp1;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_ONLY, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp1,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+
+	/* add RTE_IPSEC_SAD_SPI_DIP */
+	install_sa = &tmp2;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+
+	/* add RTE_IPSEC_SAD_SPI_DIP_SIP */
+	install_sa = &tmp3;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP_SIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+
+	/* delete RTE_IPSEC_SAD_SPI_ONLY */
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_ONLY);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 2, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	/* delete RTE_IPSEC_SAD_SPI_DIP */
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 1, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	/* delete RTE_IPSEC_SAD_SPI_DIP_SIP */
+	status = rte_ipsec_sad_del(sad, tuple, RTE_IPSEC_SAD_SPI_DIP_SIP);
+	RTE_TEST_ASSERT(status == 0, "Failed to delete a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 0, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	/* add RTE_IPSEC_SAD_SPI_DIP_SIP */
+	install_sa = &tmp3;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP_SIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 1, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == NULL,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	/* add RTE_IPSEC_SAD_SPI_DIP */
+	install_sa = &tmp2;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_DIP, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 2, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == NULL,
+		"Lookup returns an unexpected result\n");
+
+	/* add RTE_IPSEC_SAD_SPI_ONLY */
+	install_sa = &tmp1;
+	status = rte_ipsec_sad_add(sad, tuple,
+			RTE_IPSEC_SAD_SPI_ONLY, install_sa);
+	RTE_TEST_ASSERT(status == 0, "Failed to add a rule\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, 3, sa);
+	RTE_TEST_ASSERT(status == 3, "Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[0] == &tmp3,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[1] == &tmp2,
+		"Lookup returns an unexpected result\n");
+	RTE_TEST_ASSERT(sa[2] == &tmp1,
+		"Lookup returns an unexpected result\n");
+
+	rte_ipsec_sad_free(sad);
+	return TEST_SUCCESS;
+}
+
+/*
+ * Check an order of add and delete
+ */
+int32_t
+test_lookup_order(void)
+{
+	int status;
+	/* key to install*/
+	struct rte_ipsec_sadv4_key tuple_v4 = {SPI, DIP, SIP};
+	struct rte_ipsec_sadv4_key tuple_v4_1 = {SPI, DIP, BAD};
+	struct rte_ipsec_sadv4_key tuple_v4_2 = {SPI, BAD, SIP};
+	/* key to install*/
+	struct rte_ipsec_sadv6_key tuple_v6 = {SPI, {0xbe, 0xef, },
+			{0xf0, 0x0d, } };
+	struct rte_ipsec_sadv6_key tuple_v6_1 = {SPI, {0xbe, 0xef, },
+			{0x0b, 0xad, } };
+	struct rte_ipsec_sadv6_key tuple_v6_2 = {SPI, {0x0b, 0xad, },
+			{0xf0, 0x0d, } };
+
+	status = __test_lookup_order(1, (union rte_ipsec_sad_key *)&tuple_v4,
+			(union rte_ipsec_sad_key *)&tuple_v4_1,
+			(union rte_ipsec_sad_key *)&tuple_v4_2);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_order(0, (union rte_ipsec_sad_key *)&tuple_v6,
+			(union rte_ipsec_sad_key *)&tuple_v6_1,
+			(union rte_ipsec_sad_key *)&tuple_v6_2);
+	return status;
+}
+
+static struct unit_test_suite ipsec_sad_tests = {
+	.suite_name = "ipsec sad autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_create_invalid),
+		TEST_CASE(test_multiple_create),
+		TEST_CASE(test_add_invalid),
+		TEST_CASE(test_delete_invalid),
+		TEST_CASE(test_lookup_invalid),
+		TEST_CASE(test_lookup_basic),
+		TEST_CASE(test_lookup_adv),
+		TEST_CASE(test_lookup_order),
+		TEST_CASES_END()
+	}
+};
+
+static int
+test_ipsec_sad(void)
+{
+	return unit_test_suite_runner(&ipsec_sad_tests);
+}
+
+REGISTER_TEST_COMMAND(ipsec_sad_autotest, test_ipsec_sad);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [dpdk-dev] [RFC 5/5] app: add test-sad application
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (3 preceding siblings ...)
  2019-08-13 15:13 ` [dpdk-dev] [RFC 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-08-13 15:13 ` Vladimir Medvedkin
  4 siblings, 0 replies; 6+ messages in thread
From: Vladimir Medvedkin @ 2019-08-13 15:13 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger

Usage example and performance evaluation for the ipsec SAD library

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 app/Makefile             |   1 +
 app/meson.build          |   3 +-
 app/test-sad/Makefile    |  18 ++
 app/test-sad/main.c      | 420 +++++++++++++++++++++++++++++++++++++++++++++++
 app/test-sad/meson.build |   6 +
 5 files changed, 447 insertions(+), 1 deletion(-)
 create mode 100644 app/test-sad/Makefile
 create mode 100644 app/test-sad/main.c
 create mode 100644 app/test-sad/meson.build

diff --git a/app/Makefile b/app/Makefile
index 28acbce..db9d2d5 100644
--- a/app/Makefile
+++ b/app/Makefile
@@ -10,6 +10,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += pdump
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += test-acl
 DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += test-cmdline
 DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += test-pipeline
+DIRS-$(CONFIG_RTE_LIBRTE_IPSEC) += test-sad
 
 ifeq ($(CONFIG_RTE_LIBRTE_BBDEV),y)
 DIRS-$(CONFIG_RTE_TEST_BBDEV) += test-bbdev
diff --git a/app/meson.build b/app/meson.build
index b0e6afb..71109cc 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -15,7 +15,8 @@ apps = [
 	'test-crypto-perf',
 	'test-eventdev',
 	'test-pipeline',
-	'test-pmd']
+	'test-pmd',
+	'test-sad']
 
 # for BSD only
 lib_execinfo = cc.find_library('execinfo', required: false)
diff --git a/app/test-sad/Makefile b/app/test-sad/Makefile
new file mode 100644
index 0000000..9b35413
--- /dev/null
+++ b/app/test-sad/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2014 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifeq ($(CONFIG_RTE_LIBRTE_IPSEC),y)
+
+APP = testsad
+
+CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+include $(RTE_SDK)/mk/rte.app.mk
+
+endif
diff --git a/app/test-sad/main.c b/app/test-sad/main.c
new file mode 100644
index 0000000..039397f
--- /dev/null
+++ b/app/test-sad/main.c
@@ -0,0 +1,420 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <rte_string_fns.h>
+#include <rte_ipsec_sad.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_ip.h>
+#include <rte_random.h>
+#include <rte_malloc.h>
+#include <rte_ipsec_sad.h>
+
+#define	PRINT_USAGE_START	"%s [EAL options] --\n"
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {		\
+	unsigned long val;					\
+	char *end_fld;						\
+	errno = 0;						\
+	val = strtoul((in), &end_fld, (base));			\
+	if (errno != 0 || end_fld[0] != (dlm) || val > (lim))	\
+		return -EINVAL;					\
+	(fd) = (typeof(fd))val;					\
+	(in) = end_fld + 1;					\
+} while (0)
+
+#define	DEF_RULE_NUM		0x10000
+#define	DEF_TUPLES_NUM	0x100000
+
+static struct {
+	const char	*prgname;
+	const char	*rules_file;
+	const char	*tuples_file;
+	uint32_t	nb_rules;
+	uint32_t	nb_tuples;
+	uint32_t	nb_rules_32;
+	uint32_t	nb_rules_64;
+	uint32_t	nb_rules_96;
+	uint32_t	nb_tuples_rnd;
+	uint8_t		fract_32;
+	uint8_t		fract_64;
+	uint8_t		fract_96;
+	uint8_t		fract_rnd_tuples;
+} config = {
+	.rules_file = NULL,
+	.tuples_file = NULL,
+	.nb_rules = DEF_RULE_NUM,
+	.nb_tuples = DEF_TUPLES_NUM,
+	.nb_rules_32 = 0,
+	.nb_rules_64 = 0,
+	.nb_rules_96 = 0,
+	.nb_tuples_rnd = 0,
+	.fract_32 = 90,
+	.fract_64 = 9,
+	.fract_96 = 1,
+	.fract_rnd_tuples = 0
+};
+
+enum {
+	CB_RULE_SPI,
+	CB_RULE_DIP,
+	CB_RULE_SIP,
+	CB_RULE_LEN,
+	CB_RULE_NUM,
+};
+
+static char line[LINE_MAX];
+struct rule {
+	struct rte_ipsec_sadv4_key tuple;
+	int rule_type;
+};
+
+static struct rule *rules_tbl;
+static struct rule *tuples_tbl;
+
+static int
+parse_distrib(const char *in)
+{
+	int a, b, c;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, 0);
+
+	if ((a + b + c) != 100)
+		return -EINVAL;
+
+	config.fract_32 = a;
+	config.fract_64 = b;
+	config.fract_96 = c;
+
+	return 0;
+}
+
+static void
+print_config(void)
+{
+	fprintf(stdout,
+		"Rules total: %u\n"
+		"Configured rules distribution SPI/SPI_DIP/SIP_DIP_SIP:"
+		"%u/%u/%u\n"
+		"SPI only rules: %u\n"
+		"SPI_DIP  rules: %u\n"
+		"SPI_DIP_SIP rules: %u\n"
+		"Lookup tuples: %u\n"
+		"Configured fraction of random tuples: %u\n"
+		"Random lookup tuples: %u\n",
+		config.nb_rules, config.fract_32, config.fract_64,
+		config.fract_96, config.nb_rules_32, config.nb_rules_64,
+		config.nb_rules_96, config.nb_tuples, config.fract_rnd_tuples,
+		config.nb_tuples_rnd);
+}
+
+static void
+print_usage(void)
+{
+	fprintf(stdout,
+		PRINT_USAGE_START
+		"[-f <rules file>]\n"
+		"[-t <tuples file for lookup>]\n"
+		"[-n <rules number (if -f is not specified)>]\n"
+		"[-l <lookup tuples number (if -t is not specified)>]\n"
+		"[-d <\"/\" separated rules length distribution"
+		"(if -f is not specified)>]\n"
+		"[-r <random tuples fraction to lookup"
+		"(if -t is not specified)>]\n",
+		config.prgname);
+
+}
+
+static int
+get_str_num(FILE *f, int num)
+{
+	int n_lines = 0;
+
+	if (f != NULL) {
+		while (fgets(line, sizeof(line), f) != NULL)
+			n_lines++;
+		rewind(f);
+	} else {
+		n_lines = num;
+	}
+	return n_lines;
+}
+
+static int
+parse_file(FILE *f, struct rule *tbl, int rule_tbl)
+{
+	int ret, i, j = 0;
+	char *s, *sp, *in[CB_RULE_NUM];
+	static const char *dlm = " \t\n";
+	int string_tok_nb = RTE_DIM(in);
+
+	string_tok_nb -= (rule_tbl == 0) ? 1 : 0;
+	while (fgets(line, sizeof(line), f) != NULL) {
+		s = line;
+		for (i = 0; i != string_tok_nb; i++) {
+			in[i] = strtok_r(s, dlm, &sp);
+			if (in[i] == NULL)
+				return -EINVAL;
+			s = NULL;
+		}
+		GET_CB_FIELD(in[CB_RULE_SPI], tbl[j].tuple.spi, 0,
+				UINT32_MAX, 0);
+
+		ret = inet_pton(AF_INET, in[CB_RULE_DIP], &tbl[j].tuple.dip);
+		if (ret != 1)
+			return -EINVAL;
+		ret = inet_pton(AF_INET, in[CB_RULE_SIP], &tbl[j].tuple.sip);
+		if (ret != 1)
+			return -EINVAL;
+		if ((rule_tbl) && (in[CB_RULE_LEN] != NULL)) {
+			if (strcmp(in[CB_RULE_LEN], "SPI_DIP_SIP") == 0) {
+				tbl[j].rule_type = RTE_IPSEC_SAD_SPI_DIP_SIP;
+				config.nb_rules_96++;
+			} else if (strcmp(in[CB_RULE_LEN], "SPI_DIP") == 0) {
+				tbl[j].rule_type = RTE_IPSEC_SAD_SPI_DIP;
+				config.nb_rules_64++;
+			} else if (strcmp(in[CB_RULE_LEN], "SPI") == 0) {
+				tbl[j].rule_type = RTE_IPSEC_SAD_SPI_ONLY;
+				config.nb_rules_32++;
+			} else {
+				return -EINVAL;
+			}
+		}
+		j++;
+	}
+	return 0;
+}
+
+static void
+get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
+{
+	unsigned i, rnd;
+	int rule_type;
+
+	for (i = 0; i < nb_rules; i++) {
+		rnd = rte_rand() % 100;
+		if (rule_tbl) {
+			tbl[i].tuple.spi = rte_rand();
+			tbl[i].tuple.dip = rte_rand();
+			tbl[i].tuple.sip = rte_rand();
+			if (rnd >= (100UL - config.fract_32)) {
+				rule_type = RTE_IPSEC_SAD_SPI_ONLY;
+				config.nb_rules_32++;
+			} else if (rnd >= (100UL - (config.fract_32 +
+					config.fract_64))) {
+				rule_type = RTE_IPSEC_SAD_SPI_DIP;
+				config.nb_rules_64++;
+			} else {
+				rule_type = RTE_IPSEC_SAD_SPI_DIP_SIP;
+				config.nb_rules_96++;
+			}
+			tbl[i].rule_type = rule_type;
+		} else {
+			if (rnd >= 100UL - config.fract_rnd_tuples) {
+				tbl[i].tuple.spi = rte_rand();
+				tbl[i].tuple.dip = rte_rand();
+				tbl[i].tuple.sip = rte_rand();
+				config.nb_tuples_rnd++;
+			} else {
+				tbl[i].tuple.spi = rules_tbl[i %
+					config.nb_rules].tuple.spi;
+				tbl[i].tuple.dip = rules_tbl[i %
+					config.nb_rules].tuple.dip;
+				tbl[i].tuple.sip = rules_tbl[i %
+					config.nb_rules].tuple.sip;
+			}
+		}
+	}
+}
+
+static void
+tbl_init(struct rule **tbl, uint32_t *n_entries,
+	const char *file_name, int rule_tbl)
+{
+	FILE *f = NULL;
+	int ret;
+	const char *rules = "rules";
+	const char *tuples = "tuples";
+
+	if (file_name != NULL) {
+		f = fopen(file_name, "r");
+		if (f == NULL)
+			rte_exit(-EINVAL, "failed to open file: %s\n",
+				file_name);
+	}
+
+	printf("init %s table...", (rule_tbl) ? rules : tuples);
+	*n_entries = get_str_num(f, *n_entries);
+	printf("%d entries\n", *n_entries);
+	*tbl = rte_zmalloc(NULL, sizeof(struct rule) * *n_entries,
+		RTE_CACHE_LINE_SIZE);
+	if (*tbl == NULL)
+		rte_exit(-ENOMEM, "failed to allocate tbl\n");
+
+	if (f != NULL) {
+		printf("parse file %s\n", file_name);
+		ret = parse_file(f, *tbl, rule_tbl);
+		if (ret != 0)
+			rte_exit(-EINVAL, "failed to parse file %s\n"
+				"rules file must be: "
+				"<uint32_t: spi> <space> "
+				"<ip_addr: dip> <space> "
+				"<ip_addr: sip> <space> "
+				"<string: SPI|SPI_DIP|SIP_DIP_SIP>\n"
+				"tuples file must be: "
+				"<uint32_t: spi> <space> "
+				"<ip_addr: dip> <space> "
+				"<ip_addr: sip>\n",
+				file_name);
+	} else {
+		printf("generate random values in %s table\n",
+			(rule_tbl) ? rules : tuples);
+		get_random_rules(*tbl, *n_entries, rule_tbl);
+	}
+	if (f != NULL)
+		fclose(f);
+}
+
+static void
+parse_opts(int argc, char **argv)
+{
+	int opt, ret;
+	char *endptr;
+
+	while ((opt = getopt(argc, argv, "f:t:n:d:l:r:")) != -1) {
+		switch (opt) {
+		case 'f':
+			config.rules_file = optarg;
+			break;
+		case 't':
+			config.tuples_file = optarg;
+			break;
+		case 'n':
+			errno = 0;
+			config.nb_rules = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.nb_rules == 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -n\n");
+			}
+			break;
+		case 'd':
+			ret = parse_distrib(optarg);
+			if (ret != 0) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -d\n");
+			}
+			break;
+		case 'l':
+			errno = 0;
+			config.nb_tuples = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.nb_tuples == 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -l\n");
+			}
+			break;
+		case 'r':
+			errno = 0;
+			config.fract_rnd_tuples = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.fract_rnd_tuples == 0) ||
+					(config.fract_rnd_tuples >= 100)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -r\n");
+			}
+			break;
+		default:
+			print_usage();
+			rte_exit(-EINVAL, "Invalid options\n");
+		}
+	}
+}
+
+#define BURST_SZ	64
+static void
+lookup(struct rte_ipsec_sad *sad)
+{
+	int ret, j;
+	unsigned i;
+	const union rte_ipsec_sad_key *keys[BURST_SZ];
+	void *vals[BURST_SZ];
+	uint64_t start, acc = 0;
+
+	for (i = 0; i < config.nb_tuples; i += BURST_SZ) {
+		for (j = 0; j < BURST_SZ; j++)
+			keys[j] = (union rte_ipsec_sad_key *)
+				(&tuples_tbl[i + j].tuple);
+		start = rte_rdtsc();
+		ret = rte_ipsec_sad_lookup(sad, keys, BURST_SZ, vals);
+		acc += rte_rdtsc() - start;
+		if (ret < 0)
+			rte_exit(-EINVAL, "Lookup failed\n");
+	}
+	printf("Average lookup cycles %lu\n", acc / config.nb_tuples);
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+	unsigned i;
+	struct rte_ipsec_sad *sad;
+	struct rte_ipsec_sad_conf conf;
+	uint64_t start;
+
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_panic("Cannot init EAL\n");
+
+	argc -= ret;
+	argv += ret;
+
+	config.prgname = argv[0];
+
+	parse_opts(argc, argv);
+	tbl_init(&rules_tbl, &config.nb_rules, config.rules_file, 1);
+	tbl_init(&tuples_tbl, &config.nb_tuples, config.tuples_file, 0);
+	if (config.rules_file != NULL) {
+		config.fract_32 = (100 * config.nb_rules_32) / config.nb_rules;
+		config.fract_64 = (100 * config.nb_rules_64) / config.nb_rules;
+		config.fract_96 = (100 * config.nb_rules_96) / config.nb_rules;
+	}
+	if (config.tuples_file != NULL) {
+		config.fract_rnd_tuples = 0;
+		config.nb_tuples_rnd = 0;
+	}
+	conf.socket_id = -1;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = config.nb_rules * 2;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules * 2;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules * 2;
+	conf.flags = RTE_IPSEC_SAD_FLAG_IPV4|RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
+	sad = rte_ipsec_sad_create("test", &conf);
+	if (sad == NULL)
+		rte_exit(-rte_errno, "can not allocate SAD table\n");
+
+	print_config();
+
+	start = rte_rdtsc();
+	for (i = 0; i < config.nb_rules; i++) {
+		ret = rte_ipsec_sad_add(sad,
+			(union rte_ipsec_sad_key *)&rules_tbl[i].tuple,
+			rules_tbl[i].rule_type, &rules_tbl[i]);
+		if (ret != 0)
+			rte_exit(ret, "can not add rule to SAD table\n");
+	}
+	printf("Average ADD cycles: %lu\n",
+		(rte_rdtsc() - start) / config.nb_rules);
+
+	lookup(sad);
+
+	return 0;
+}
diff --git a/app/test-sad/meson.build b/app/test-sad/meson.build
new file mode 100644
index 0000000..31f9aab
--- /dev/null
+++ b/app/test-sad/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2019 Intel Corporation
+
+allow_experimental_apis = true
+sources = files('main.c')
+deps += ['ipsec', 'net']
-- 
2.7.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, back to index

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
2019-08-13 15:13 ` [dpdk-dev] [RFC 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
2019-08-13 15:13 ` [dpdk-dev] [RFC 2/5] ipsec: add SAD create/free API Vladimir Medvedkin
2019-08-13 15:13 ` [dpdk-dev] [RFC 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
2019-08-13 15:13 ` [dpdk-dev] [RFC 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
2019-08-13 15:13 ` [dpdk-dev] [RFC 5/5] app: add test-sad application Vladimir Medvedkin

DPDK-dev Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/dpdk-dev/0 dpdk-dev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 dpdk-dev dpdk-dev/ https://lore.kernel.org/dpdk-dev \
		dev@dpdk.org dpdk-dev@archiver.kernel.org
	public-inbox-index dpdk-dev


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.dpdk.dev


AGPL code for this site: git clone https://public-inbox.org/ public-inbox