DPDK patches and discussions
 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
                   ` (11 more replies)
  0 siblings, 12 replies; 60+ 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] 60+ 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
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 60+ 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] 60+ 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
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 60+ 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] 60+ 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
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 60+ 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] 60+ 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
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 60+ 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] 60+ 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
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 60+ 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] 60+ messages in thread

* [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (4 preceding siblings ...)
  2019-08-13 15:13 ` [dpdk-dev] [RFC 5/5] app: add test-sad application Vladimir Medvedkin
@ 2019-09-03 16:55 ` Vladimir Medvedkin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
                     ` (5 more replies)
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
                   ` (5 subsequent siblings)
  11 siblings, 6 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-09-03 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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] 60+ messages in thread

* [dpdk-dev] [PATCH v1 1/5] ipsec: add inbound SAD API
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (5 preceding siblings ...)
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
@ 2019-09-03 16:55 ` Vladimir Medvedkin
  2019-09-14 23:05   ` Ananyev, Konstantin
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 2/5] ipsec: add SAD create/free API Vladimir Medvedkin
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-09-03 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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] 60+ messages in thread

* [dpdk-dev] [PATCH v1 2/5] ipsec: add SAD create/free API
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (6 preceding siblings ...)
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-09-03 16:55 ` Vladimir Medvedkin
  2019-09-12 18:08   ` Ananyev, Konstantin
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-09-03 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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] 60+ messages in thread

* [dpdk-dev] [PATCH v1 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
                   ` (7 preceding siblings ...)
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 2/5] ipsec: add SAD create/free API Vladimir Medvedkin
@ 2019-09-03 16:55 ` Vladimir Medvedkin
  2019-09-12 17:58   ` Ananyev, Konstantin
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-09-03 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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] 60+ messages in thread

* [dpdk-dev] [PATCH v1 4/5] test/ipsec: add ipsec SAD autotests
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (8 preceding siblings ...)
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-09-03 16:55 ` Vladimir Medvedkin
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 5/5] app: add test-sad application Vladimir Medvedkin
  2019-09-12 18:34 ` [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Ananyev, Konstantin
  11 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-09-03 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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] 60+ messages in thread

* [dpdk-dev] [PATCH v1 5/5] app: add test-sad application
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (9 preceding siblings ...)
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-09-03 16:55 ` Vladimir Medvedkin
  2019-09-12 18:30   ` Ananyev, Konstantin
  2019-09-12 18:34 ` [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Ananyev, Konstantin
  11 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-09-03 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v1 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-09-12 17:58   ` Ananyev, Konstantin
  2019-10-01 17:24     ` Medvedkin, Vladimir
  0 siblings, 1 reply; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-09-12 17:58 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal

Hi Vladimir,

> 
> 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)
>  {

const void *key
? 

> -	return -ENOTSUP;
> +	void *tmp_val;
> +	int ret;
> +	uint32_t *cnt;

Few extra comments inside that function and add_specific() wouldn't hurt.

> +
> +	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,

const 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);

I think something like:
ret = ret < 0 ? ret : 0;
is needed here.

> +		} 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);
>  }
> 

Pls add a comment for that one and other internal function.
Even if you do remember what exactly it does now, you won't a year later :)

> +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;

s/n_pkts/found/
?

> +
> +	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);
> +	}

Just as a thought - instead of setting all sa[] to NULL first, and then
going though only found in the loop above, wouldn't it be a bit faster -
after lookup bulk go through all sa[] and set them depending on mask value?
Then first(zero sa[] loop) can be removed.

> +
> +	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[])

Better to follow usual parameter convention and move 'n' after pointers, i.e.:

int rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad, const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)

Or provably even better:
int rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad, const union rte_ipsec_sad_key *keys[], const void *sa[], uint32_t n)


>  {
> -	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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v1 2/5] ipsec: add SAD create/free API
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 2/5] ipsec: add SAD create/free API Vladimir Medvedkin
@ 2019-09-12 18:08   ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-09-12 18:08 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal


> --- 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
> +


As a nit - probably worth to add a comment what this structure is for,
Aslo might be better to use something more informative for names:
dip, dip_sip or so.
or even:


> +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[];
> +};
> +

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

* Re: [dpdk-dev] [PATCH v1 5/5] app: add test-sad application
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 5/5] app: add test-sad application Vladimir Medvedkin
@ 2019-09-12 18:30   ` Ananyev, Konstantin
  2019-09-12 18:33     ` Ananyev, Konstantin
  0 siblings, 1 reply; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-09-12 18:30 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal



> 
> Usage example and performance evaluation for the ipsec SAD library

As a generic comment - I think <DEL> all rules after lookup is missing.
Also seems right now only ipv4 keys are supported by the app.
Would be good to have ipv6 support also.

> 
> 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();

As discussed off-line that can cause duplicate keys.
Probably not big deal, but would be good either to get rid of it, or just count number of
generated dups.


> +			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);

Here and in other similar places - better to check that endptr[0] == 0.

> +			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];

Would it beneficial to have burst_size configurable (extra cmd-line option)?

> +	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();

rdtsc_precise() seems more appropriate here and below.

> +		ret = rte_ipsec_sad_lookup(sad, keys, BURST_SZ, vals);
> +		acc += rte_rdtsc() - start;

Probably worth to add verbose mode (cmd-line option) and when it's on -
dump to stdout lookup key and result.
That will allow for user to do functional test/verification - will be handy.

> +		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;

Seems that would create way too big table...
Wouldn't something like:

 conf.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = config.nb_rules_32 * 5 / 4;
 conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
 conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;

be sufficient here?

> +	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);

Probably worth to move <add> block into a separate function.
Also would be interesting to see does ADD time changes as table fills in
(print stats for every 10% add or so).

> +
> +	lookup(sad);

As another possible enhancement - add ability to run lookup() on all
available lcores on parallel. 

> +
> +	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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v1 5/5] app: add test-sad application
  2019-09-12 18:30   ` Ananyev, Konstantin
@ 2019-09-12 18:33     ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-09-12 18:33 UTC (permalink / raw)
  To: Ananyev, Konstantin, Medvedkin, Vladimir, dev
  Cc: Iremonger, Bernard, akhil.goyal



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ananyev, Konstantin
> Sent: Thursday, September 12, 2019 7:31 PM
> To: Medvedkin, Vladimir <vladimir.medvedkin@intel.com>; dev@dpdk.org
> Cc: Iremonger, Bernard <bernard.iremonger@intel.com>; akhil.goyal@nxp.com
> Subject: Re: [dpdk-dev] [PATCH v1 5/5] app: add test-sad application
> 
> 
> 
> >
> > Usage example and performance evaluation for the ipsec SAD library
> 
> As a generic comment - I think <DEL> all rules after lookup is missing.
> Also seems right now only ipv4 keys are supported by the app.
> Would be good to have ipv6 support also.
> 

Also, pls fix 32-bit compilation problem:

/app/test-sad/main.c: In function 'main':
../app/test-sad/main.c:414:9: error: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'uint64_t {aka long long unsigned int}' [-Werror=format=]
  printf("Average ADD cycles: %lu\n",
         ^


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

* Re: [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD
  2019-08-13 15:13 [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                   ` (10 preceding siblings ...)
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 5/5] app: add test-sad application Vladimir Medvedkin
@ 2019-09-12 18:34 ` Ananyev, Konstantin
  11 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-09-12 18:34 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard



> -----Original Message-----
> From: Medvedkin, Vladimir
> Sent: Tuesday, August 13, 2019 4:13 PM
> To: dev@dpdk.org
> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Iremonger, Bernard <bernard.iremonger@intel.com>
> Subject: [RFC 0/5] ipsec: add inbound SAD
> 
> 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

I think doc update (PG and RN) is also missing


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

* Re: [dpdk-dev] [PATCH v1 1/5] ipsec: add inbound SAD API
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-09-14 23:05   ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-09-14 23:05 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal

> --- /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

Don't think we need to values - ipv4/ipv6 flags are mutually exclusive here.
Might be better:
_ipv4=0, _ipv6=1 (or visa-versa) _mask=1

> +/** 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);

const struct rte_ipsec_sad_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);

As a nit - might be better name I _destroy.
Usually such API comes in pairs: create/destroy, alloc/free, etc.


> +
> +/**
> + * 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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v1 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-09-12 17:58   ` Ananyev, Konstantin
@ 2019-10-01 17:24     ` Medvedkin, Vladimir
  0 siblings, 0 replies; 60+ messages in thread
From: Medvedkin, Vladimir @ 2019-10-01 17:24 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev; +Cc: Iremonger, Bernard, akhil.goyal

Hi Konstantin,

On 12/09/2019 18:58, Ananyev, Konstantin wrote:
> Hi Vladimir,
>
>> 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)
>>   {
> const void *key
> ?
>
>> -	return -ENOTSUP;
>> +	void *tmp_val;
>> +	int ret;
>> +	uint32_t *cnt;
> Few extra comments inside that function and add_specific() wouldn't hurt.
>
>> +
>> +	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,
> const 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);
> I think something like:
> ret = ret < 0 ? ret : 0;
> is needed here.
>
>> +		} 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);
>>   }
>>
> Pls add a comment for that one and other internal function.
> Even if you do remember what exactly it does now, you won't a year later :)
>
>> +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;
> s/n_pkts/found/
> ?
>
>> +
>> +	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);
>> +	}
> Just as a thought - instead of setting all sa[] to NULL first, and then
> going though only found in the loop above, wouldn't it be a bit faster -
> after lookup bulk go through all sa[] and set them depending on mask value?
> Then first(zero sa[] loop) can be removed.

It turns out that this would be slower if there are keys that cause 
lookup miss.

>
>> +
>> +	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[])
> Better to follow usual parameter convention and move 'n' after pointers, i.e.:
>
> int rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad, const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
>
> Or provably even better:
> int rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad, const union rte_ipsec_sad_key *keys[], const void *sa[], uint32_t n)
>
>
>>   {
>> -	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

-- 
Regards,
Vladimir


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

* [dpdk-dev] [PATCH v2 0/5] ipsec: add inbound SAD
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
@ 2019-10-01 17:25   ` " Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
                       ` (5 more replies)
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
                     ` (4 subsequent siblings)
  5 siblings, 6 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-01 17:25 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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
- documentation

v2:
- various bugs fixed
- rte_ipsec_sad_free renamed to rte_ipsec_sad_destroy
- added const qualifier to rte_ipsec_sad_key *key for add/delete
- added more comments into the code
- added ipv6 support into the testsad app
- added <DEL> measurement into the testsad app
- random SPI values are generated without dups
- added support for configurable burst size in testsad app
- added verbose mode into the testsad app


Vladimir Medvedkin (5):
  ipsec: add inbound SAD API
  ipsec: add SAD create/destroy implementation
  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                    | 662 +++++++++++++++++++++++++
 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              | 856 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/Makefile              |   4 +-
 lib/librte_ipsec/ipsec_sad.c           | 523 ++++++++++++++++++++
 lib/librte_ipsec/meson.build           |   6 +-
 lib/librte_ipsec/rte_ipsec_sad.h       | 175 +++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 +
 14 files changed, 2264 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] 60+ messages in thread

* [dpdk-dev] [PATCH v2 1/5] ipsec: add inbound SAD API
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
@ 2019-10-01 17:25   ` Vladimir Medvedkin
  2019-10-02 11:24     ` Ananyev, Konstantin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
                     ` (3 subsequent siblings)
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-01 17:25 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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       | 175 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 ++
 5 files changed, 236 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..703be65
--- /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 const 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 const 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 const 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_destroy(__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 void *sa[], __rte_unused uint32_t n)
+{
+	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..d6bd802
--- /dev/null
+++ b/lib/librte_ipsec/rte_ipsec_sad.h
@@ -0,0 +1,175 @@
+
+/* 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_IPV6			0x1
+/** Flag to support reader writer concurrency */
+#define RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY	0x2
+
+/** 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,
+	const 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,
+	const 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, const struct rte_ipsec_sad_conf *conf);
+
+/**
+ * Find an existing SAD object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the SAD 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);
+
+/**
+ * Destroy SAD object.
+ *
+ * @param sad
+ *   pointer to the SAD object
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_ipsec_sad_destroy(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[],
+	void *sa[], uint32_t n);
+
+#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..3c6c630 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_destroy;
+	rte_ipsec_sad_lookup;
+
 	local: *;
 };
-- 
2.7.4


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

* [dpdk-dev] [PATCH v2 2/5] ipsec: add SAD create/destroy implementation
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-10-01 17:25   ` Vladimir Medvedkin
  2019-10-02 11:55     ` Ananyev, Konstantin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
                     ` (2 subsequent siblings)
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-01 17:25 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Replace rte_ipsec_sad_create(), rte_ipsec_sad_destroy() 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 | 233 +++++++++++++++++++++++++++++++++++++++++--
 lib/librte_ipsec/meson.build |   2 +-
 3 files changed, 228 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 703be65..5e05c6f 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -2,10 +2,53 @@
  * 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"
 
+/*
+ * 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>" */
+#define SAD_FORMAT		SAD_PREFIX "%s"
+
+#define DEFAULT_HASH_FUNC	rte_jhash
+#define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
+
+struct hash_cnt {
+	uint32_t cnt_dip;
+	uint32_t cnt_dip_sip;
+};
+
+struct rte_ipsec_sad {
+	char name[IPSEC_SAD_NAMESIZE];
+	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	/* Array to track number of more specific rules
+	 * (spi_dip or spi_dip_sip). Used only in add/delete
+	 * as a helper struct.
+	 */
+	__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 const union rte_ipsec_sad_key *key,
@@ -23,22 +66,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 const struct rte_ipsec_sad_conf *conf)
+rte_ipsec_sad_create(const char *name, const 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)) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	/** Init SAD*/
+	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		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_destroy(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_destroy(sad);
+		return NULL;
+	}
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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_destroy(sad);
+		return NULL;
+	}
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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_destroy(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_destroy(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_destroy(__rte_unused struct rte_ipsec_sad *sad)
+rte_ipsec_sad_destroy(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] 60+ messages in thread

* [dpdk-dev] [PATCH v2 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                     ` (2 preceding siblings ...)
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
@ 2019-10-01 17:25   ` Vladimir Medvedkin
  2019-10-02 12:04     ` Ananyev, Konstantin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-01 17:25 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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 | 278 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 266 insertions(+), 12 deletions(-)

diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
index 5e05c6f..b908dd2 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -49,20 +49,182 @@ 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, const void *key,
+		int key_type, void *sa)
+{
+	void *tmp_val;
+	int ret, notexist;
+
+	/* Check if the key is present in the table.
+	 * Need for further accaunting in cnt_arr
+	 */
+	ret = rte_hash_lookup(sad->hash[key_type], key);
+	notexist = (ret == -ENOENT);
+
+	/* Add an SA to the corresponding table.*/
+	ret = rte_hash_add_key_data(sad->hash[key_type], key, sa);
+	if (ret != 0)
+		return ret;
+
+	/* Check if there is an entry in SPI only table with the same SPI */
+	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);
+
+	/* Add an entry into SPI only table */
+	ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, tmp_val);
+	if (ret != 0)
+		return ret;
+
+	/* Update a counter for a given SPI */
+	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_dip += notexist;
+	else
+		sad->cnt_arr[ret].cnt_dip_sip += notexist;
+
+	return 0;
+}
+
 int
-rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type, __rte_unused void *sa)
+rte_ipsec_sad_add(struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *key,
+		int key_type, void *sa)
 {
-	return -ENOTSUP;
+	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, const void *key, int key_type)
+{
+	void *tmp_val;
+	int ret;
+	uint32_t *cnt;
+
+	/* Remove an SA from the corresponding table.*/
+	ret = rte_hash_del_key(sad->hash[key_type], key);
+	if (ret < 0)
+		return ret;
+
+	/* Get an index of cnt_arr entry for a given SPI */
+	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_dip :
+			&sad->cnt_arr[ret].cnt_dip_sip;
+	if (--(*cnt) != 0)
+		return 0;
+
+	/* corresponding counter is 0, clear the bit indicating
+	 * the presence of more specific rule for a given SPI.
+	 */
+	tmp_val = CLEAR_BIT(tmp_val, key_type);
+
+	/* if there are no rules left with same SPI,
+	 * remove an entry from SPI_only table
+	 */
+	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 const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type)
+rte_ipsec_sad_del(struct rte_ipsec_sad *sad,
+		const 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) {
+			ret = rte_hash_del_key(sad->hash[key_type],
+				key);
+			ret = ret < 0 ? ret : 0;
+		} 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 *
@@ -260,10 +422,102 @@ rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
 		rte_free(te);
 }
 
+/*
+ * @internal helper function
+ * Lookup a batch of keys in three hash tables.
+ * First lookup key in SPI_ONLY table.
+ * If there is an entry for the corresponding SPI check its value.
+ * Two least significant bits of the value indicate
+ * the presence of more specific rule in other tables.
+ * Perform additional lookup in corresponding hash tables
+ * and update the value if lookup succeeded.
+ */
+static int
+__ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
+{
+	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 found = 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];
+		}
+		/* clear 2 LSB's which idicate the presence
+		 * of more specific rules
+		 */
+		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
+	}
+
+	/* Lookup for more specific rules in SPI_DIP table */
+	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];
+		}
+	}
+	/* Lookup for more specific rules in SPI_DIP_SIP table */
+	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++)
+		found += (sa[i] != NULL);
+
+	return found;
+}
+
 int
-rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *keys[],
-		__rte_unused void *sa[], __rte_unused uint32_t n)
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
 {
-	return -ENOTSUP;
+	uint32_t num, i = 0;
+	int found = 0;
+
+	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
+		return -EINVAL;
+
+	do {
+		num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
+		found += __ipsec_sad_lookup(sad,
+			&keys[i], &sa[i], num);
+		i += num;
+	} while (i != n);
+
+	return found;
 }
-- 
2.7.4


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

* [dpdk-dev] [PATCH v2 4/5] test/ipsec: add ipsec SAD autotests
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                     ` (3 preceding siblings ...)
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-10-01 17:25   ` Vladimir Medvedkin
  2019-10-02 11:16     ` Ananyev, Konstantin
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-01 17:25 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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 | 856 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 864 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..1778b08
--- /dev/null
+++ b/app/test/test_ipsec_sad.c
@@ -0,0 +1,856 @@
+/* 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 = 0;
+
+	/* 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;
+
+	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.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_destroy(sad);
+	}
+	return TEST_SUCCESS;
+}
+
+static int32_t
+__test_add_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_add_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+
+}
+
+static int32_t
+__test_delete_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_delete_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_invalid(int ipv6, 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, sa, 1);
+	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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, NULL, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_destroy(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(0,
+			(union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_invalid(1,
+			(union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_basic(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	RTE_TEST_ASSERT((status == 0) && (sa[0] == NULL),
+		"Lookup returns an unexpected result\n");
+
+	rte_ipsec_sad_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			(union rte_ipsec_sad_key *)&tuple_v6_1);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_adv(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			key_arr);
+
+	return status;
+}
+
+
+static int32_t
+__test_lookup_order(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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_destroy(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(0, (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(1, (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] 60+ messages in thread

* [dpdk-dev] [PATCH v2 5/5] app: add test-sad application
  2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
                     ` (4 preceding siblings ...)
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-10-01 17:25   ` Vladimir Medvedkin
  2019-10-02 13:27     ` Ananyev, Konstantin
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-01 17:25 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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      | 662 +++++++++++++++++++++++++++++++++++++++++++++++
 app/test-sad/meson.build |   6 +
 5 files changed, 689 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..753721e
--- /dev/null
+++ b/app/test-sad/main.c
@@ -0,0 +1,662 @@
+/* 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
+#define BURST_SZ	64
+
+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;
+	uint32_t	burst_sz;
+	uint8_t		fract_32;
+	uint8_t		fract_64;
+	uint8_t		fract_96;
+	uint8_t		fract_rnd_tuples;
+	int		ipv6;
+	int		verbose;
+} 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,
+	.burst_sz = BURST_SZ,
+	.fract_32 = 90,
+	.fract_64 = 9,
+	.fract_96 = 1,
+	.fract_rnd_tuples = 0,
+	.ipv6 = 0,
+	.verbose = 0
+};
+
+enum {
+	CB_RULE_SPI,
+	CB_RULE_DIP,
+	CB_RULE_SIP,
+	CB_RULE_LEN,
+	CB_RULE_NUM,
+};
+
+static char line[LINE_MAX];
+struct rule {
+	union rte_ipsec_sad_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"
+		"Lookup burst size %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.burst_sz,
+		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"
+		"[-6 <ipv6 tests>]\n"
+		"[-d <\"/\" separated rules length distribution"
+		"(if -f is not specified)>]\n"
+		"[-r <random tuples fraction to lookup"
+		"(if -t is not specified)>]\n"
+		"[-b <lookup burst size: 1-64 >]\n"
+		"[-v <verbose, print results on lookup>]\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.v4.spi, 0,
+				UINT32_MAX, 0);
+
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_DIP],
+				&tbl[j].tuple.v6.dip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_DIP],
+				&tbl[j].tuple.v4.dip);
+		if (ret != 1)
+			return -EINVAL;
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_SIP],
+				&tbl[j].tuple.v6.sip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_SIP],
+				&tbl[j].tuple.v4.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 uint64_t
+get_rnd_rng(uint64_t l, uint64_t u)
+{
+	if (l == u)
+		return l;
+	else
+		return (rte_rand() % (u - l) + l);
+}
+
+static void
+get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
+{
+	unsigned i, j, rnd;
+	int rule_type;
+	double edge = 0;
+	double step;
+
+	step = (double)UINT32_MAX / nb_rules;
+	for (i = 0; i < nb_rules; i++, edge += step) {
+		rnd = rte_rand() % 100;
+		if (rule_tbl) {
+			tbl[i].tuple.v4.spi = get_rnd_rng((uint64_t)edge,
+						(uint64_t)(edge + step));
+			if (config.ipv6) {
+				for (j = 0; j < 16; j++) {
+					tbl[i].tuple.v6.dip[j] = rte_rand();
+					tbl[i].tuple.v6.sip[j] = rte_rand();
+				}
+			} else {
+				tbl[i].tuple.v4.dip = rte_rand();
+				tbl[i].tuple.v4.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.v4.spi =
+					get_rnd_rng((uint64_t)edge,
+					(uint64_t)(edge + step));
+				if (config.ipv6) {
+					for (j = 0; j < 16; j++) {
+						tbl[i].tuple.v6.dip[j] =
+								rte_rand();
+						tbl[i].tuple.v6.sip[j] =
+								rte_rand();
+					}
+				} else {
+					tbl[i].tuple.v4.dip = rte_rand();
+					tbl[i].tuple.v4.sip = rte_rand();
+				}
+				config.nb_tuples_rnd++;
+			} else {
+				tbl[i].tuple.v4.spi = rules_tbl[i %
+					config.nb_rules].tuple.v4.spi;
+				if (config.ipv6) {
+					int r_idx = i % config.nb_rules;
+					memcpy(tbl[i].tuple.v6.dip,
+						rules_tbl[r_idx].tuple.v6.dip,
+						sizeof(tbl[i].tuple.v6.dip));
+					memcpy(tbl[i].tuple.v6.sip,
+						rules_tbl[r_idx].tuple.v6.sip,
+						sizeof(tbl[i].tuple.v6.sip));
+				} else {
+					tbl[i].tuple.v4.dip = rules_tbl[i %
+						config.nb_rules].tuple.v4.dip;
+					tbl[i].tuple.v4.sip = rules_tbl[i %
+						config.nb_rules].tuple.v4.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:6b:v")) != -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) ||
+					(endptr[0] != 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 'b':
+			errno = 0;
+			config.burst_sz = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.burst_sz == 0) ||
+					(config.burst_sz > 64) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -b\n");
+			}
+			break;
+		case 'l':
+			errno = 0;
+			config.nb_tuples = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.nb_tuples == 0) ||
+					(endptr[0] != 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) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -r\n");
+			}
+			break;
+		case '6':
+			config.ipv6 = 1;
+			break;
+		case 'v':
+			config.verbose = 1;
+			break;
+		default:
+			print_usage();
+			rte_exit(-EINVAL, "Invalid options\n");
+		}
+	}
+}
+
+static void
+print_addr(int af, const uint8_t *addr)
+{
+	int i;
+
+	if (af == AF_INET) {
+		for (i = 0; i < 3; i++)
+			printf("%d.", addr[i]);
+		printf("%d", addr[i]);
+	} else {
+		for (i = 0; i < 7; i++)
+			printf("%04x:", addr[2 * i] << 8 | addr[2 * i + 1]);
+		printf("%04x", addr[2 * i] << 8 | addr[2 * i + 1]);
+	}
+
+}
+
+static void
+print_result(const union rte_ipsec_sad_key *key, void *res)
+{
+	struct rule *rule = res;
+	const struct rte_ipsec_sadv4_key *v4;
+	const struct rte_ipsec_sadv6_key *v6;
+	const char *spi_only = "SPI_ONLY";
+	const char *spi_dip = "SPI_DIP";
+	const char *spi_dip_sip = "SPI_DIP_SIP";
+	const char *rule_type;
+
+	if (res == NULL) {
+		if (config.ipv6) {
+			v6 = &key->v6;
+			printf("TUPLE: < SPI: %u DIP: ", v6->spi);
+			print_addr(AF_INET6, v6->dip);
+			printf(" SIP: ");
+			print_addr(AF_INET6, v6->sip);
+			printf(" > not found\n");
+		} else {
+			v4 = &key->v4;
+			printf("TUPLE: < SPI: %u DIP: ", v4->spi);
+			print_addr(AF_INET, (const uint8_t *)&v4->dip);
+			printf(" SIP: ");
+			print_addr(AF_INET, (const uint8_t *)&v4->sip);
+			printf(" > not found\n");
+		}
+		return;
+	}
+
+	switch (rule->rule_type) {
+	case RTE_IPSEC_SAD_SPI_ONLY:
+		rule_type = spi_only;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP:
+		rule_type = spi_dip;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP_SIP:
+		rule_type = spi_dip_sip;
+		break;
+	default:
+		return;
+	}
+
+	if (config.ipv6) {
+		v6 = &key->v6;
+		printf("TUPLE: < SPI: %u DIP: ", v6->spi);
+		print_addr(AF_INET6, v6->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET6, v6->sip);
+		printf(" >\n");
+		v6 = &rule->tuple.v6;
+		printf("\tpoints to RULE ID %zu < SPI: %u DIP: ",
+			RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule),
+			v6->spi);
+		print_addr(AF_INET6, v6->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET6, v6->sip);
+		printf("/%s >\n", rule_type);
+	} else {
+		v4 = &key->v4;
+		printf("TUPLE: < SPI: %u DIP: ", v4->spi);
+		print_addr(AF_INET, (const uint8_t *)&v4->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET, (const uint8_t *)&v4->sip);
+		printf(" >\n");
+		v4 = &rule->tuple.v4;
+		printf("\tpoints to RULE ID %zu < SPI: %u DIP: ",
+			RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule),
+			v4->spi);
+		print_addr(AF_INET, (const uint8_t *)&v4->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET, (const uint8_t *)&v4->sip);
+		printf("/%s >\n", rule_type);
+	}
+}
+
+static void
+lookup(struct rte_ipsec_sad *sad, uint32_t burst_sz)
+{
+	int ret;
+	unsigned int i, j;
+	const union rte_ipsec_sad_key *keys[burst_sz];
+	void *vals[burst_sz];
+	uint64_t start, acc = 0;
+
+	burst_sz = RTE_MIN(burst_sz, config.nb_tuples);
+	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_precise();
+		ret = rte_ipsec_sad_lookup(sad, keys, vals, burst_sz);
+		acc += rte_rdtsc_precise() - start;
+		if (ret < 0)
+			rte_exit(-EINVAL, "Lookup failed\n");
+		if (config.verbose) {
+			for (j = 0; j < burst_sz; j++)
+				print_result(keys[j], vals[j]);
+		}
+	}
+	printf("Average lookup cycles %.2Lf, lookups/sec: %.2Lf\n",
+		(long double)acc / config.nb_tuples,
+		(long double)config.nb_tuples * rte_get_tsc_hz() / acc);
+}
+
+static void
+add_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_add(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type, &rules_tbl[i]);
+			if (ret != 0)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("ADD %u rules, %.2Lf cycles/rule, %.2Lf ADD/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+static void
+del_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_del(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type);
+			if (ret != 0 && ret != -ENOENT)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("DEL %u rules, %.2Lf cycles/rule, %.2Lf DEL/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+	struct rte_ipsec_sad *sad;
+	struct rte_ipsec_sad_conf conf;
+
+	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_32 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;
+	if (config.ipv6)
+		conf.flags = RTE_IPSEC_SAD_FLAG_IPV6|
+			RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
+	else
+		conf.flags = 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();
+
+	add_rules(sad, 10);
+	lookup(sad, config.burst_sz);
+	del_rules(sad, 10);
+
+	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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/5] test/ipsec: add ipsec SAD autotests
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-10-02 11:16     ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-02 11:16 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal


> 
> Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
> ---

Please add a brief commit description :)
Apart from that:

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> --
> 2.7.4


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

* Re: [dpdk-dev] [PATCH v2 1/5] ipsec: add inbound SAD API
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-10-02 11:24     ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-02 11:24 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal

Hi Vladimir,

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

Whole patch series looks really good from my perspective.
Just few nits here and in other patches.
Konstantin

> --- /dev/null
> +++ b/lib/librte_ipsec/rte_ipsec_sad.h
> @@ -0,0 +1,175 @@
> +
> +/* 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.

Ok..., but rte_ipsec_sad.h is not included into rte_ipsec.h.
Probably just a copy&paste mistake?

> + * Contains helper functions to lookup and maintain SAD
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif

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

* Re: [dpdk-dev] [PATCH v2 2/5] ipsec: add SAD create/destroy implementation
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
@ 2019-10-02 11:55     ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-02 11:55 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal


> 
> Replace rte_ipsec_sad_create(), rte_ipsec_sad_destroy() 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 | 233 +++++++++++++++++++++++++++++++++++++++++--
>  lib/librte_ipsec/meson.build |   2 +-
>  3 files changed, 228 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 703be65..5e05c6f 100644
> --- a/lib/librte_ipsec/ipsec_sad.c
> +++ b/lib/librte_ipsec/ipsec_sad.c
> @@ -2,10 +2,53 @@
>   * 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"
> 
> +/*
> + * 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>" */
> +#define SAD_FORMAT		SAD_PREFIX "%s"
> +
> +#define DEFAULT_HASH_FUNC	rte_jhash
> +#define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
> +
> +struct hash_cnt {
> +	uint32_t cnt_dip;
> +	uint32_t cnt_dip_sip;
> +};
> +
> +struct rte_ipsec_sad {
> +	char name[IPSEC_SAD_NAMESIZE];
> +	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
> +	/* Array to track number of more specific rules
> +	 * (spi_dip or spi_dip_sip). Used only in add/delete
> +	 * as a helper struct.
> +	 */
> +	__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 const union rte_ipsec_sad_key *key,
> @@ -23,22 +66,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 const struct rte_ipsec_sad_conf *conf)
> +rte_ipsec_sad_create(const char *name, const 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)) {

Why not to allow 0 for some of the key_types, you are doing RTE_MAX() below anyway?
I.E:

If (... || (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))

> +		rte_errno = EINVAL;
> +		return NULL;
> +	}
> +
> +	/** Init SAD*/
> +	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
> +		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
> +		RTE_MAX(MIN_HASH_ENTRIES,
> +		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
> +		RTE_MAX(MIN_HASH_ENTRIES,
> +		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)) {

I think your forgot rte_free(sad) here.
Alternative approach would be to have it in different order:
char  sad_name[sad->name];
...
ret = snprintf(sad_name, sizeof(sad_name), SAD_FORMAT, name);
...
sad =  rte_zmalloc_socket(...);
memcpy(sad->name, sad_name, 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);

"sad_1_%p" to keep checkpatch happy? :)

> +	if (ret < 0 || ret >= (int)sizeof(hash_name)) {

I don't think this format can cause hash_name[] overflow,
so no point to check return value here.
Though if you do - you need to call sad_destroy() then.
But my suggestion would be just to remove that if(...) {...}. 

> +		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_destroy(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)) {

Same comments as above, can be safely removed I think.

> +		rte_errno = ENAMETOOLONG;
> +		rte_ipsec_sad_destroy(sad);
> +		return NULL;
> +	}
> +	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
> +	else
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
> +	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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)) {

Same comments as above, can be safely removed I think.

> +		rte_errno = ENAMETOOLONG;
> +		rte_ipsec_sad_destroy(sad);
> +		return NULL;
> +	}
> +	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
> +	else
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
> +	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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)

I think it should be just:
if (strcmp, sad->name, tmp_sad->name)


> +			break;
> +	}
> +	if (te != NULL) {
> +		rte_mcfg_tailq_write_unlock();
> +		rte_errno = EEXIST;
> +		rte_ipsec_sad_destroy(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_destroy(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)

As I can see you trying to compare:
"XXX" with "SAD_XXX".
?
Think you need to do something like that:
char tmp_name[IPSEC_SAD_NAMESIZE];
...
snprintf(tmp_name, sizeof(tmp_name), SAD_FORMAT, name); 
...
if (strcmp(tmp_name, sad->name) == 0)
...

> +			break;
> +	}
> +	rte_mcfg_tailq_read_unlock();
> +
> +	if (te == NULL) {
> +		rte_errno = ENOENT;
> +		return NULL;
> +	}
> +
> +	return sad;
>  }
> 
>  void
> -rte_ipsec_sad_destroy(__rte_unused struct rte_ipsec_sad *sad)
> +rte_ipsec_sad_destroy(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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v2 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-10-02 12:04     ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-02 12:04 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal


> 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>
> ---


> +/*
> + * @internal helper function
> + * Lookup a batch of keys in three hash tables.
> + * First lookup key in SPI_ONLY table.
> + * If there is an entry for the corresponding SPI check its value.
> + * Two least significant bits of the value indicate
> + * the presence of more specific rule in other tables.
> + * Perform additional lookup in corresponding hash tables
> + * and update the value if lookup succeeded.
> + */
> +static int
> +__ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
> +		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
> +{
> +	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 found = 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];
> +		}
> +		/* clear 2 LSB's which idicate the presence

s/idicate/indicate/

> +		 * of more specific rules
> +		 */
> +		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
> +	}
> +
> +	/* Lookup for more specific rules in SPI_DIP table */
> +	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];
> +		}
> +	}
> +	/* Lookup for more specific rules in SPI_DIP_SIP table */
> +	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++)
> +		found += (sa[i] != NULL);
> +
> +	return found;
> +}
> +
> --

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> 2.7.4


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

* Re: [dpdk-dev] [PATCH v2 5/5] app: add test-sad application
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 5/5] app: add test-sad application Vladimir Medvedkin
@ 2019-10-02 13:27     ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-02 13:27 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal



> 
> Usage example and performance evaluation for the ipsec SAD library

That's seems way too laconic :)

May be at least:
"Introduce new application to provide user to evaluate and perform
custom functional and performance tests for IPsec SAD implementation." 
?

Few more nits inline.

> 
> 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      | 662 +++++++++++++++++++++++++++++++++++++++++++++++
>  app/test-sad/meson.build |   6 +
>  5 files changed, 689 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..753721e
> --- /dev/null
> +++ b/app/test-sad/main.c
> @@ -0,0 +1,662 @@
> +/* 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
> +#define BURST_SZ	64
> +
> +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;
> +	uint32_t	burst_sz;
> +	uint8_t		fract_32;
> +	uint8_t		fract_64;
> +	uint8_t		fract_96;
> +	uint8_t		fract_rnd_tuples;
> +	int		ipv6;
> +	int		verbose;
> +} 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,
> +	.burst_sz = BURST_SZ,
> +	.fract_32 = 90,
> +	.fract_64 = 9,
> +	.fract_96 = 1,
> +	.fract_rnd_tuples = 0,
> +	.ipv6 = 0,
> +	.verbose = 0
> +};
> +
> +enum {
> +	CB_RULE_SPI,
> +	CB_RULE_DIP,
> +	CB_RULE_SIP,
> +	CB_RULE_LEN,
> +	CB_RULE_NUM,
> +};
> +
> +static char line[LINE_MAX];
> +struct rule {
> +	union rte_ipsec_sad_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"
> +		"Lookup burst size %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.burst_sz,
> +		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"
> +		"[-6 <ipv6 tests>]\n"
> +		"[-d <\"/\" separated rules length distribution"
> +		"(if -f is not specified)>]\n"
> +		"[-r <random tuples fraction to lookup"
> +		"(if -t is not specified)>]\n"
> +		"[-b <lookup burst size: 1-64 >]\n"
> +		"[-v <verbose, print results on lookup>]\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.v4.spi, 0,
> +				UINT32_MAX, 0);
> +
> +		if (config.ipv6)
> +			ret = inet_pton(AF_INET6, in[CB_RULE_DIP],
> +				&tbl[j].tuple.v6.dip);
> +		else
> +			ret = inet_pton(AF_INET, in[CB_RULE_DIP],
> +				&tbl[j].tuple.v4.dip);
> +		if (ret != 1)
> +			return -EINVAL;
> +		if (config.ipv6)
> +			ret = inet_pton(AF_INET6, in[CB_RULE_SIP],
> +				&tbl[j].tuple.v6.sip);
> +		else
> +			ret = inet_pton(AF_INET, in[CB_RULE_SIP],
> +				&tbl[j].tuple.v4.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 uint64_t
> +get_rnd_rng(uint64_t l, uint64_t u)
> +{
> +	if (l == u)
> +		return l;
> +	else
> +		return (rte_rand() % (u - l) + l);
> +}
> +
> +static void
> +get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
> +{
> +	unsigned i, j, rnd;
> +	int rule_type;
> +	double edge = 0;
> +	double step;
> +
> +	step = (double)UINT32_MAX / nb_rules;
> +	for (i = 0; i < nb_rules; i++, edge += step) {
> +		rnd = rte_rand() % 100;
> +		if (rule_tbl) {
> +			tbl[i].tuple.v4.spi = get_rnd_rng((uint64_t)edge,
> +						(uint64_t)(edge + step));
> +			if (config.ipv6) {
> +				for (j = 0; j < 16; j++) {
> +					tbl[i].tuple.v6.dip[j] = rte_rand();
> +					tbl[i].tuple.v6.sip[j] = rte_rand();
> +				}
> +			} else {
> +				tbl[i].tuple.v4.dip = rte_rand();
> +				tbl[i].tuple.v4.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.v4.spi =
> +					get_rnd_rng((uint64_t)edge,
> +					(uint64_t)(edge + step));
> +				if (config.ipv6) {
> +					for (j = 0; j < 16; j++) {
> +						tbl[i].tuple.v6.dip[j] =
> +								rte_rand();
> +						tbl[i].tuple.v6.sip[j] =
> +								rte_rand();
> +					}
> +				} else {
> +					tbl[i].tuple.v4.dip = rte_rand();
> +					tbl[i].tuple.v4.sip = rte_rand();
> +				}
> +				config.nb_tuples_rnd++;
> +			} else {
> +				tbl[i].tuple.v4.spi = rules_tbl[i %
> +					config.nb_rules].tuple.v4.spi;
> +				if (config.ipv6) {
> +					int r_idx = i % config.nb_rules;
> +					memcpy(tbl[i].tuple.v6.dip,
> +						rules_tbl[r_idx].tuple.v6.dip,
> +						sizeof(tbl[i].tuple.v6.dip));
> +					memcpy(tbl[i].tuple.v6.sip,
> +						rules_tbl[r_idx].tuple.v6.sip,
> +						sizeof(tbl[i].tuple.v6.sip));
> +				} else {
> +					tbl[i].tuple.v4.dip = rules_tbl[i %
> +						config.nb_rules].tuple.v4.dip;
> +					tbl[i].tuple.v4.sip = rules_tbl[i %
> +						config.nb_rules].tuple.v4.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:6b:v")) != -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) ||
> +					(endptr[0] != 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 'b':
> +			errno = 0;
> +			config.burst_sz = strtoul(optarg, &endptr, 10);
> +			if ((errno != 0) || (config.burst_sz == 0) ||
> +					(config.burst_sz > 64) ||
> +					(endptr[0] != 0)) {
> +				print_usage();
> +				rte_exit(-EINVAL, "Invalid option -b\n");
> +			}
> +			break;
> +		case 'l':
> +			errno = 0;
> +			config.nb_tuples = strtoul(optarg, &endptr, 10);
> +			if ((errno != 0) || (config.nb_tuples == 0) ||
> +					(endptr[0] != 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) ||
> +					(endptr[0] != 0)) {
> +				print_usage();
> +				rte_exit(-EINVAL, "Invalid option -r\n");
> +			}
> +			break;
> +		case '6':
> +			config.ipv6 = 1;
> +			break;
> +		case 'v':
> +			config.verbose = 1;
> +			break;
> +		default:
> +			print_usage();
> +			rte_exit(-EINVAL, "Invalid options\n");
> +		}
> +	}
> +}
> +
> +static void
> +print_addr(int af, const uint8_t *addr)
> +{
> +	int i;

Why not use inet_ntop inside that function?

> +
> +	if (af == AF_INET) {
> +		for (i = 0; i < 3; i++)
> +			printf("%d.", addr[i]);
> +		printf("%d", addr[i]);
> +	} else {
> +		for (i = 0; i < 7; i++)
> +			printf("%04x:", addr[2 * i] << 8 | addr[2 * i + 1]);
> +		printf("%04x", addr[2 * i] << 8 | addr[2 * i + 1]);
> +	}
> +
> +}
> +
> +static void
> +print_result(const union rte_ipsec_sad_key *key, void *res)
> +{
> +	struct rule *rule = res;
> +	const struct rte_ipsec_sadv4_key *v4;
> +	const struct rte_ipsec_sadv6_key *v6;
> +	const char *spi_only = "SPI_ONLY";
> +	const char *spi_dip = "SPI_DIP";
> +	const char *spi_dip_sip = "SPI_DIP_SIP";
> +	const char *rule_type;
> +
> +	if (res == NULL) {
> +		if (config.ipv6) {
> +			v6 = &key->v6;
> +			printf("TUPLE: < SPI: %u DIP: ", v6->spi);
> +			print_addr(AF_INET6, v6->dip);
> +			printf(" SIP: ");
> +			print_addr(AF_INET6, v6->sip);
> +			printf(" > not found\n");
> +		} else {
> +			v4 = &key->v4;
> +			printf("TUPLE: < SPI: %u DIP: ", v4->spi);
> +			print_addr(AF_INET, (const uint8_t *)&v4->dip);
> +			printf(" SIP: ");
> +			print_addr(AF_INET, (const uint8_t *)&v4->sip);
> +			printf(" > not found\n");
> +		}
> +		return;
> +	}
> +
> +	switch (rule->rule_type) {
> +	case RTE_IPSEC_SAD_SPI_ONLY:
> +		rule_type = spi_only;
> +		break;
> +	case RTE_IPSEC_SAD_SPI_DIP:
> +		rule_type = spi_dip;
> +		break;
> +	case RTE_IPSEC_SAD_SPI_DIP_SIP:
> +		rule_type = spi_dip_sip;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	if (config.ipv6) {
> +		v6 = &key->v6;
> +		printf("TUPLE: < SPI: %u DIP: ", v6->spi);
> +		print_addr(AF_INET6, v6->dip);
> +		printf(" SIP: ");
> +		print_addr(AF_INET6, v6->sip);
> +		printf(" >\n");

That looks identical to the code above for NULL result.
Could be squeezed together I think.
Another alternative: define 2 new fucntions (print_tuple, print_rule) and use it here
to reduce code duplication.

> +		v6 = &rule->tuple.v6;
> +		printf("\tpoints to RULE ID %zu < SPI: %u DIP: ",
> +			RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule),
> +			v6->spi);
> +		print_addr(AF_INET6, v6->dip);
> +		printf(" SIP: ");
> +		print_addr(AF_INET6, v6->sip);
> +		printf("/%s >\n", rule_type);
> +	} else {
> +		v4 = &key->v4;
> +		printf("TUPLE: < SPI: %u DIP: ", v4->spi);
> +		print_addr(AF_INET, (const uint8_t *)&v4->dip);
> +		printf(" SIP: ");
> +		print_addr(AF_INET, (const uint8_t *)&v4->sip);
> +		printf(" >\n");
> +		v4 = &rule->tuple.v4;
> +		printf("\tpoints to RULE ID %zu < SPI: %u DIP: ",
> +			RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule),
> +			v4->spi);
> +		print_addr(AF_INET, (const uint8_t *)&v4->dip);
> +		printf(" SIP: ");
> +		print_addr(AF_INET, (const uint8_t *)&v4->sip);
> +		printf("/%s >\n", rule_type);
> +	}
> +}
> +
> +static void
> +lookup(struct rte_ipsec_sad *sad, uint32_t burst_sz)
> +{
> +	int ret;
> +	unsigned int i, j;
> +	const union rte_ipsec_sad_key *keys[burst_sz];
> +	void *vals[burst_sz];
> +	uint64_t start, acc = 0;
> +
> +	burst_sz = RTE_MIN(burst_sz, config.nb_tuples);
> +	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_precise();
> +		ret = rte_ipsec_sad_lookup(sad, keys, vals, burst_sz);
> +		acc += rte_rdtsc_precise() - start;
> +		if (ret < 0)
> +			rte_exit(-EINVAL, "Lookup failed\n");
> +		if (config.verbose) {
> +			for (j = 0; j < burst_sz; j++)
> +				print_result(keys[j], vals[j]);
> +		}
> +	}
> +	printf("Average lookup cycles %.2Lf, lookups/sec: %.2Lf\n",
> +		(long double)acc / config.nb_tuples,
> +		(long double)config.nb_tuples * rte_get_tsc_hz() / acc);
> +}
> +
> +static void
> +add_rules(struct rte_ipsec_sad *sad, uint32_t fract)
> +{
> +	int32_t ret;
> +	uint32_t i, j, f, fn, n;
> +	uint64_t start, tm[fract + 1];
> +	uint32_t nm[fract + 1];
> +
> +	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
> +
> +	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
> +
> +		fn = n + f;
> +		fn = fn > config.nb_rules ? config.nb_rules : fn;
> +
> +		start = rte_rdtsc_precise();
> +		for (i = n; i != fn; i++) {
> +			ret = rte_ipsec_sad_add(sad,
> +				&rules_tbl[i].tuple,
> +				rules_tbl[i].rule_type, &rules_tbl[i]);
> +			if (ret != 0)
> +				rte_exit(ret, "%s failed @ %u-th rule\n",
> +					__func__, i);
> +		}
> +		tm[j] = rte_rdtsc_precise() - start;
> +		nm[j] = fn - n;
> +	}
> +
> +	for (i = 0; i != j; i++)
> +		printf("ADD %u rules, %.2Lf cycles/rule, %.2Lf ADD/sec\n",
> +			nm[i], (long double)tm[i] / nm[i],
> +			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
> +}
> +
> +static void
> +del_rules(struct rte_ipsec_sad *sad, uint32_t fract)
> +{
> +	int32_t ret;
> +	uint32_t i, j, f, fn, n;
> +	uint64_t start, tm[fract + 1];
> +	uint32_t nm[fract + 1];
> +
> +	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
> +
> +	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
> +
> +		fn = n + f;
> +		fn = fn > config.nb_rules ? config.nb_rules : fn;
> +
> +		start = rte_rdtsc_precise();
> +		for (i = n; i != fn; i++) {
> +			ret = rte_ipsec_sad_del(sad,
> +				&rules_tbl[i].tuple,
> +				rules_tbl[i].rule_type);
> +			if (ret != 0 && ret != -ENOENT)
> +				rte_exit(ret, "%s failed @ %u-th rule\n",
> +					__func__, i);
> +		}
> +		tm[j] = rte_rdtsc_precise() - start;
> +		nm[j] = fn - n;
> +	}
> +
> +	for (i = 0; i != j; i++)
> +		printf("DEL %u rules, %.2Lf cycles/rule, %.2Lf DEL/sec\n",
> +			nm[i], (long double)tm[i] / nm[i],
> +			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
> +}
> +
> +int
> +main(int argc, char **argv)
> +{
> +	int ret;
> +	struct rte_ipsec_sad *sad;
> +	struct rte_ipsec_sad_conf conf;
> +
> +	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_32 * 5 / 4;
> +	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
> +	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;
> +	if (config.ipv6)
> +		conf.flags = RTE_IPSEC_SAD_FLAG_IPV6|
> +			RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
> +	else
> +		conf.flags = 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();
> +
> +	add_rules(sad, 10);
> +	lookup(sad, config.burst_sz);
> +	del_rules(sad, 10);
> +
> +	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']
> --

Apart from the nits above:

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> 2.7.4


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

* [dpdk-dev] [PATCH v3 0/5] ipsec: add inbound SAD
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
@ 2019-10-08  9:40     ` " Vladimir Medvedkin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
                         ` (5 more replies)
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
                       ` (4 subsequent siblings)
  5 siblings, 6 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08  9:40 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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
- documentation

v3:
- fixes in rte_ipsec_sad_create() and rte_ipsec_sad_find_existing()
- fix typos
- updated commit messages

v2:
- various bugs fixed
- rte_ipsec_sad_free renamed to rte_ipsec_sad_destroy
- added const qualifier to rte_ipsec_sad_key *key for add/delete
- added more comments into the code
- added ipv6 support into the testsad app
- added <DEL> measurement into the testsad app
- random SPI values are generated without dups
- added support for configurable burst size in testsad app
- added verbose mode into the testsad app

Vladimir Medvedkin (5):
  ipsec: add inbound SAD API
  ipsec: add SAD create/destroy implementation
  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                    | 662 ++++++++++++++++++++++++
 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              | 885 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/Makefile              |   4 +-
 lib/librte_ipsec/ipsec_sad.c           | 520 +++++++++++++++++++
 lib/librte_ipsec/meson.build           |   6 +-
 lib/librte_ipsec/rte_ipsec.h           |   1 +
 lib/librte_ipsec/rte_ipsec_sad.h       | 175 +++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 +
 15 files changed, 2291 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] 60+ messages in thread

* [dpdk-dev] [PATCH v3 1/5] ipsec: add inbound SAD API
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
@ 2019-10-08  9:40     ` Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
                       ` (3 subsequent siblings)
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08  9:40 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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.h           |   1 +
 lib/librte_ipsec/rte_ipsec_sad.h       | 175 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 ++
 6 files changed, 237 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..703be65
--- /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 const 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 const 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 const 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_destroy(__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 void *sa[], __rte_unused uint32_t n)
+{
+	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.h b/lib/librte_ipsec/rte_ipsec.h
index f3b1f93..69a22b8 100644
--- a/lib/librte_ipsec/rte_ipsec.h
+++ b/lib/librte_ipsec/rte_ipsec.h
@@ -149,6 +149,7 @@ rte_ipsec_pkt_process(const struct rte_ipsec_session *ss, struct rte_mbuf *mb[],
 }
 
 #include <rte_ipsec_group.h>
+#include <rte_ipsec_sad.h>
 
 #ifdef __cplusplus
 }
diff --git a/lib/librte_ipsec/rte_ipsec_sad.h b/lib/librte_ipsec/rte_ipsec_sad.h
new file mode 100644
index 0000000..d6bd802
--- /dev/null
+++ b/lib/librte_ipsec/rte_ipsec_sad.h
@@ -0,0 +1,175 @@
+
+/* 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_IPV6			0x1
+/** Flag to support reader writer concurrency */
+#define RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY	0x2
+
+/** 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,
+	const 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,
+	const 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, const struct rte_ipsec_sad_conf *conf);
+
+/**
+ * Find an existing SAD object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the SAD 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);
+
+/**
+ * Destroy SAD object.
+ *
+ * @param sad
+ *   pointer to the SAD object
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_ipsec_sad_destroy(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[],
+	void *sa[], uint32_t n);
+
+#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..3c6c630 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_destroy;
+	rte_ipsec_sad_lookup;
+
 	local: *;
 };
-- 
2.7.4


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

* [dpdk-dev] [PATCH v3 2/5] ipsec: add SAD create/destroy implementation
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-10-08  9:40     ` Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
                       ` (2 subsequent siblings)
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08  9:40 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Replace rte_ipsec_sad_create(), rte_ipsec_sad_destroy() 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 | 230 +++++++++++++++++++++++++++++++++++++++++--
 lib/librte_ipsec/meson.build |   2 +-
 3 files changed, 225 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 703be65..cabad44 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -2,10 +2,53 @@
  * 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"
 
+/*
+ * 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>" */
+#define SAD_FORMAT		SAD_PREFIX "%s"
+
+#define DEFAULT_HASH_FUNC	rte_jhash
+#define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
+
+struct hash_cnt {
+	uint32_t cnt_dip;
+	uint32_t cnt_dip_sip;
+};
+
+struct rte_ipsec_sad {
+	char name[IPSEC_SAD_NAMESIZE];
+	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	/* Array to track number of more specific rules
+	 * (spi_dip or spi_dip_sip). Used only in add/delete
+	 * as a helper struct.
+	 */
+	__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 const union rte_ipsec_sad_key *key,
@@ -23,22 +66,195 @@ 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 const struct rte_ipsec_sad_conf *conf)
+rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
 {
-	return NULL;
+	char hash_name[RTE_HASH_NAMESIZE];
+	char sad_name[IPSEC_SAD_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))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	/** Init SAD*/
+	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		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;
+	}
+	memcpy(sad->name, sad_name, sizeof(sad_name));
+
+	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 */
+	snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
+	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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash_2 for SPI + DIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash_3 for SPI + DIP + SIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_3_%p", name);
+	if (ret < 0 || ret >= (int)sizeof(hash_name)) {
+		rte_errno = ENAMETOOLONG;
+		rte_ipsec_sad_destroy(sad);
+		return NULL;
+	}
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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(sad_name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
+			break;
+	}
+	if (te != NULL) {
+		rte_mcfg_tailq_write_unlock();
+		rte_errno = EEXIST;
+		rte_ipsec_sad_destroy(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_destroy(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;
+	char sad_name[IPSEC_SAD_NAMESIZE];
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_ipsec_sad_list *sad_list;
+	int ret;
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	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(sad_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_destroy(__rte_unused struct rte_ipsec_sad *sad)
+rte_ipsec_sad_destroy(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] 60+ messages in thread

* [dpdk-dev] [PATCH v3 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
                       ` (2 preceding siblings ...)
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
@ 2019-10-08  9:40     ` Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08  9:40 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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 | 278 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 266 insertions(+), 12 deletions(-)

diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
index cabad44..7db4a5b 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -49,20 +49,182 @@ 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, const void *key,
+		int key_type, void *sa)
+{
+	void *tmp_val;
+	int ret, notexist;
+
+	/* Check if the key is present in the table.
+	 * Need for further accaunting in cnt_arr
+	 */
+	ret = rte_hash_lookup(sad->hash[key_type], key);
+	notexist = (ret == -ENOENT);
+
+	/* Add an SA to the corresponding table.*/
+	ret = rte_hash_add_key_data(sad->hash[key_type], key, sa);
+	if (ret != 0)
+		return ret;
+
+	/* Check if there is an entry in SPI only table with the same SPI */
+	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);
+
+	/* Add an entry into SPI only table */
+	ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, tmp_val);
+	if (ret != 0)
+		return ret;
+
+	/* Update a counter for a given SPI */
+	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_dip += notexist;
+	else
+		sad->cnt_arr[ret].cnt_dip_sip += notexist;
+
+	return 0;
+}
+
 int
-rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type, __rte_unused void *sa)
+rte_ipsec_sad_add(struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *key,
+		int key_type, void *sa)
 {
-	return -ENOTSUP;
+	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, const void *key, int key_type)
+{
+	void *tmp_val;
+	int ret;
+	uint32_t *cnt;
+
+	/* Remove an SA from the corresponding table.*/
+	ret = rte_hash_del_key(sad->hash[key_type], key);
+	if (ret < 0)
+		return ret;
+
+	/* Get an index of cnt_arr entry for a given SPI */
+	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_dip :
+			&sad->cnt_arr[ret].cnt_dip_sip;
+	if (--(*cnt) != 0)
+		return 0;
+
+	/* corresponding counter is 0, clear the bit indicating
+	 * the presence of more specific rule for a given SPI.
+	 */
+	tmp_val = CLEAR_BIT(tmp_val, key_type);
+
+	/* if there are no rules left with same SPI,
+	 * remove an entry from SPI_only table
+	 */
+	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 const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type)
+rte_ipsec_sad_del(struct rte_ipsec_sad *sad,
+		const 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) {
+			ret = rte_hash_del_key(sad->hash[key_type],
+				key);
+			ret = ret < 0 ? ret : 0;
+		} 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 *
@@ -257,10 +419,102 @@ rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
 		rte_free(te);
 }
 
+/*
+ * @internal helper function
+ * Lookup a batch of keys in three hash tables.
+ * First lookup key in SPI_ONLY table.
+ * If there is an entry for the corresponding SPI check its value.
+ * Two least significant bits of the value indicate
+ * the presence of more specific rule in other tables.
+ * Perform additional lookup in corresponding hash tables
+ * and update the value if lookup succeeded.
+ */
+static int
+__ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
+{
+	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 found = 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];
+		}
+		/* clear 2 LSB's which indicate the presence
+		 * of more specific rules
+		 */
+		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
+	}
+
+	/* Lookup for more specific rules in SPI_DIP table */
+	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];
+		}
+	}
+	/* Lookup for more specific rules in SPI_DIP_SIP table */
+	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++)
+		found += (sa[i] != NULL);
+
+	return found;
+}
+
 int
-rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *keys[],
-		__rte_unused void *sa[], __rte_unused uint32_t n)
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
 {
-	return -ENOTSUP;
+	uint32_t num, i = 0;
+	int found = 0;
+
+	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
+		return -EINVAL;
+
+	do {
+		num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
+		found += __ipsec_sad_lookup(sad,
+			&keys[i], &sa[i], num);
+		i += num;
+	} while (i != n);
+
+	return found;
 }
-- 
2.7.4


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

* [dpdk-dev] [PATCH v3 4/5] test/ipsec: add ipsec SAD autotests
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
                       ` (3 preceding siblings ...)
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-10-08  9:40     ` Vladimir Medvedkin
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08  9:40 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

add unittests for ipsec SAD library

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 | 885 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 893 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..9a6cbfc
--- /dev/null
+++ b/app/test/test_ipsec_sad.c
@@ -0,0 +1,885 @@
+/* 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_find_existing(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 = 0;
+
+	/* name == NULL */
+	sad = rte_ipsec_sad_create(NULL, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* max_sa for every type = 0 */
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	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_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	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;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Test rte_ipsec_sad_find_existing()
+ * Create SAD and try to find it by it's name
+ */
+int32_t
+test_find_existing(void)
+{
+	const char *name1 = "sad_one";
+	const char *name2 = "sad_two";
+	struct rte_ipsec_sad *one, *two, *tmp;
+	struct rte_ipsec_sad_conf config;
+
+	config.socket_id = SOCKET_ID_ANY;
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = 0;
+	one = rte_ipsec_sad_create(name1, &config);
+	RTE_TEST_ASSERT_NOT_NULL(one, "Failed to create SAD\n");
+	two = rte_ipsec_sad_create(name2, &config);
+	RTE_TEST_ASSERT_NOT_NULL(two, "Failed to create SAD\n");
+
+	/* Find non existing */
+	tmp = rte_ipsec_sad_find_existing("sad_three");
+	RTE_TEST_ASSERT(tmp == NULL,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name1);
+	RTE_TEST_ASSERT(tmp == one,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name2);
+	RTE_TEST_ASSERT(tmp == two,
+		"rte_ipsec_sad_find_existing returns invalid SAD\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.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_destroy(sad);
+	}
+	return TEST_SUCCESS;
+}
+
+static int32_t
+__test_add_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_add_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+
+}
+
+static int32_t
+__test_delete_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_delete_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_invalid(int ipv6, 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, sa, 1);
+	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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, NULL, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_destroy(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(0,
+			(union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_invalid(1,
+			(union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_basic(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	RTE_TEST_ASSERT((status == 0) && (sa[0] == NULL),
+		"Lookup returns an unexpected result\n");
+
+	rte_ipsec_sad_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			(union rte_ipsec_sad_key *)&tuple_v6_1);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_adv(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			key_arr);
+
+	return status;
+}
+
+
+static int32_t
+__test_lookup_order(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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_destroy(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(0, (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(1, (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_find_existing),
+		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] 60+ messages in thread

* [dpdk-dev] [PATCH v3 5/5] app: add test-sad application
  2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
                       ` (4 preceding siblings ...)
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-10-08  9:40     ` Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08  9:40 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Introduce new application to provide user to evaluate and perform
custom functional and performance tests for IPsec SAD implementation.

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      | 662 +++++++++++++++++++++++++++++++++++++++++++++++
 app/test-sad/meson.build |   6 +
 5 files changed, 689 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..753721e
--- /dev/null
+++ b/app/test-sad/main.c
@@ -0,0 +1,662 @@
+/* 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
+#define BURST_SZ	64
+
+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;
+	uint32_t	burst_sz;
+	uint8_t		fract_32;
+	uint8_t		fract_64;
+	uint8_t		fract_96;
+	uint8_t		fract_rnd_tuples;
+	int		ipv6;
+	int		verbose;
+} 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,
+	.burst_sz = BURST_SZ,
+	.fract_32 = 90,
+	.fract_64 = 9,
+	.fract_96 = 1,
+	.fract_rnd_tuples = 0,
+	.ipv6 = 0,
+	.verbose = 0
+};
+
+enum {
+	CB_RULE_SPI,
+	CB_RULE_DIP,
+	CB_RULE_SIP,
+	CB_RULE_LEN,
+	CB_RULE_NUM,
+};
+
+static char line[LINE_MAX];
+struct rule {
+	union rte_ipsec_sad_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"
+		"Lookup burst size %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.burst_sz,
+		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"
+		"[-6 <ipv6 tests>]\n"
+		"[-d <\"/\" separated rules length distribution"
+		"(if -f is not specified)>]\n"
+		"[-r <random tuples fraction to lookup"
+		"(if -t is not specified)>]\n"
+		"[-b <lookup burst size: 1-64 >]\n"
+		"[-v <verbose, print results on lookup>]\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.v4.spi, 0,
+				UINT32_MAX, 0);
+
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_DIP],
+				&tbl[j].tuple.v6.dip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_DIP],
+				&tbl[j].tuple.v4.dip);
+		if (ret != 1)
+			return -EINVAL;
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_SIP],
+				&tbl[j].tuple.v6.sip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_SIP],
+				&tbl[j].tuple.v4.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 uint64_t
+get_rnd_rng(uint64_t l, uint64_t u)
+{
+	if (l == u)
+		return l;
+	else
+		return (rte_rand() % (u - l) + l);
+}
+
+static void
+get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
+{
+	unsigned i, j, rnd;
+	int rule_type;
+	double edge = 0;
+	double step;
+
+	step = (double)UINT32_MAX / nb_rules;
+	for (i = 0; i < nb_rules; i++, edge += step) {
+		rnd = rte_rand() % 100;
+		if (rule_tbl) {
+			tbl[i].tuple.v4.spi = get_rnd_rng((uint64_t)edge,
+						(uint64_t)(edge + step));
+			if (config.ipv6) {
+				for (j = 0; j < 16; j++) {
+					tbl[i].tuple.v6.dip[j] = rte_rand();
+					tbl[i].tuple.v6.sip[j] = rte_rand();
+				}
+			} else {
+				tbl[i].tuple.v4.dip = rte_rand();
+				tbl[i].tuple.v4.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.v4.spi =
+					get_rnd_rng((uint64_t)edge,
+					(uint64_t)(edge + step));
+				if (config.ipv6) {
+					for (j = 0; j < 16; j++) {
+						tbl[i].tuple.v6.dip[j] =
+								rte_rand();
+						tbl[i].tuple.v6.sip[j] =
+								rte_rand();
+					}
+				} else {
+					tbl[i].tuple.v4.dip = rte_rand();
+					tbl[i].tuple.v4.sip = rte_rand();
+				}
+				config.nb_tuples_rnd++;
+			} else {
+				tbl[i].tuple.v4.spi = rules_tbl[i %
+					config.nb_rules].tuple.v4.spi;
+				if (config.ipv6) {
+					int r_idx = i % config.nb_rules;
+					memcpy(tbl[i].tuple.v6.dip,
+						rules_tbl[r_idx].tuple.v6.dip,
+						sizeof(tbl[i].tuple.v6.dip));
+					memcpy(tbl[i].tuple.v6.sip,
+						rules_tbl[r_idx].tuple.v6.sip,
+						sizeof(tbl[i].tuple.v6.sip));
+				} else {
+					tbl[i].tuple.v4.dip = rules_tbl[i %
+						config.nb_rules].tuple.v4.dip;
+					tbl[i].tuple.v4.sip = rules_tbl[i %
+						config.nb_rules].tuple.v4.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:6b:v")) != -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) ||
+					(endptr[0] != 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 'b':
+			errno = 0;
+			config.burst_sz = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.burst_sz == 0) ||
+					(config.burst_sz > 64) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -b\n");
+			}
+			break;
+		case 'l':
+			errno = 0;
+			config.nb_tuples = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.nb_tuples == 0) ||
+					(endptr[0] != 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) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -r\n");
+			}
+			break;
+		case '6':
+			config.ipv6 = 1;
+			break;
+		case 'v':
+			config.verbose = 1;
+			break;
+		default:
+			print_usage();
+			rte_exit(-EINVAL, "Invalid options\n");
+		}
+	}
+}
+
+static void
+print_addr(int af, const uint8_t *addr)
+{
+	int i;
+
+	if (af == AF_INET) {
+		for (i = 0; i < 3; i++)
+			printf("%d.", addr[i]);
+		printf("%d", addr[i]);
+	} else {
+		for (i = 0; i < 7; i++)
+			printf("%04x:", addr[2 * i] << 8 | addr[2 * i + 1]);
+		printf("%04x", addr[2 * i] << 8 | addr[2 * i + 1]);
+	}
+
+}
+
+static void
+print_result(const union rte_ipsec_sad_key *key, void *res)
+{
+	struct rule *rule = res;
+	const struct rte_ipsec_sadv4_key *v4;
+	const struct rte_ipsec_sadv6_key *v6;
+	const char *spi_only = "SPI_ONLY";
+	const char *spi_dip = "SPI_DIP";
+	const char *spi_dip_sip = "SPI_DIP_SIP";
+	const char *rule_type;
+
+	if (res == NULL) {
+		if (config.ipv6) {
+			v6 = &key->v6;
+			printf("TUPLE: < SPI: %u DIP: ", v6->spi);
+			print_addr(AF_INET6, v6->dip);
+			printf(" SIP: ");
+			print_addr(AF_INET6, v6->sip);
+			printf(" > not found\n");
+		} else {
+			v4 = &key->v4;
+			printf("TUPLE: < SPI: %u DIP: ", v4->spi);
+			print_addr(AF_INET, (const uint8_t *)&v4->dip);
+			printf(" SIP: ");
+			print_addr(AF_INET, (const uint8_t *)&v4->sip);
+			printf(" > not found\n");
+		}
+		return;
+	}
+
+	switch (rule->rule_type) {
+	case RTE_IPSEC_SAD_SPI_ONLY:
+		rule_type = spi_only;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP:
+		rule_type = spi_dip;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP_SIP:
+		rule_type = spi_dip_sip;
+		break;
+	default:
+		return;
+	}
+
+	if (config.ipv6) {
+		v6 = &key->v6;
+		printf("TUPLE: < SPI: %u DIP: ", v6->spi);
+		print_addr(AF_INET6, v6->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET6, v6->sip);
+		printf(" >\n");
+		v6 = &rule->tuple.v6;
+		printf("\tpoints to RULE ID %zu < SPI: %u DIP: ",
+			RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule),
+			v6->spi);
+		print_addr(AF_INET6, v6->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET6, v6->sip);
+		printf("/%s >\n", rule_type);
+	} else {
+		v4 = &key->v4;
+		printf("TUPLE: < SPI: %u DIP: ", v4->spi);
+		print_addr(AF_INET, (const uint8_t *)&v4->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET, (const uint8_t *)&v4->sip);
+		printf(" >\n");
+		v4 = &rule->tuple.v4;
+		printf("\tpoints to RULE ID %zu < SPI: %u DIP: ",
+			RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule),
+			v4->spi);
+		print_addr(AF_INET, (const uint8_t *)&v4->dip);
+		printf(" SIP: ");
+		print_addr(AF_INET, (const uint8_t *)&v4->sip);
+		printf("/%s >\n", rule_type);
+	}
+}
+
+static void
+lookup(struct rte_ipsec_sad *sad, uint32_t burst_sz)
+{
+	int ret;
+	unsigned int i, j;
+	const union rte_ipsec_sad_key *keys[burst_sz];
+	void *vals[burst_sz];
+	uint64_t start, acc = 0;
+
+	burst_sz = RTE_MIN(burst_sz, config.nb_tuples);
+	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_precise();
+		ret = rte_ipsec_sad_lookup(sad, keys, vals, burst_sz);
+		acc += rte_rdtsc_precise() - start;
+		if (ret < 0)
+			rte_exit(-EINVAL, "Lookup failed\n");
+		if (config.verbose) {
+			for (j = 0; j < burst_sz; j++)
+				print_result(keys[j], vals[j]);
+		}
+	}
+	printf("Average lookup cycles %.2Lf, lookups/sec: %.2Lf\n",
+		(long double)acc / config.nb_tuples,
+		(long double)config.nb_tuples * rte_get_tsc_hz() / acc);
+}
+
+static void
+add_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_add(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type, &rules_tbl[i]);
+			if (ret != 0)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("ADD %u rules, %.2Lf cycles/rule, %.2Lf ADD/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+static void
+del_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_del(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type);
+			if (ret != 0 && ret != -ENOENT)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("DEL %u rules, %.2Lf cycles/rule, %.2Lf DEL/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+	struct rte_ipsec_sad *sad;
+	struct rte_ipsec_sad_conf conf;
+
+	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_32 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;
+	if (config.ipv6)
+		conf.flags = RTE_IPSEC_SAD_FLAG_IPV6|
+			RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
+	else
+		conf.flags = 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();
+
+	add_rules(sad, 10);
+	lookup(sad, config.burst_sz);
+	del_rules(sad, 10);
+
+	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] 60+ messages in thread

* [dpdk-dev] [PATCH v4 0/5] ipsec: add inbound SAD
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
@ 2019-10-08 16:55       ` " Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
                           ` (5 more replies)
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
                         ` (4 subsequent siblings)
  5 siblings, 6 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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
- documentation

v4:
- fixes in test-sad app
- small fixes in rte_ipsec_sad_create()
- fixes in test_find_existing() from unittests

v3:
- fixes in rte_ipsec_sad_create() and rte_ipsec_sad_find_existing()
- fix typos
- updated commit messages
- added test_find_existing() in unittests

v2:
- various bugs fixed
- rte_ipsec_sad_free renamed to rte_ipsec_sad_destroy
- added const qualifier to rte_ipsec_sad_key *key for add/delete
- added more comments into the code
- added ipv6 support into the testsad app
- added <DEL> measurement into the testsad app
- random SPI values are generated without dups
- added support for configurable burst size in testsad app
- added verbose mode into the testsad app


Vladimir Medvedkin (5):
  ipsec: add inbound SAD API
  ipsec: add SAD create/destroy implementation
  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                    | 644 ++++++++++++++++++++++++
 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              | 887 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/Makefile              |   4 +-
 lib/librte_ipsec/ipsec_sad.c           | 515 +++++++++++++++++++
 lib/librte_ipsec/meson.build           |   6 +-
 lib/librte_ipsec/rte_ipsec_sad.h       | 173 +++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 +
 14 files changed, 2267 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] 60+ messages in thread

* [dpdk-dev] [PATCH v4 1/5] ipsec: add inbound SAD API
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
@ 2019-10-08 16:55       ` Vladimir Medvedkin
  2019-10-09 10:49         ` Ananyev, Konstantin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
                         ` (3 subsequent siblings)
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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       | 173 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 ++
 5 files changed, 234 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..703be65
--- /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 const 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 const 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 const 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_destroy(__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 void *sa[], __rte_unused uint32_t n)
+{
+	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..2cd9b52
--- /dev/null
+++ b/lib/librte_ipsec/rte_ipsec_sad.h
@@ -0,0 +1,173 @@
+
+/* 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.
+ * 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_IPV6			0x1
+/** Flag to support reader writer concurrency */
+#define RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY	0x2
+
+/** 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,
+	const 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,
+	const 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, const struct rte_ipsec_sad_conf *conf);
+
+/**
+ * Find an existing SAD object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the SAD 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);
+
+/**
+ * Destroy SAD object.
+ *
+ * @param sad
+ *   pointer to the SAD object
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_ipsec_sad_destroy(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[],
+	void *sa[], uint32_t n);
+
+#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..3c6c630 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_destroy;
+	rte_ipsec_sad_lookup;
+
 	local: *;
 };
-- 
2.7.4


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

* [dpdk-dev] [PATCH v4 2/5] ipsec: add SAD create/destroy implementation
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-10-08 16:55       ` Vladimir Medvedkin
  2019-10-09 10:56         ` Ananyev, Konstantin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
                         ` (2 subsequent siblings)
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Replace rte_ipsec_sad_create(), rte_ipsec_sad_destroy() 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 | 225 +++++++++++++++++++++++++++++++++++++++++--
 lib/librte_ipsec/meson.build |   2 +-
 3 files changed, 220 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 703be65..1a26cff 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -2,10 +2,53 @@
  * 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"
 
+/*
+ * 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>" */
+#define SAD_FORMAT		SAD_PREFIX "%s"
+
+#define DEFAULT_HASH_FUNC	rte_jhash
+#define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
+
+struct hash_cnt {
+	uint32_t cnt_dip;
+	uint32_t cnt_dip_sip;
+};
+
+struct rte_ipsec_sad {
+	char name[IPSEC_SAD_NAMESIZE];
+	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	/* Array to track number of more specific rules
+	 * (spi_dip or spi_dip_sip). Used only in add/delete
+	 * as a helper struct.
+	 */
+	__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 const union rte_ipsec_sad_key *key,
@@ -23,22 +66,190 @@ 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 const struct rte_ipsec_sad_conf *conf)
+rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
 {
-	return NULL;
+	char hash_name[RTE_HASH_NAMESIZE];
+	char sad_name[IPSEC_SAD_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))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	/** Init SAD*/
+	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		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;
+	}
+	memcpy(sad->name, sad_name, sizeof(sad_name));
+
+	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 */
+	snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
+	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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash[RTE_IPSEC_SAD_SPI_DIP] for SPI + DIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash[[RTE_IPSEC_SAD_SPI_DIP_SIP] for SPI + DIP + SIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_3_%p", name);
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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(sad_name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
+			break;
+	}
+	if (te != NULL) {
+		rte_mcfg_tailq_write_unlock();
+		rte_errno = EEXIST;
+		rte_ipsec_sad_destroy(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_destroy(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;
+	char sad_name[IPSEC_SAD_NAMESIZE];
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_ipsec_sad_list *sad_list;
+	int ret;
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	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(sad_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_destroy(__rte_unused struct rte_ipsec_sad *sad)
+rte_ipsec_sad_destroy(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] 60+ messages in thread

* [dpdk-dev] [PATCH v4 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
                         ` (2 preceding siblings ...)
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
@ 2019-10-08 16:55       ` Vladimir Medvedkin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 lib/librte_ipsec/ipsec_sad.c | 278 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 266 insertions(+), 12 deletions(-)

diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
index 1a26cff..4465a4e 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -49,20 +49,182 @@ 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, const void *key,
+		int key_type, void *sa)
+{
+	void *tmp_val;
+	int ret, notexist;
+
+	/* Check if the key is present in the table.
+	 * Need for further accaunting in cnt_arr
+	 */
+	ret = rte_hash_lookup(sad->hash[key_type], key);
+	notexist = (ret == -ENOENT);
+
+	/* Add an SA to the corresponding table.*/
+	ret = rte_hash_add_key_data(sad->hash[key_type], key, sa);
+	if (ret != 0)
+		return ret;
+
+	/* Check if there is an entry in SPI only table with the same SPI */
+	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);
+
+	/* Add an entry into SPI only table */
+	ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, tmp_val);
+	if (ret != 0)
+		return ret;
+
+	/* Update a counter for a given SPI */
+	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_dip += notexist;
+	else
+		sad->cnt_arr[ret].cnt_dip_sip += notexist;
+
+	return 0;
+}
+
 int
-rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type, __rte_unused void *sa)
+rte_ipsec_sad_add(struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *key,
+		int key_type, void *sa)
 {
-	return -ENOTSUP;
+	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, const void *key, int key_type)
+{
+	void *tmp_val;
+	int ret;
+	uint32_t *cnt;
+
+	/* Remove an SA from the corresponding table.*/
+	ret = rte_hash_del_key(sad->hash[key_type], key);
+	if (ret < 0)
+		return ret;
+
+	/* Get an index of cnt_arr entry for a given SPI */
+	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_dip :
+			&sad->cnt_arr[ret].cnt_dip_sip;
+	if (--(*cnt) != 0)
+		return 0;
+
+	/* corresponding counter is 0, clear the bit indicating
+	 * the presence of more specific rule for a given SPI.
+	 */
+	tmp_val = CLEAR_BIT(tmp_val, key_type);
+
+	/* if there are no rules left with same SPI,
+	 * remove an entry from SPI_only table
+	 */
+	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 const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type)
+rte_ipsec_sad_del(struct rte_ipsec_sad *sad,
+		const 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) {
+			ret = rte_hash_del_key(sad->hash[key_type],
+				key);
+			ret = ret < 0 ? ret : 0;
+		} 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 *
@@ -252,10 +414,102 @@ rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
 		rte_free(te);
 }
 
+/*
+ * @internal helper function
+ * Lookup a batch of keys in three hash tables.
+ * First lookup key in SPI_ONLY table.
+ * If there is an entry for the corresponding SPI check its value.
+ * Two least significant bits of the value indicate
+ * the presence of more specific rule in other tables.
+ * Perform additional lookup in corresponding hash tables
+ * and update the value if lookup succeeded.
+ */
+static int
+__ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
+{
+	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 found = 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];
+		}
+		/* clear 2 LSB's which indicate the presence
+		 * of more specific rules
+		 */
+		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
+	}
+
+	/* Lookup for more specific rules in SPI_DIP table */
+	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];
+		}
+	}
+	/* Lookup for more specific rules in SPI_DIP_SIP table */
+	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++)
+		found += (sa[i] != NULL);
+
+	return found;
+}
+
 int
-rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *keys[],
-		__rte_unused void *sa[], __rte_unused uint32_t n)
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
 {
-	return -ENOTSUP;
+	uint32_t num, i = 0;
+	int found = 0;
+
+	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
+		return -EINVAL;
+
+	do {
+		num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
+		found += __ipsec_sad_lookup(sad,
+			&keys[i], &sa[i], num);
+		i += num;
+	} while (i != n);
+
+	return found;
 }
-- 
2.7.4


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

* [dpdk-dev] [PATCH v4 4/5] test/ipsec: add ipsec SAD autotests
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
                         ` (3 preceding siblings ...)
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-10-08 16:55       ` Vladimir Medvedkin
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

add unittests for ipsec SAD library

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   1 +
 app/test/test_ipsec_sad.c | 887 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 895 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..4911646
--- /dev/null
+++ b/app/test/test_ipsec_sad.c
@@ -0,0 +1,887 @@
+/* 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_find_existing(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 = 0;
+
+	/* name == NULL */
+	sad = rte_ipsec_sad_create(NULL, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* max_sa for every type = 0 */
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	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_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	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;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Test rte_ipsec_sad_find_existing()
+ * Create SAD and try to find it by it's name
+ */
+int32_t
+test_find_existing(void)
+{
+	const char *name1 = "sad_one";
+	const char *name2 = "sad_two";
+	struct rte_ipsec_sad *one, *two, *tmp;
+	struct rte_ipsec_sad_conf config;
+
+	config.socket_id = SOCKET_ID_ANY;
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = 0;
+	one = rte_ipsec_sad_create(name1, &config);
+	RTE_TEST_ASSERT_NOT_NULL(one, "Failed to create SAD\n");
+	two = rte_ipsec_sad_create(name2, &config);
+	RTE_TEST_ASSERT_NOT_NULL(two, "Failed to create SAD\n");
+
+	/* Find non existing */
+	tmp = rte_ipsec_sad_find_existing("sad_three");
+	RTE_TEST_ASSERT(tmp == NULL,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name1);
+	RTE_TEST_ASSERT(tmp == one,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name2);
+	RTE_TEST_ASSERT(tmp == two,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	rte_ipsec_sad_destroy(one);
+	rte_ipsec_sad_destroy(two);
+	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.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_destroy(sad);
+	}
+	return TEST_SUCCESS;
+}
+
+static int32_t
+__test_add_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_add_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+
+}
+
+static int32_t
+__test_delete_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_delete_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_invalid(int ipv6, 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, sa, 1);
+	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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, NULL, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_destroy(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(0,
+			(union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_invalid(1,
+			(union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_basic(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	RTE_TEST_ASSERT((status == 0) && (sa[0] == NULL),
+		"Lookup returns an unexpected result\n");
+
+	rte_ipsec_sad_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			(union rte_ipsec_sad_key *)&tuple_v6_1);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_adv(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			key_arr);
+
+	return status;
+}
+
+
+static int32_t
+__test_lookup_order(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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_destroy(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(0, (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(1, (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_find_existing),
+		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] 60+ messages in thread

* [dpdk-dev] [PATCH v4 5/5] app: add test-sad application
  2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
                         ` (4 preceding siblings ...)
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-10-08 16:55       ` Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-08 16:55 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Introduce new application to provide user to evaluate and perform
custom functional and performance tests for IPsec SAD implementation.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 app/Makefile             |   1 +
 app/meson.build          |   3 +-
 app/test-sad/Makefile    |  18 ++
 app/test-sad/main.c      | 644 +++++++++++++++++++++++++++++++++++++++++++++++
 app/test-sad/meson.build |   6 +
 5 files changed, 671 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..8a45f91
--- /dev/null
+++ b/app/test-sad/main.c
@@ -0,0 +1,644 @@
+/* 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
+#define BURST_SZ	64
+
+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;
+	uint32_t	burst_sz;
+	uint8_t		fract_32;
+	uint8_t		fract_64;
+	uint8_t		fract_96;
+	uint8_t		fract_rnd_tuples;
+	int		ipv6;
+	int		verbose;
+} 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,
+	.burst_sz = BURST_SZ,
+	.fract_32 = 90,
+	.fract_64 = 9,
+	.fract_96 = 1,
+	.fract_rnd_tuples = 0,
+	.ipv6 = 0,
+	.verbose = 0
+};
+
+enum {
+	CB_RULE_SPI,
+	CB_RULE_DIP,
+	CB_RULE_SIP,
+	CB_RULE_LEN,
+	CB_RULE_NUM,
+};
+
+static char line[LINE_MAX];
+struct rule {
+	union rte_ipsec_sad_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"
+		"Lookup burst size %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.burst_sz,
+		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"
+		"[-6 <ipv6 tests>]\n"
+		"[-d <\"/\" separated rules length distribution"
+		"(if -f is not specified)>]\n"
+		"[-r <random tuples fraction to lookup"
+		"(if -t is not specified)>]\n"
+		"[-b <lookup burst size: 1-64 >]\n"
+		"[-v <verbose, print results on lookup>]\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.v4.spi, 0,
+				UINT32_MAX, 0);
+
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_DIP],
+				&tbl[j].tuple.v6.dip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_DIP],
+				&tbl[j].tuple.v4.dip);
+		if (ret != 1)
+			return -EINVAL;
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_SIP],
+				&tbl[j].tuple.v6.sip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_SIP],
+				&tbl[j].tuple.v4.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 uint64_t
+get_rnd_rng(uint64_t l, uint64_t u)
+{
+	if (l == u)
+		return l;
+	else
+		return (rte_rand() % (u - l) + l);
+}
+
+static void
+get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
+{
+	unsigned i, j, rnd;
+	int rule_type;
+	double edge = 0;
+	double step;
+
+	step = (double)UINT32_MAX / nb_rules;
+	for (i = 0; i < nb_rules; i++, edge += step) {
+		rnd = rte_rand() % 100;
+		if (rule_tbl) {
+			tbl[i].tuple.v4.spi = get_rnd_rng((uint64_t)edge,
+						(uint64_t)(edge + step));
+			if (config.ipv6) {
+				for (j = 0; j < 16; j++) {
+					tbl[i].tuple.v6.dip[j] = rte_rand();
+					tbl[i].tuple.v6.sip[j] = rte_rand();
+				}
+			} else {
+				tbl[i].tuple.v4.dip = rte_rand();
+				tbl[i].tuple.v4.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.v4.spi =
+					get_rnd_rng((uint64_t)edge,
+					(uint64_t)(edge + step));
+				if (config.ipv6) {
+					for (j = 0; j < 16; j++) {
+						tbl[i].tuple.v6.dip[j] =
+								rte_rand();
+						tbl[i].tuple.v6.sip[j] =
+								rte_rand();
+					}
+				} else {
+					tbl[i].tuple.v4.dip = rte_rand();
+					tbl[i].tuple.v4.sip = rte_rand();
+				}
+				config.nb_tuples_rnd++;
+			} else {
+				tbl[i].tuple.v4.spi = rules_tbl[i %
+					config.nb_rules].tuple.v4.spi;
+				if (config.ipv6) {
+					int r_idx = i % config.nb_rules;
+					memcpy(tbl[i].tuple.v6.dip,
+						rules_tbl[r_idx].tuple.v6.dip,
+						sizeof(tbl[i].tuple.v6.dip));
+					memcpy(tbl[i].tuple.v6.sip,
+						rules_tbl[r_idx].tuple.v6.sip,
+						sizeof(tbl[i].tuple.v6.sip));
+				} else {
+					tbl[i].tuple.v4.dip = rules_tbl[i %
+						config.nb_rules].tuple.v4.dip;
+					tbl[i].tuple.v4.sip = rules_tbl[i %
+						config.nb_rules].tuple.v4.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:6b:v")) != -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) ||
+					(endptr[0] != 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 'b':
+			errno = 0;
+			config.burst_sz = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.burst_sz == 0) ||
+					(config.burst_sz > 64) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -b\n");
+			}
+			break;
+		case 'l':
+			errno = 0;
+			config.nb_tuples = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.nb_tuples == 0) ||
+					(endptr[0] != 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) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -r\n");
+			}
+			break;
+		case '6':
+			config.ipv6 = 1;
+			break;
+		case 'v':
+			config.verbose = 1;
+			break;
+		default:
+			print_usage();
+			rte_exit(-EINVAL, "Invalid options\n");
+		}
+	}
+}
+
+static void
+print_addr(int af, const void *addr)
+{
+	char str[INET6_ADDRSTRLEN];
+	const char *ret;
+
+	ret = inet_ntop(af, addr, str, sizeof(str));
+	if (ret != NULL)
+		printf("%s", str);
+}
+
+static void
+print_tuple(int af, uint32_t spi, const void *dip, const void *sip)
+{
+
+	printf("<SPI: %u DIP: ", spi);
+	print_addr(af, dip);
+	printf(" SIP: ");
+	print_addr(af, sip);
+	printf(">");
+}
+
+static void
+print_result(const union rte_ipsec_sad_key *key, void *res)
+{
+	struct rule *rule = res;
+	const struct rte_ipsec_sadv4_key *v4;
+	const struct rte_ipsec_sadv6_key *v6;
+	const char *spi_only = "SPI_ONLY";
+	const char *spi_dip = "SPI_DIP";
+	const char *spi_dip_sip = "SPI_DIP_SIP";
+	const char *rule_type;
+	const void *dip, *sip;
+	uint32_t spi;
+	int af;
+
+	af = (config.ipv6) ? AF_INET6 : AF_INET;
+	v4 = &key->v4;
+	v6 = &key->v6;
+	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
+	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
+	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
+
+	if (res == NULL) {
+		printf("TUPLE: ");
+		print_tuple(af, spi, dip, sip);
+		printf(" not found\n");
+		return;
+	}
+
+	switch (rule->rule_type) {
+	case RTE_IPSEC_SAD_SPI_ONLY:
+		rule_type = spi_only;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP:
+		rule_type = spi_dip;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP_SIP:
+		rule_type = spi_dip_sip;
+		break;
+	default:
+		return;
+	}
+
+	print_tuple(af, spi, dip, sip);
+	v4 = &rule->tuple.v4;
+	v6 = &rule->tuple.v6;
+	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
+	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
+	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
+	printf("\n\tpoints to RULE ID %zu ",
+		RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule));
+	print_tuple(af, spi, dip, sip);
+	printf(" %s\n", rule_type);
+}
+
+static void
+lookup(struct rte_ipsec_sad *sad, uint32_t burst_sz)
+{
+	int ret;
+	unsigned int i, j;
+	const union rte_ipsec_sad_key *keys[burst_sz];
+	void *vals[burst_sz];
+	uint64_t start, acc = 0;
+
+	burst_sz = RTE_MIN(burst_sz, config.nb_tuples);
+	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_precise();
+		ret = rte_ipsec_sad_lookup(sad, keys, vals, burst_sz);
+		acc += rte_rdtsc_precise() - start;
+		if (ret < 0)
+			rte_exit(-EINVAL, "Lookup failed\n");
+		if (config.verbose) {
+			for (j = 0; j < burst_sz; j++)
+				print_result(keys[j], vals[j]);
+		}
+	}
+	printf("Average lookup cycles %.2Lf, lookups/sec: %.2Lf\n",
+		(long double)acc / config.nb_tuples,
+		(long double)config.nb_tuples * rte_get_tsc_hz() / acc);
+}
+
+static void
+add_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_add(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type, &rules_tbl[i]);
+			if (ret != 0)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("ADD %u rules, %.2Lf cycles/rule, %.2Lf ADD/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+static void
+del_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_del(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type);
+			if (ret != 0 && ret != -ENOENT)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("DEL %u rules, %.2Lf cycles/rule, %.2Lf DEL/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+	struct rte_ipsec_sad *sad;
+	struct rte_ipsec_sad_conf conf;
+
+	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_32 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;
+	if (config.ipv6)
+		conf.flags = RTE_IPSEC_SAD_FLAG_IPV6|
+			RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
+	else
+		conf.flags = 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();
+
+	add_rules(sad, 10);
+	lookup(sad, config.burst_sz);
+	del_rules(sad, 10);
+
+	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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v4 1/5] ipsec: add inbound SAD API
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-10-09 10:49         ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-09 10:49 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal



> 
> Add inbound security association database (SAD) API
> and stub implementation.
> 
> Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
> ---

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> 2.7.4


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

* Re: [dpdk-dev] [PATCH v4 2/5] ipsec: add SAD create/destroy implementation
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
@ 2019-10-09 10:56         ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-09 10:56 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal


> Replace rte_ipsec_sad_create(), rte_ipsec_sad_destroy() 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 | 225 +++++++++++++++++++++++++++++++++++++++++--
>  lib/librte_ipsec/meson.build |   2 +-
>  3 files changed, 220 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 703be65..1a26cff 100644
> --- a/lib/librte_ipsec/ipsec_sad.c
> +++ b/lib/librte_ipsec/ipsec_sad.c
> @@ -2,10 +2,53 @@
>   * 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"
> 
> +/*
> + * 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>" */
> +#define SAD_FORMAT		SAD_PREFIX "%s"
> +
> +#define DEFAULT_HASH_FUNC	rte_jhash
> +#define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
> +
> +struct hash_cnt {
> +	uint32_t cnt_dip;
> +	uint32_t cnt_dip_sip;
> +};
> +
> +struct rte_ipsec_sad {
> +	char name[IPSEC_SAD_NAMESIZE];
> +	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
> +	/* Array to track number of more specific rules
> +	 * (spi_dip or spi_dip_sip). Used only in add/delete
> +	 * as a helper struct.
> +	 */
> +	__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 const union rte_ipsec_sad_key *key,
> @@ -23,22 +66,190 @@ 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 const struct rte_ipsec_sad_conf *conf)
> +rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
>  {
> -	return NULL;
> +	char hash_name[RTE_HASH_NAMESIZE];
> +	char sad_name[IPSEC_SAD_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))) {
> +		rte_errno = EINVAL;
> +		return NULL;
> +	}
> +
> +	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
> +	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
> +		rte_errno = ENAMETOOLONG;
> +		return NULL;
> +	}
> +
> +	/** Init SAD*/
> +	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
> +		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
> +		RTE_MAX(MIN_HASH_ENTRIES,
> +		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
> +		RTE_MAX(MIN_HASH_ENTRIES,
> +		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;
> +	}
> +	memcpy(sad->name, sad_name, sizeof(sad_name));
> +
> +	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 */
> +	snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
> +	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_destroy(sad);
> +		return NULL;
> +	}
> +
> +	/** Init hash[RTE_IPSEC_SAD_SPI_DIP] for SPI + DIP */
> +	snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
> +	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
> +	else
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
> +	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(sad);
> +		return NULL;
> +	}
> +
> +	/** Init hash[[RTE_IPSEC_SAD_SPI_DIP_SIP] for SPI + DIP + SIP */
> +	snprintf(hash_name, sizeof(hash_name), "sad_3_%p", name);

I think it should be:
snprintf(hash_name, sizeof(hash_name), "sad_3_%p", sad);
Apart from that:
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>


> +	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
> +	else
> +		hash_params.key_len +=
> +			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
> +	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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(sad_name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
> +			break;
> +	}
> +	if (te != NULL) {
> +		rte_mcfg_tailq_write_unlock();
> +		rte_errno = EEXIST;
> +		rte_ipsec_sad_destroy(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_destroy(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;
> +	char sad_name[IPSEC_SAD_NAMESIZE];
> +	struct rte_ipsec_sad *sad = NULL;
> +	struct rte_tailq_entry *te;
> +	struct rte_ipsec_sad_list *sad_list;
> +	int ret;
> +
> +	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
> +	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
> +		rte_errno = ENAMETOOLONG;
> +		return NULL;
> +	}
> +
> +	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(sad_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_destroy(__rte_unused struct rte_ipsec_sad *sad)
> +rte_ipsec_sad_destroy(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] 60+ messages in thread

* [dpdk-dev] [PATCH v5 0/5] ipsec: add inbound SAD
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
@ 2019-10-10 16:49         ` " Vladimir Medvedkin
  2019-10-11 11:34           ` Akhil Goyal
                             ` (7 more replies)
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
                           ` (4 subsequent siblings)
  5 siblings, 8 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-10 16:49 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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
- documentation

v5:
- small fix in rte_ipsec_sad_create()
- add comments in rte_ipsec_sad.h

v4:
- fixes in test-sad app
- small fixes in rte_ipsec_sad_create()
- fixes in test_find_existing() from unittests

v3:
- fixes in rte_ipsec_sad_create() and rte_ipsec_sad_find_existing()
- fix typos
- updated commit messages
- added test_find_existing() in unittests

v2:
- various bugs fixed
- rte_ipsec_sad_free renamed to rte_ipsec_sad_destroy
- added const qualifier to rte_ipsec_sad_key *key for add/delete
- added more comments into the code
- added ipv6 support into the testsad app
- added <DEL> measurement into the testsad app
- random SPI values are generated without dups
- added support for configurable burst size in testsad app
- added verbose mode into the testsad app


Vladimir Medvedkin (5):
  ipsec: add inbound SAD API
  ipsec: add SAD create/destroy implementation
  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                    | 644 ++++++++++++++++++++++++
 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              | 887 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/Makefile              |   4 +-
 lib/librte_ipsec/ipsec_sad.c           | 515 +++++++++++++++++++
 lib/librte_ipsec/meson.build           |   6 +-
 lib/librte_ipsec/rte_ipsec_sad.h       | 176 +++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 +
 14 files changed, 2270 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] 60+ messages in thread

* [dpdk-dev] [PATCH v5 1/5] ipsec: add inbound SAD API
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
@ 2019-10-10 16:49         ` Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
                           ` (3 subsequent siblings)
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-10 16:49 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@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       | 176 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 ++
 5 files changed, 237 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..703be65
--- /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 const 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 const 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 const 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_destroy(__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 void *sa[], __rte_unused uint32_t n)
+{
+	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..06eb2cd
--- /dev/null
+++ b/lib/librte_ipsec/rte_ipsec_sad.h
@@ -0,0 +1,176 @@
+
+/* 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.
+ * 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;
+};
+
+/** Flag to create SAD with ipv6 dip and sip addresses */
+#define RTE_IPSEC_SAD_FLAG_IPV6			0x1
+/** Flag to support reader writer concurrency */
+#define RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY	0x2
+
+/** IPsec SAD configuration structure */
+struct rte_ipsec_sad_conf {
+	/** CPU socket ID where rte_ipsec_sad should be allocated */
+	int		socket_id;
+	/** maximum number of SA for each type of key */
+	uint32_t	max_sa[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	/** RTE_IPSEC_SAD_FLAG_* flags */
+	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,
+	const 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,
+	const 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, const struct rte_ipsec_sad_conf *conf);
+
+/**
+ * Find an existing SAD object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the SAD 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);
+
+/**
+ * Destroy SAD object.
+ *
+ * @param sad
+ *   pointer to the SAD object
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_ipsec_sad_destroy(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[],
+	void *sa[], uint32_t n);
+
+#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..3c6c630 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_destroy;
+	rte_ipsec_sad_lookup;
+
 	local: *;
 };
-- 
2.7.4


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

* [dpdk-dev] [PATCH v5 2/5] ipsec: add SAD create/destroy implementation
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-10-10 16:49         ` Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
                           ` (2 subsequent siblings)
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-10 16:49 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 lib/librte_ipsec/Makefile    |   2 +-
 lib/librte_ipsec/ipsec_sad.c | 225 +++++++++++++++++++++++++++++++++++++++++--
 lib/librte_ipsec/meson.build |   2 +-
 3 files changed, 220 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 703be65..e16c7d9 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -2,10 +2,53 @@
  * 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"
 
+/*
+ * 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>" */
+#define SAD_FORMAT		SAD_PREFIX "%s"
+
+#define DEFAULT_HASH_FUNC	rte_jhash
+#define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
+
+struct hash_cnt {
+	uint32_t cnt_dip;
+	uint32_t cnt_dip_sip;
+};
+
+struct rte_ipsec_sad {
+	char name[IPSEC_SAD_NAMESIZE];
+	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	/* Array to track number of more specific rules
+	 * (spi_dip or spi_dip_sip). Used only in add/delete
+	 * as a helper struct.
+	 */
+	__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 const union rte_ipsec_sad_key *key,
@@ -23,22 +66,190 @@ 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 const struct rte_ipsec_sad_conf *conf)
+rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
 {
-	return NULL;
+	char hash_name[RTE_HASH_NAMESIZE];
+	char sad_name[IPSEC_SAD_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))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	/** Init SAD*/
+	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		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;
+	}
+	memcpy(sad->name, sad_name, sizeof(sad_name));
+
+	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 */
+	snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
+	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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash[RTE_IPSEC_SAD_SPI_DIP] for SPI + DIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash[[RTE_IPSEC_SAD_SPI_DIP_SIP] for SPI + DIP + SIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_3_%p", sad);
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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(sad_name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
+			break;
+	}
+	if (te != NULL) {
+		rte_mcfg_tailq_write_unlock();
+		rte_errno = EEXIST;
+		rte_ipsec_sad_destroy(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_destroy(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;
+	char sad_name[IPSEC_SAD_NAMESIZE];
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_ipsec_sad_list *sad_list;
+	int ret;
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	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(sad_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_destroy(__rte_unused struct rte_ipsec_sad *sad)
+rte_ipsec_sad_destroy(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] 60+ messages in thread

* [dpdk-dev] [PATCH v5 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
                           ` (2 preceding siblings ...)
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
@ 2019-10-10 16:49         ` Vladimir Medvedkin
  2019-10-11 10:42           ` Akhil Goyal
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-10 16:49 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 lib/librte_ipsec/ipsec_sad.c | 278 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 266 insertions(+), 12 deletions(-)

diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
index e16c7d9..db2c44c 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -49,20 +49,182 @@ 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, const void *key,
+		int key_type, void *sa)
+{
+	void *tmp_val;
+	int ret, notexist;
+
+	/* Check if the key is present in the table.
+	 * Need for further accaunting in cnt_arr
+	 */
+	ret = rte_hash_lookup(sad->hash[key_type], key);
+	notexist = (ret == -ENOENT);
+
+	/* Add an SA to the corresponding table.*/
+	ret = rte_hash_add_key_data(sad->hash[key_type], key, sa);
+	if (ret != 0)
+		return ret;
+
+	/* Check if there is an entry in SPI only table with the same SPI */
+	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);
+
+	/* Add an entry into SPI only table */
+	ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, tmp_val);
+	if (ret != 0)
+		return ret;
+
+	/* Update a counter for a given SPI */
+	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_dip += notexist;
+	else
+		sad->cnt_arr[ret].cnt_dip_sip += notexist;
+
+	return 0;
+}
+
 int
-rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type, __rte_unused void *sa)
+rte_ipsec_sad_add(struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *key,
+		int key_type, void *sa)
 {
-	return -ENOTSUP;
+	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, const void *key, int key_type)
+{
+	void *tmp_val;
+	int ret;
+	uint32_t *cnt;
+
+	/* Remove an SA from the corresponding table.*/
+	ret = rte_hash_del_key(sad->hash[key_type], key);
+	if (ret < 0)
+		return ret;
+
+	/* Get an index of cnt_arr entry for a given SPI */
+	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_dip :
+			&sad->cnt_arr[ret].cnt_dip_sip;
+	if (--(*cnt) != 0)
+		return 0;
+
+	/* corresponding counter is 0, clear the bit indicating
+	 * the presence of more specific rule for a given SPI.
+	 */
+	tmp_val = CLEAR_BIT(tmp_val, key_type);
+
+	/* if there are no rules left with same SPI,
+	 * remove an entry from SPI_only table
+	 */
+	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 const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type)
+rte_ipsec_sad_del(struct rte_ipsec_sad *sad,
+		const 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) {
+			ret = rte_hash_del_key(sad->hash[key_type],
+				key);
+			ret = ret < 0 ? ret : 0;
+		} 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 *
@@ -252,10 +414,102 @@ rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
 		rte_free(te);
 }
 
+/*
+ * @internal helper function
+ * Lookup a batch of keys in three hash tables.
+ * First lookup key in SPI_ONLY table.
+ * If there is an entry for the corresponding SPI check its value.
+ * Two least significant bits of the value indicate
+ * the presence of more specific rule in other tables.
+ * Perform additional lookup in corresponding hash tables
+ * and update the value if lookup succeeded.
+ */
+static int
+__ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
+{
+	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 found = 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];
+		}
+		/* clear 2 LSB's which indicate the presence
+		 * of more specific rules
+		 */
+		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
+	}
+
+	/* Lookup for more specific rules in SPI_DIP table */
+	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];
+		}
+	}
+	/* Lookup for more specific rules in SPI_DIP_SIP table */
+	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++)
+		found += (sa[i] != NULL);
+
+	return found;
+}
+
 int
-rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *keys[],
-		__rte_unused void *sa[], __rte_unused uint32_t n)
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
 {
-	return -ENOTSUP;
+	uint32_t num, i = 0;
+	int found = 0;
+
+	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
+		return -EINVAL;
+
+	do {
+		num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
+		found += __ipsec_sad_lookup(sad,
+			&keys[i], &sa[i], num);
+		i += num;
+	} while (i != n);
+
+	return found;
 }
-- 
2.7.4


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

* [dpdk-dev] [PATCH v5 4/5] test/ipsec: add ipsec SAD autotests
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
                           ` (3 preceding siblings ...)
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-10-10 16:49         ` Vladimir Medvedkin
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 5/5] app: add test-sad application Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-10 16:49 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

add unittests for ipsec SAD library

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   1 +
 app/test/test_ipsec_sad.c | 887 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 895 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..4911646
--- /dev/null
+++ b/app/test/test_ipsec_sad.c
@@ -0,0 +1,887 @@
+/* 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_find_existing(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 = 0;
+
+	/* name == NULL */
+	sad = rte_ipsec_sad_create(NULL, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* max_sa for every type = 0 */
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	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_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	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;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Test rte_ipsec_sad_find_existing()
+ * Create SAD and try to find it by it's name
+ */
+int32_t
+test_find_existing(void)
+{
+	const char *name1 = "sad_one";
+	const char *name2 = "sad_two";
+	struct rte_ipsec_sad *one, *two, *tmp;
+	struct rte_ipsec_sad_conf config;
+
+	config.socket_id = SOCKET_ID_ANY;
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = 0;
+	one = rte_ipsec_sad_create(name1, &config);
+	RTE_TEST_ASSERT_NOT_NULL(one, "Failed to create SAD\n");
+	two = rte_ipsec_sad_create(name2, &config);
+	RTE_TEST_ASSERT_NOT_NULL(two, "Failed to create SAD\n");
+
+	/* Find non existing */
+	tmp = rte_ipsec_sad_find_existing("sad_three");
+	RTE_TEST_ASSERT(tmp == NULL,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name1);
+	RTE_TEST_ASSERT(tmp == one,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name2);
+	RTE_TEST_ASSERT(tmp == two,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	rte_ipsec_sad_destroy(one);
+	rte_ipsec_sad_destroy(two);
+	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.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_destroy(sad);
+	}
+	return TEST_SUCCESS;
+}
+
+static int32_t
+__test_add_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_add_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+
+}
+
+static int32_t
+__test_delete_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_delete_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_invalid(int ipv6, 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, sa, 1);
+	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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, NULL, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_destroy(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(0,
+			(union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_invalid(1,
+			(union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_basic(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	RTE_TEST_ASSERT((status == 0) && (sa[0] == NULL),
+		"Lookup returns an unexpected result\n");
+
+	rte_ipsec_sad_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			(union rte_ipsec_sad_key *)&tuple_v6_1);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_adv(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			key_arr);
+
+	return status;
+}
+
+
+static int32_t
+__test_lookup_order(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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_destroy(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(0, (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(1, (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_find_existing),
+		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] 60+ messages in thread

* [dpdk-dev] [PATCH v5 5/5] app: add test-sad application
  2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
                           ` (4 preceding siblings ...)
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-10-10 16:49         ` Vladimir Medvedkin
  5 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-10 16:49 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Introduce new application to provide user to evaluate and perform
custom functional and performance tests for IPsec SAD implementation.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 app/Makefile             |   1 +
 app/meson.build          |   3 +-
 app/test-sad/Makefile    |  18 ++
 app/test-sad/main.c      | 644 +++++++++++++++++++++++++++++++++++++++++++++++
 app/test-sad/meson.build |   6 +
 5 files changed, 671 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..8a45f91
--- /dev/null
+++ b/app/test-sad/main.c
@@ -0,0 +1,644 @@
+/* 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
+#define BURST_SZ	64
+
+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;
+	uint32_t	burst_sz;
+	uint8_t		fract_32;
+	uint8_t		fract_64;
+	uint8_t		fract_96;
+	uint8_t		fract_rnd_tuples;
+	int		ipv6;
+	int		verbose;
+} 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,
+	.burst_sz = BURST_SZ,
+	.fract_32 = 90,
+	.fract_64 = 9,
+	.fract_96 = 1,
+	.fract_rnd_tuples = 0,
+	.ipv6 = 0,
+	.verbose = 0
+};
+
+enum {
+	CB_RULE_SPI,
+	CB_RULE_DIP,
+	CB_RULE_SIP,
+	CB_RULE_LEN,
+	CB_RULE_NUM,
+};
+
+static char line[LINE_MAX];
+struct rule {
+	union rte_ipsec_sad_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"
+		"Lookup burst size %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.burst_sz,
+		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"
+		"[-6 <ipv6 tests>]\n"
+		"[-d <\"/\" separated rules length distribution"
+		"(if -f is not specified)>]\n"
+		"[-r <random tuples fraction to lookup"
+		"(if -t is not specified)>]\n"
+		"[-b <lookup burst size: 1-64 >]\n"
+		"[-v <verbose, print results on lookup>]\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.v4.spi, 0,
+				UINT32_MAX, 0);
+
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_DIP],
+				&tbl[j].tuple.v6.dip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_DIP],
+				&tbl[j].tuple.v4.dip);
+		if (ret != 1)
+			return -EINVAL;
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_SIP],
+				&tbl[j].tuple.v6.sip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_SIP],
+				&tbl[j].tuple.v4.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 uint64_t
+get_rnd_rng(uint64_t l, uint64_t u)
+{
+	if (l == u)
+		return l;
+	else
+		return (rte_rand() % (u - l) + l);
+}
+
+static void
+get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
+{
+	unsigned i, j, rnd;
+	int rule_type;
+	double edge = 0;
+	double step;
+
+	step = (double)UINT32_MAX / nb_rules;
+	for (i = 0; i < nb_rules; i++, edge += step) {
+		rnd = rte_rand() % 100;
+		if (rule_tbl) {
+			tbl[i].tuple.v4.spi = get_rnd_rng((uint64_t)edge,
+						(uint64_t)(edge + step));
+			if (config.ipv6) {
+				for (j = 0; j < 16; j++) {
+					tbl[i].tuple.v6.dip[j] = rte_rand();
+					tbl[i].tuple.v6.sip[j] = rte_rand();
+				}
+			} else {
+				tbl[i].tuple.v4.dip = rte_rand();
+				tbl[i].tuple.v4.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.v4.spi =
+					get_rnd_rng((uint64_t)edge,
+					(uint64_t)(edge + step));
+				if (config.ipv6) {
+					for (j = 0; j < 16; j++) {
+						tbl[i].tuple.v6.dip[j] =
+								rte_rand();
+						tbl[i].tuple.v6.sip[j] =
+								rte_rand();
+					}
+				} else {
+					tbl[i].tuple.v4.dip = rte_rand();
+					tbl[i].tuple.v4.sip = rte_rand();
+				}
+				config.nb_tuples_rnd++;
+			} else {
+				tbl[i].tuple.v4.spi = rules_tbl[i %
+					config.nb_rules].tuple.v4.spi;
+				if (config.ipv6) {
+					int r_idx = i % config.nb_rules;
+					memcpy(tbl[i].tuple.v6.dip,
+						rules_tbl[r_idx].tuple.v6.dip,
+						sizeof(tbl[i].tuple.v6.dip));
+					memcpy(tbl[i].tuple.v6.sip,
+						rules_tbl[r_idx].tuple.v6.sip,
+						sizeof(tbl[i].tuple.v6.sip));
+				} else {
+					tbl[i].tuple.v4.dip = rules_tbl[i %
+						config.nb_rules].tuple.v4.dip;
+					tbl[i].tuple.v4.sip = rules_tbl[i %
+						config.nb_rules].tuple.v4.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:6b:v")) != -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) ||
+					(endptr[0] != 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 'b':
+			errno = 0;
+			config.burst_sz = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.burst_sz == 0) ||
+					(config.burst_sz > 64) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -b\n");
+			}
+			break;
+		case 'l':
+			errno = 0;
+			config.nb_tuples = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.nb_tuples == 0) ||
+					(endptr[0] != 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) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -r\n");
+			}
+			break;
+		case '6':
+			config.ipv6 = 1;
+			break;
+		case 'v':
+			config.verbose = 1;
+			break;
+		default:
+			print_usage();
+			rte_exit(-EINVAL, "Invalid options\n");
+		}
+	}
+}
+
+static void
+print_addr(int af, const void *addr)
+{
+	char str[INET6_ADDRSTRLEN];
+	const char *ret;
+
+	ret = inet_ntop(af, addr, str, sizeof(str));
+	if (ret != NULL)
+		printf("%s", str);
+}
+
+static void
+print_tuple(int af, uint32_t spi, const void *dip, const void *sip)
+{
+
+	printf("<SPI: %u DIP: ", spi);
+	print_addr(af, dip);
+	printf(" SIP: ");
+	print_addr(af, sip);
+	printf(">");
+}
+
+static void
+print_result(const union rte_ipsec_sad_key *key, void *res)
+{
+	struct rule *rule = res;
+	const struct rte_ipsec_sadv4_key *v4;
+	const struct rte_ipsec_sadv6_key *v6;
+	const char *spi_only = "SPI_ONLY";
+	const char *spi_dip = "SPI_DIP";
+	const char *spi_dip_sip = "SPI_DIP_SIP";
+	const char *rule_type;
+	const void *dip, *sip;
+	uint32_t spi;
+	int af;
+
+	af = (config.ipv6) ? AF_INET6 : AF_INET;
+	v4 = &key->v4;
+	v6 = &key->v6;
+	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
+	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
+	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
+
+	if (res == NULL) {
+		printf("TUPLE: ");
+		print_tuple(af, spi, dip, sip);
+		printf(" not found\n");
+		return;
+	}
+
+	switch (rule->rule_type) {
+	case RTE_IPSEC_SAD_SPI_ONLY:
+		rule_type = spi_only;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP:
+		rule_type = spi_dip;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP_SIP:
+		rule_type = spi_dip_sip;
+		break;
+	default:
+		return;
+	}
+
+	print_tuple(af, spi, dip, sip);
+	v4 = &rule->tuple.v4;
+	v6 = &rule->tuple.v6;
+	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
+	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
+	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
+	printf("\n\tpoints to RULE ID %zu ",
+		RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule));
+	print_tuple(af, spi, dip, sip);
+	printf(" %s\n", rule_type);
+}
+
+static void
+lookup(struct rte_ipsec_sad *sad, uint32_t burst_sz)
+{
+	int ret;
+	unsigned int i, j;
+	const union rte_ipsec_sad_key *keys[burst_sz];
+	void *vals[burst_sz];
+	uint64_t start, acc = 0;
+
+	burst_sz = RTE_MIN(burst_sz, config.nb_tuples);
+	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_precise();
+		ret = rte_ipsec_sad_lookup(sad, keys, vals, burst_sz);
+		acc += rte_rdtsc_precise() - start;
+		if (ret < 0)
+			rte_exit(-EINVAL, "Lookup failed\n");
+		if (config.verbose) {
+			for (j = 0; j < burst_sz; j++)
+				print_result(keys[j], vals[j]);
+		}
+	}
+	printf("Average lookup cycles %.2Lf, lookups/sec: %.2Lf\n",
+		(long double)acc / config.nb_tuples,
+		(long double)config.nb_tuples * rte_get_tsc_hz() / acc);
+}
+
+static void
+add_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_add(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type, &rules_tbl[i]);
+			if (ret != 0)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("ADD %u rules, %.2Lf cycles/rule, %.2Lf ADD/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+static void
+del_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_del(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type);
+			if (ret != 0 && ret != -ENOENT)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("DEL %u rules, %.2Lf cycles/rule, %.2Lf DEL/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+	struct rte_ipsec_sad *sad;
+	struct rte_ipsec_sad_conf conf;
+
+	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_32 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;
+	if (config.ipv6)
+		conf.flags = RTE_IPSEC_SAD_FLAG_IPV6|
+			RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
+	else
+		conf.flags = 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();
+
+	add_rules(sad, 10);
+	lookup(sad, config.burst_sz);
+	del_rules(sad, 10);
+
+	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] 60+ messages in thread

* Re: [dpdk-dev] [PATCH v5 3/5] ipsec: add SAD add/delete/lookup implementation
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-10-11 10:42           ` Akhil Goyal
  0 siblings, 0 replies; 60+ messages in thread
From: Akhil Goyal @ 2019-10-11 10:42 UTC (permalink / raw)
  To: Vladimir Medvedkin, dev; +Cc: konstantin.ananyev, bernard.iremonger



>  int
> -rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
> -		__rte_unused const union rte_ipsec_sad_key *keys[],
> -		__rte_unused void *sa[], __rte_unused uint32_t n)
> +rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
> +		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
>  {
> -	return -ENOTSUP;
> +	uint32_t num, i = 0;
> +	int found = 0;
> +
> +	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
> +		return -EINVAL;
> +
> +	do {
> +		num = RTE_MIN(n - i,
> (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
> +		found += __ipsec_sad_lookup(sad,
> +			&keys[i], &sa[i], num);
> +		i += num;
> +	} while (i != n);
> +
> +	return found;
>  }
rte_ipsec_sad_lookup should return 0 or a negative value as per the comments in the declaration.
But here it is returning more than 0. Something is not correct here.


> --
> 2.7.4


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

* Re: [dpdk-dev] [PATCH v5 0/5] ipsec: add inbound SAD
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
@ 2019-10-11 11:34           ` Akhil Goyal
  2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 0/6] " Vladimir Medvedkin
                             ` (6 subsequent siblings)
  7 siblings, 0 replies; 60+ messages in thread
From: Akhil Goyal @ 2019-10-11 11:34 UTC (permalink / raw)
  To: Vladimir Medvedkin, dev, Mariusz Drost
  Cc: konstantin.ananyev, bernard.iremonger



> -----Original Message-----
> From: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
> Sent: Thursday, October 10, 2019 10:19 PM
> To: dev@dpdk.org
> Cc: konstantin.ananyev@intel.com; bernard.iremonger@intel.com; Akhil Goyal
> <akhil.goyal@nxp.com>
> Subject: [PATCH v5 0/5] ipsec: add inbound SAD
> 
> 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
> - documentation

I believe documentation should be part of this patchset combined with the patches
which introduce that API.
Apart from that, 0ne comment is there on the lookup API return value.

Integration with IPSec Application can be done separately. No issues with that.
With this series, I think we don’t need the changes done in 
http://patches.dpdk.org/user/todo/dpdk/?series=6499
If it is agreed, I will move it as Rejected.

With Above changes - Series 
Acked-by: Akhil Goyal <akhil.goyal@nxp.com>

Please send these changes so that I can merge this series.



> 
> v5:
> - small fix in rte_ipsec_sad_create()
> - add comments in rte_ipsec_sad.h
> 
> v4:
> - fixes in test-sad app
> - small fixes in rte_ipsec_sad_create()
> - fixes in test_find_existing() from unittests
> 
> v3:
> - fixes in rte_ipsec_sad_create() and rte_ipsec_sad_find_existing()
> - fix typos
> - updated commit messages
> - added test_find_existing() in unittests
> 
> v2:
> - various bugs fixed
> - rte_ipsec_sad_free renamed to rte_ipsec_sad_destroy
> - added const qualifier to rte_ipsec_sad_key *key for add/delete
> - added more comments into the code
> - added ipv6 support into the testsad app
> - added <DEL> measurement into the testsad app
> - random SPI values are generated without dups
> - added support for configurable burst size in testsad app
> - added verbose mode into the testsad app
> 
> 
> Vladimir Medvedkin (5):
>   ipsec: add inbound SAD API
>   ipsec: add SAD create/destroy implementation
>   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                    | 644 ++++++++++++++++++++++++
>  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              | 887
> +++++++++++++++++++++++++++++++++
>  lib/librte_ipsec/Makefile              |   4 +-
>  lib/librte_ipsec/ipsec_sad.c           | 515 +++++++++++++++++++
>  lib/librte_ipsec/meson.build           |   6 +-
>  lib/librte_ipsec/rte_ipsec_sad.h       | 176 +++++++
>  lib/librte_ipsec/rte_ipsec_version.map |   7 +
>  14 files changed, 2270 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] 60+ messages in thread

* [dpdk-dev] [PATCH v6 0/6] ipsec: add inbound SAD
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
  2019-10-11 11:34           ` Akhil Goyal
@ 2019-10-17 15:47           ` " Vladimir Medvedkin
  2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 1/6] ipsec: add inbound SAD API Vladimir Medvedkin
                             ` (5 subsequent siblings)
  7 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-17 15:47 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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.

v6:
- fix rte_ipsec_sad_lookup() comments regarding return value
- added parallel lookup feature to test-sad app
- added read/write concurrency support flag to test-sad app
- added programmer's guide
- updated release notes

v5:
- small fix in rte_ipsec_sad_create()
- add comments in rte_ipsec_sad.h

v4:
- fixes in test-sad app
- small fixes in rte_ipsec_sad_create()
- fixes in test_find_existing() from unittests

v3:
- fixes in rte_ipsec_sad_create() and rte_ipsec_sad_find_existing()
- fix typos
- updated commit messages
- added test_find_existing() in unittests

v2:
- various bugs fixed
- rte_ipsec_sad_free renamed to rte_ipsec_sad_destroy
- added const qualifier to rte_ipsec_sad_key *key for add/delete
- added more comments into the code
- added ipv6 support into the testsad app
- added <DEL> measurement into the testsad app
- random SPI values are generated without dups
- added support for configurable burst size in testsad app
- added verbose mode into the testsad app

Vladimir Medvedkin (6):
  ipsec: add inbound SAD API
  ipsec: add SAD create/destroy implementation
  ipsec: add SAD add/delete/lookup implementation
  test/ipsec: add ipsec SAD autotests
  app: add test-sad application
  doc/ipsec: update ipsec programmer's guide

 app/Makefile                           |   1 +
 app/meson.build                        |   3 +-
 app/test-sad/Makefile                  |  18 +
 app/test-sad/main.c                    | 668 +++++++++++++++++++++++++
 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              | 887 +++++++++++++++++++++++++++++++++
 doc/guides/prog_guide/ipsec_lib.rst    | 152 ++++++
 doc/guides/rel_notes/release_19_11.rst |   3 +
 lib/librte_ipsec/Makefile              |   4 +-
 lib/librte_ipsec/ipsec_sad.c           | 515 +++++++++++++++++++
 lib/librte_ipsec/meson.build           |   6 +-
 lib/librte_ipsec/rte_ipsec_sad.h       | 176 +++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 +
 16 files changed, 2449 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] 60+ messages in thread

* [dpdk-dev] [PATCH v6 1/6] ipsec: add inbound SAD API
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
  2019-10-11 11:34           ` Akhil Goyal
  2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 0/6] " Vladimir Medvedkin
@ 2019-10-17 15:47           ` Vladimir Medvedkin
  2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 2/6] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
                             ` (4 subsequent siblings)
  7 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-17 15:47 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Akhil Goyal <akhil.goyal@nxp.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@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       | 176 +++++++++++++++++++++++++++++++++
 lib/librte_ipsec/rte_ipsec_version.map |   7 ++
 5 files changed, 237 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..703be65
--- /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 const 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 const 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 const 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_destroy(__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 void *sa[], __rte_unused uint32_t n)
+{
+	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..8386f73
--- /dev/null
+++ b/lib/librte_ipsec/rte_ipsec_sad.h
@@ -0,0 +1,176 @@
+
+/* 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.
+ * 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;
+};
+
+/** Flag to create SAD with ipv6 dip and sip addresses */
+#define RTE_IPSEC_SAD_FLAG_IPV6			0x1
+/** Flag to support reader writer concurrency */
+#define RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY	0x2
+
+/** IPsec SAD configuration structure */
+struct rte_ipsec_sad_conf {
+	/** CPU socket ID where rte_ipsec_sad should be allocated */
+	int		socket_id;
+	/** maximum number of SA for each type of key */
+	uint32_t	max_sa[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	/** RTE_IPSEC_SAD_FLAG_* flags */
+	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,
+	const 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,
+	const 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, const struct rte_ipsec_sad_conf *conf);
+
+/**
+ * Find an existing SAD object and return a pointer to it.
+ *
+ * @param name
+ *  Name of the SAD 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);
+
+/**
+ * Destroy SAD object.
+ *
+ * @param sad
+ *   pointer to the SAD object
+ * @return
+ *   None
+ */
+__rte_experimental
+void
+rte_ipsec_sad_destroy(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 number of successful lookups.
+ */
+__rte_experimental
+int
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+	const union rte_ipsec_sad_key *keys[],
+	void *sa[], uint32_t n);
+
+#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..3c6c630 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_destroy;
+	rte_ipsec_sad_lookup;
+
 	local: *;
 };
-- 
2.7.4


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

* [dpdk-dev] [PATCH v6 2/6] ipsec: add SAD create/destroy implementation
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
                             ` (2 preceding siblings ...)
  2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 1/6] ipsec: add inbound SAD API Vladimir Medvedkin
@ 2019-10-17 15:47           ` Vladimir Medvedkin
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 3/6] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
                             ` (3 subsequent siblings)
  7 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-17 15:47 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Akhil Goyal <akhil.goyal@nxp.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 lib/librte_ipsec/Makefile    |   2 +-
 lib/librte_ipsec/ipsec_sad.c | 225 +++++++++++++++++++++++++++++++++++++++++--
 lib/librte_ipsec/meson.build |   2 +-
 3 files changed, 220 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 703be65..e16c7d9 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -2,10 +2,53 @@
  * 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"
 
+/*
+ * 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>" */
+#define SAD_FORMAT		SAD_PREFIX "%s"
+
+#define DEFAULT_HASH_FUNC	rte_jhash
+#define MIN_HASH_ENTRIES	8U /* From rte_cuckoo_hash.h */
+
+struct hash_cnt {
+	uint32_t cnt_dip;
+	uint32_t cnt_dip_sip;
+};
+
+struct rte_ipsec_sad {
+	char name[IPSEC_SAD_NAMESIZE];
+	struct rte_hash	*hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
+	/* Array to track number of more specific rules
+	 * (spi_dip or spi_dip_sip). Used only in add/delete
+	 * as a helper struct.
+	 */
+	__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 const union rte_ipsec_sad_key *key,
@@ -23,22 +66,190 @@ 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 const struct rte_ipsec_sad_conf *conf)
+rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
 {
-	return NULL;
+	char hash_name[RTE_HASH_NAMESIZE];
+	char sad_name[IPSEC_SAD_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))) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	/** Init SAD*/
+	sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
+		RTE_MAX(MIN_HASH_ENTRIES,
+		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;
+	}
+	memcpy(sad->name, sad_name, sizeof(sad_name));
+
+	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 */
+	snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
+	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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash[RTE_IPSEC_SAD_SPI_DIP] for SPI + DIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(sad);
+		return NULL;
+	}
+
+	/** Init hash[[RTE_IPSEC_SAD_SPI_DIP_SIP] for SPI + DIP + SIP */
+	snprintf(hash_name, sizeof(hash_name), "sad_3_%p", sad);
+	if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
+	else
+		hash_params.key_len +=
+			sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
+	hash_params.entries = RTE_MAX(MIN_HASH_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_destroy(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(sad_name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
+			break;
+	}
+	if (te != NULL) {
+		rte_mcfg_tailq_write_unlock();
+		rte_errno = EEXIST;
+		rte_ipsec_sad_destroy(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_destroy(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;
+	char sad_name[IPSEC_SAD_NAMESIZE];
+	struct rte_ipsec_sad *sad = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_ipsec_sad_list *sad_list;
+	int ret;
+
+	ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
+	if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
+		rte_errno = ENAMETOOLONG;
+		return NULL;
+	}
+
+	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(sad_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_destroy(__rte_unused struct rte_ipsec_sad *sad)
+rte_ipsec_sad_destroy(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] 60+ messages in thread

* [dpdk-dev] [PATCH v6 3/6] ipsec: add SAD add/delete/lookup implementation
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
                             ` (3 preceding siblings ...)
  2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 2/6] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
@ 2019-10-17 15:48           ` Vladimir Medvedkin
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 4/6] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
                             ` (2 subsequent siblings)
  7 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-17 15:48 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

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>
Acked-by: Akhil Goyal <akhil.goyal@nxp.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 lib/librte_ipsec/ipsec_sad.c | 278 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 266 insertions(+), 12 deletions(-)

diff --git a/lib/librte_ipsec/ipsec_sad.c b/lib/librte_ipsec/ipsec_sad.c
index e16c7d9..db2c44c 100644
--- a/lib/librte_ipsec/ipsec_sad.c
+++ b/lib/librte_ipsec/ipsec_sad.c
@@ -49,20 +49,182 @@ 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, const void *key,
+		int key_type, void *sa)
+{
+	void *tmp_val;
+	int ret, notexist;
+
+	/* Check if the key is present in the table.
+	 * Need for further accaunting in cnt_arr
+	 */
+	ret = rte_hash_lookup(sad->hash[key_type], key);
+	notexist = (ret == -ENOENT);
+
+	/* Add an SA to the corresponding table.*/
+	ret = rte_hash_add_key_data(sad->hash[key_type], key, sa);
+	if (ret != 0)
+		return ret;
+
+	/* Check if there is an entry in SPI only table with the same SPI */
+	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);
+
+	/* Add an entry into SPI only table */
+	ret = rte_hash_add_key_data(sad->hash[RTE_IPSEC_SAD_SPI_ONLY],
+		key, tmp_val);
+	if (ret != 0)
+		return ret;
+
+	/* Update a counter for a given SPI */
+	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_dip += notexist;
+	else
+		sad->cnt_arr[ret].cnt_dip_sip += notexist;
+
+	return 0;
+}
+
 int
-rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type, __rte_unused void *sa)
+rte_ipsec_sad_add(struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *key,
+		int key_type, void *sa)
 {
-	return -ENOTSUP;
+	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, const void *key, int key_type)
+{
+	void *tmp_val;
+	int ret;
+	uint32_t *cnt;
+
+	/* Remove an SA from the corresponding table.*/
+	ret = rte_hash_del_key(sad->hash[key_type], key);
+	if (ret < 0)
+		return ret;
+
+	/* Get an index of cnt_arr entry for a given SPI */
+	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_dip :
+			&sad->cnt_arr[ret].cnt_dip_sip;
+	if (--(*cnt) != 0)
+		return 0;
+
+	/* corresponding counter is 0, clear the bit indicating
+	 * the presence of more specific rule for a given SPI.
+	 */
+	tmp_val = CLEAR_BIT(tmp_val, key_type);
+
+	/* if there are no rules left with same SPI,
+	 * remove an entry from SPI_only table
+	 */
+	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 const union rte_ipsec_sad_key *key,
-		__rte_unused int key_type)
+rte_ipsec_sad_del(struct rte_ipsec_sad *sad,
+		const 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) {
+			ret = rte_hash_del_key(sad->hash[key_type],
+				key);
+			ret = ret < 0 ? ret : 0;
+		} 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 *
@@ -252,10 +414,102 @@ rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
 		rte_free(te);
 }
 
+/*
+ * @internal helper function
+ * Lookup a batch of keys in three hash tables.
+ * First lookup key in SPI_ONLY table.
+ * If there is an entry for the corresponding SPI check its value.
+ * Two least significant bits of the value indicate
+ * the presence of more specific rule in other tables.
+ * Perform additional lookup in corresponding hash tables
+ * and update the value if lookup succeeded.
+ */
+static int
+__ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
+{
+	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 found = 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];
+		}
+		/* clear 2 LSB's which indicate the presence
+		 * of more specific rules
+		 */
+		sa[i] = CLEAR_BIT(sa[i], RTE_IPSEC_SAD_KEY_TYPE_MASK);
+	}
+
+	/* Lookup for more specific rules in SPI_DIP table */
+	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];
+		}
+	}
+	/* Lookup for more specific rules in SPI_DIP_SIP table */
+	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++)
+		found += (sa[i] != NULL);
+
+	return found;
+}
+
 int
-rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
-		__rte_unused const union rte_ipsec_sad_key *keys[],
-		__rte_unused void *sa[], __rte_unused uint32_t n)
+rte_ipsec_sad_lookup(const struct rte_ipsec_sad *sad,
+		const union rte_ipsec_sad_key *keys[], void *sa[], uint32_t n)
 {
-	return -ENOTSUP;
+	uint32_t num, i = 0;
+	int found = 0;
+
+	if (unlikely((sad == NULL) || (keys == NULL) || (sa == NULL)))
+		return -EINVAL;
+
+	do {
+		num = RTE_MIN(n - i, (uint32_t)RTE_HASH_LOOKUP_BULK_MAX);
+		found += __ipsec_sad_lookup(sad,
+			&keys[i], &sa[i], num);
+		i += num;
+	} while (i != n);
+
+	return found;
 }
-- 
2.7.4


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

* [dpdk-dev] [PATCH v6 4/6] test/ipsec: add ipsec SAD autotests
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
                             ` (4 preceding siblings ...)
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 3/6] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
@ 2019-10-17 15:48           ` Vladimir Medvedkin
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 5/6] app: add test-sad application Vladimir Medvedkin
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 6/6] doc/ipsec: update ipsec programmer's guide Vladimir Medvedkin
  7 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-17 15:48 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

add unittests for ipsec SAD library

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Akhil Goyal <akhil.goyal@nxp.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 app/test/Makefile         |   1 +
 app/test/autotest_data.py |   6 +
 app/test/meson.build      |   1 +
 app/test/test_ipsec_sad.c | 887 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 895 insertions(+)
 create mode 100644 app/test/test_ipsec_sad.c

diff --git a/app/test/Makefile b/app/test/Makefile
index df7f77f..e2832fb 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -224,6 +224,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 2c23c63..94f6b49 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -58,6 +58,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..4911646
--- /dev/null
+++ b/app/test/test_ipsec_sad.c
@@ -0,0 +1,887 @@
+/* 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_find_existing(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 = 0;
+
+	/* name == NULL */
+	sad = rte_ipsec_sad_create(NULL, &config);
+	RTE_TEST_ASSERT(sad == NULL,
+		"Call succeeded with invalid parameters\n");
+
+	/* max_sa for every type = 0 */
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	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_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = MAX_SA;
+	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;
+
+	return TEST_SUCCESS;
+}
+
+/*
+ * Test rte_ipsec_sad_find_existing()
+ * Create SAD and try to find it by it's name
+ */
+int32_t
+test_find_existing(void)
+{
+	const char *name1 = "sad_one";
+	const char *name2 = "sad_two";
+	struct rte_ipsec_sad *one, *two, *tmp;
+	struct rte_ipsec_sad_conf config;
+
+	config.socket_id = SOCKET_ID_ANY;
+	config.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = MAX_SA;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP] = 0;
+	config.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = 0;
+	one = rte_ipsec_sad_create(name1, &config);
+	RTE_TEST_ASSERT_NOT_NULL(one, "Failed to create SAD\n");
+	two = rte_ipsec_sad_create(name2, &config);
+	RTE_TEST_ASSERT_NOT_NULL(two, "Failed to create SAD\n");
+
+	/* Find non existing */
+	tmp = rte_ipsec_sad_find_existing("sad_three");
+	RTE_TEST_ASSERT(tmp == NULL,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name1);
+	RTE_TEST_ASSERT(tmp == one,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	tmp = rte_ipsec_sad_find_existing(name2);
+	RTE_TEST_ASSERT(tmp == two,
+		"rte_ipsec_sad_find_existing returns invalid SAD\n");
+
+	rte_ipsec_sad_destroy(one);
+	rte_ipsec_sad_destroy(two);
+	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.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_destroy(sad);
+	}
+	return TEST_SUCCESS;
+}
+
+static int32_t
+__test_add_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_add_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+
+}
+
+static int32_t
+__test_delete_invalid(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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_destroy(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(0, (union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_delete_invalid(1, (union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_invalid(int ipv6, 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, sa, 1);
+	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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	status = rte_ipsec_sad_lookup(sad, key_arr, NULL, 1);
+	RTE_TEST_ASSERT(status < 0,
+		"Call succeeded with invalid parameters\n");
+
+	rte_ipsec_sad_destroy(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(0,
+			(union rte_ipsec_sad_key *)&tuple_v4);
+	if (status != TEST_SUCCESS)
+		return status;
+
+	status = __test_lookup_invalid(1,
+			(union rte_ipsec_sad_key *)&tuple_v6);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_basic(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	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, sa, 1);
+	RTE_TEST_ASSERT((status == 0) && (sa[0] == NULL),
+		"Lookup returns an unexpected result\n");
+
+	rte_ipsec_sad_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			(union rte_ipsec_sad_key *)&tuple_v6_1);
+
+	return status;
+}
+
+static int32_t
+__test_lookup_adv(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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, sa, 4);
+	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_destroy(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(0, (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(1, (union rte_ipsec_sad_key *)&tuple_v6,
+			key_arr);
+
+	return status;
+}
+
+
+static int32_t
+__test_lookup_order(int ipv6, 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;
+	config.flags = 0;
+	if (ipv6)
+		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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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, sa, 3);
+	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_destroy(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(0, (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(1, (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_find_existing),
+		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] 60+ messages in thread

* [dpdk-dev] [PATCH v6 5/6] app: add test-sad application
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
                             ` (5 preceding siblings ...)
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 4/6] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
@ 2019-10-17 15:48           ` Vladimir Medvedkin
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 6/6] doc/ipsec: update ipsec programmer's guide Vladimir Medvedkin
  7 siblings, 0 replies; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-17 15:48 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Introduce new application to provide user to evaluate and perform
custom functional and performance tests for IPsec SAD implementation.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Akhil Goyal <akhil.goyal@nxp.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 app/Makefile             |   1 +
 app/meson.build          |   3 +-
 app/test-sad/Makefile    |  18 ++
 app/test-sad/main.c      | 668 +++++++++++++++++++++++++++++++++++++++++++++++
 app/test-sad/meson.build |   6 +
 5 files changed, 695 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..34044a2
--- /dev/null
+++ b/app/test-sad/main.c
@@ -0,0 +1,668 @@
+/* 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
+#define BURST_SZ_MAX	64
+
+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;
+	uint32_t	burst_sz;
+	uint8_t		fract_32;
+	uint8_t		fract_64;
+	uint8_t		fract_96;
+	uint8_t		fract_rnd_tuples;
+	int		ipv6;
+	int		verbose;
+	int		parallel_lookup;
+	int		concurrent_rw;
+} 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,
+	.burst_sz = BURST_SZ_MAX,
+	.fract_32 = 90,
+	.fract_64 = 9,
+	.fract_96 = 1,
+	.fract_rnd_tuples = 0,
+	.ipv6 = 0,
+	.verbose = 0,
+	.parallel_lookup = 0,
+	.concurrent_rw = 0
+};
+
+enum {
+	CB_RULE_SPI,
+	CB_RULE_DIP,
+	CB_RULE_SIP,
+	CB_RULE_LEN,
+	CB_RULE_NUM,
+};
+
+static char line[LINE_MAX];
+struct rule {
+	union rte_ipsec_sad_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"
+		"Lookup burst size %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.burst_sz,
+		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"
+		"[-6 <ipv6 tests>]\n"
+		"[-d <\"/\" separated rules length distribution"
+		"(if -f is not specified)>]\n"
+		"[-r <random tuples fraction to lookup"
+		"(if -t is not specified)>]\n"
+		"[-b <lookup burst size: 1-64 >]\n"
+		"[-v <verbose, print results on lookup>]\n"
+		"[-p <parallel lookup on all availible cores>]\n"
+		"[-c <init sad supporting read/write concurrency>]\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.v4.spi, 0,
+				UINT32_MAX, 0);
+
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_DIP],
+				&tbl[j].tuple.v6.dip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_DIP],
+				&tbl[j].tuple.v4.dip);
+		if (ret != 1)
+			return -EINVAL;
+		if (config.ipv6)
+			ret = inet_pton(AF_INET6, in[CB_RULE_SIP],
+				&tbl[j].tuple.v6.sip);
+		else
+			ret = inet_pton(AF_INET, in[CB_RULE_SIP],
+				&tbl[j].tuple.v4.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 uint64_t
+get_rnd_rng(uint64_t l, uint64_t u)
+{
+	if (l == u)
+		return l;
+	else
+		return (rte_rand() % (u - l) + l);
+}
+
+static void
+get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
+{
+	unsigned i, j, rnd;
+	int rule_type;
+	double edge = 0;
+	double step;
+
+	step = (double)UINT32_MAX / nb_rules;
+	for (i = 0; i < nb_rules; i++, edge += step) {
+		rnd = rte_rand() % 100;
+		if (rule_tbl) {
+			tbl[i].tuple.v4.spi = get_rnd_rng((uint64_t)edge,
+						(uint64_t)(edge + step));
+			if (config.ipv6) {
+				for (j = 0; j < 16; j++) {
+					tbl[i].tuple.v6.dip[j] = rte_rand();
+					tbl[i].tuple.v6.sip[j] = rte_rand();
+				}
+			} else {
+				tbl[i].tuple.v4.dip = rte_rand();
+				tbl[i].tuple.v4.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.v4.spi =
+					get_rnd_rng((uint64_t)edge,
+					(uint64_t)(edge + step));
+				if (config.ipv6) {
+					for (j = 0; j < 16; j++) {
+						tbl[i].tuple.v6.dip[j] =
+								rte_rand();
+						tbl[i].tuple.v6.sip[j] =
+								rte_rand();
+					}
+				} else {
+					tbl[i].tuple.v4.dip = rte_rand();
+					tbl[i].tuple.v4.sip = rte_rand();
+				}
+				config.nb_tuples_rnd++;
+			} else {
+				tbl[i].tuple.v4.spi = rules_tbl[i %
+					config.nb_rules].tuple.v4.spi;
+				if (config.ipv6) {
+					int r_idx = i % config.nb_rules;
+					memcpy(tbl[i].tuple.v6.dip,
+						rules_tbl[r_idx].tuple.v6.dip,
+						sizeof(tbl[i].tuple.v6.dip));
+					memcpy(tbl[i].tuple.v6.sip,
+						rules_tbl[r_idx].tuple.v6.sip,
+						sizeof(tbl[i].tuple.v6.sip));
+				} else {
+					tbl[i].tuple.v4.dip = rules_tbl[i %
+						config.nb_rules].tuple.v4.dip;
+					tbl[i].tuple.v4.sip = rules_tbl[i %
+						config.nb_rules].tuple.v4.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:6b:vpc")) != -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) ||
+					(endptr[0] != 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 'b':
+			errno = 0;
+			config.burst_sz = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.burst_sz == 0) ||
+					(config.burst_sz > BURST_SZ_MAX) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -b\n");
+			}
+			break;
+		case 'l':
+			errno = 0;
+			config.nb_tuples = strtoul(optarg, &endptr, 10);
+			if ((errno != 0) || (config.nb_tuples == 0) ||
+					(endptr[0] != 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) ||
+					(endptr[0] != 0)) {
+				print_usage();
+				rte_exit(-EINVAL, "Invalid option -r\n");
+			}
+			break;
+		case '6':
+			config.ipv6 = 1;
+			break;
+		case 'v':
+			config.verbose = 1;
+			break;
+		case 'p':
+			config.parallel_lookup = 1;
+			break;
+		case 'c':
+			config.concurrent_rw = 1;
+			break;
+		default:
+			print_usage();
+			rte_exit(-EINVAL, "Invalid options\n");
+		}
+	}
+}
+
+static void
+print_addr(int af, const void *addr)
+{
+	char str[INET6_ADDRSTRLEN];
+	const char *ret;
+
+	ret = inet_ntop(af, addr, str, sizeof(str));
+	if (ret != NULL)
+		printf("%s", str);
+}
+
+static void
+print_tuple(int af, uint32_t spi, const void *dip, const void *sip)
+{
+
+	printf("<SPI: %u DIP: ", spi);
+	print_addr(af, dip);
+	printf(" SIP: ");
+	print_addr(af, sip);
+	printf(">");
+}
+
+static void
+print_result(const union rte_ipsec_sad_key *key, void *res)
+{
+	struct rule *rule = res;
+	const struct rte_ipsec_sadv4_key *v4;
+	const struct rte_ipsec_sadv6_key *v6;
+	const char *spi_only = "SPI_ONLY";
+	const char *spi_dip = "SPI_DIP";
+	const char *spi_dip_sip = "SPI_DIP_SIP";
+	const char *rule_type;
+	const void *dip, *sip;
+	uint32_t spi;
+	int af;
+
+	af = (config.ipv6) ? AF_INET6 : AF_INET;
+	v4 = &key->v4;
+	v6 = &key->v6;
+	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
+	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
+	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
+
+	if (res == NULL) {
+		printf("TUPLE: ");
+		print_tuple(af, spi, dip, sip);
+		printf(" not found\n");
+		return;
+	}
+
+	switch (rule->rule_type) {
+	case RTE_IPSEC_SAD_SPI_ONLY:
+		rule_type = spi_only;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP:
+		rule_type = spi_dip;
+		break;
+	case RTE_IPSEC_SAD_SPI_DIP_SIP:
+		rule_type = spi_dip_sip;
+		break;
+	default:
+		return;
+	}
+
+	print_tuple(af, spi, dip, sip);
+	v4 = &rule->tuple.v4;
+	v6 = &rule->tuple.v6;
+	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
+	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
+	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
+	printf("\n\tpoints to RULE ID %zu ",
+		RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule));
+	print_tuple(af, spi, dip, sip);
+	printf(" %s\n", rule_type);
+}
+
+static int
+lookup(void *arg)
+{
+	int ret;
+	unsigned int i, j;
+	const union rte_ipsec_sad_key *keys[BURST_SZ_MAX];
+	void *vals[BURST_SZ_MAX];
+	uint64_t start, acc = 0;
+	uint32_t burst_sz;
+	struct rte_ipsec_sad *sad = arg;
+
+	burst_sz = RTE_MIN(config.burst_sz, config.nb_tuples);
+	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_precise();
+		ret = rte_ipsec_sad_lookup(sad, keys, vals, burst_sz);
+		acc += rte_rdtsc_precise() - start;
+		if (ret < 0)
+			rte_exit(-EINVAL, "Lookup failed\n");
+		if (config.verbose) {
+			for (j = 0; j < burst_sz; j++)
+				print_result(keys[j], vals[j]);
+		}
+	}
+	printf("Average lookup cycles %.2Lf, lookups/sec: %.2Lf\n",
+		(long double)acc / config.nb_tuples,
+		(long double)config.nb_tuples * rte_get_tsc_hz() / acc);
+
+	return 0;
+}
+
+static void
+add_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_add(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type, &rules_tbl[i]);
+			if (ret != 0)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("ADD %u rules, %.2Lf cycles/rule, %.2Lf ADD/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+static void
+del_rules(struct rte_ipsec_sad *sad, uint32_t fract)
+{
+	int32_t ret;
+	uint32_t i, j, f, fn, n;
+	uint64_t start, tm[fract + 1];
+	uint32_t nm[fract + 1];
+
+	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
+
+	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
+
+		fn = n + f;
+		fn = fn > config.nb_rules ? config.nb_rules : fn;
+
+		start = rte_rdtsc_precise();
+		for (i = n; i != fn; i++) {
+			ret = rte_ipsec_sad_del(sad,
+				&rules_tbl[i].tuple,
+				rules_tbl[i].rule_type);
+			if (ret != 0 && ret != -ENOENT)
+				rte_exit(ret, "%s failed @ %u-th rule\n",
+					__func__, i);
+		}
+		tm[j] = rte_rdtsc_precise() - start;
+		nm[j] = fn - n;
+	}
+
+	for (i = 0; i != j; i++)
+		printf("DEL %u rules, %.2Lf cycles/rule, %.2Lf DEL/sec\n",
+			nm[i], (long double)tm[i] / nm[i],
+			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+	struct rte_ipsec_sad *sad;
+	struct rte_ipsec_sad_conf conf;
+	unsigned int lcore_id;
+
+	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_32 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
+	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;
+	if (config.ipv6)
+		conf.flags |= RTE_IPSEC_SAD_FLAG_IPV6;
+	if (config.concurrent_rw)
+		conf.flags |= 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();
+
+	add_rules(sad, 10);
+	if (config.parallel_lookup)
+		rte_eal_mp_remote_launch(lookup, sad, SKIP_MASTER);
+
+	lookup(sad);
+	if (config.parallel_lookup)
+		RTE_LCORE_FOREACH_SLAVE(lcore_id)
+			if (rte_eal_wait_lcore(lcore_id) < 0)
+				return -1;
+
+	del_rules(sad, 10);
+
+	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] 60+ messages in thread

* [dpdk-dev] [PATCH v6 6/6] doc/ipsec: update ipsec programmer's guide
  2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
                             ` (6 preceding siblings ...)
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 5/6] app: add test-sad application Vladimir Medvedkin
@ 2019-10-17 15:48           ` Vladimir Medvedkin
  2019-10-18 10:09             ` Ananyev, Konstantin
  7 siblings, 1 reply; 60+ messages in thread
From: Vladimir Medvedkin @ 2019-10-17 15:48 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bernard.iremonger, akhil.goyal

Add section about ipsec SAD
Update release notes

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
---
 doc/guides/prog_guide/ipsec_lib.rst    | 152 +++++++++++++++++++++++++++++++++
 doc/guides/rel_notes/release_19_11.rst |   3 +
 2 files changed, 155 insertions(+)

diff --git a/doc/guides/prog_guide/ipsec_lib.rst b/doc/guides/prog_guide/ipsec_lib.rst
index 63b75b6..4487e4c 100644
--- a/doc/guides/prog_guide/ipsec_lib.rst
+++ b/doc/guides/prog_guide/ipsec_lib.rst
@@ -143,6 +143,158 @@ In that mode the library functions perform
 To accommodate future custom implementations function pointers
 model is used for both *crypto_prepare* and *process* implementations.
 
+SA database API
+----------------
+
+SA database(SAD) is a table with <key, value> pairs.
+
+Value is an opaque user provided pointer to the user defined SA data structure.
+
+According to RFC4301 each SA can be uniquely identified by a key
+which is either:
+
+  - security parameter index(SPI)
+  - or SPI and destination IP(DIP)
+  - or SPI, DIP and source IP(SIP)
+
+In case of multiple matches, longest matching key will be returned.
+
+Create/destroy
+~~~~~~~~~~~~~~
+
+librte_ipsec SAD implementation provides ability to create/destroy SAD tables.
+
+To create SAD table user has to specify how many entries of each key type is
+required and IP protocol type (IPv4/IPv6).
+As an example:
+
+
+.. code-block:: c
+
+    struct rte_ipsec_sad *sad;
+    struct rte_ipsec_sad_conf conf;
+
+    conf.socket_id = -1;
+    conf.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = some_nb_rules_spi_only;
+    conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = some_nb_rules_spi_dip;
+    conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = some_nb_rules_spi_dip_sip;
+    conf.flags = RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
+
+    sad = rte_ipsec_sad_create("test", &conf);
+
+.. note::
+
+    for more information please refer to ipsec library API reference
+
+Add/delete rules
+~~~~~~~~~~~~~~~~
+
+Library also provides methods to add or delete key/value pairs from the SAD.
+To add user has to specify key, key type and a value which is an opaque pointer to SA.
+The key type reflects a set of tuple fields that will be used for lookup of the SA.
+As mentioned above there are 3 types of a key and the representation of a key type is:
+
+.. code-block:: c
+
+        RTE_IPSEC_SAD_SPI_ONLY,
+        RTE_IPSEC_SAD_SPI_DIP,
+        RTE_IPSEC_SAD_SPI_DIP_SIP,
+
+As an example, to add new entry into the SAD for IPv4 addresses:
+
+.. code-block:: c
+
+    struct rte_ipsec_sa *sa;
+    union rte_ipsec_sad_key key;
+
+    key.v4.spi = rte_cpu_to_be_32(spi_val);
+    if (key_type >= RTE_IPSEC_SAD_SPI_DIP) /* DIP is optional*/
+        key.v4.dip = rte_cpu_to_be_32(dip_val);
+    if (key_type == RTE_IPSEC_SAD_SPI_DIP_SIP) /* SIP is optional*/
+        key.v4.sip = rte_cpu_to_be_32(sip_val);
+
+    rte_ipsec_sad_add(sad, &key, key_type, sa);
+
+.. note::
+
+    By performance reason it is better to keep spi/dip/sip in net byte order
+    to eliminate byteswap on lookup
+
+To delete user has to specify key and key type.
+
+Delete code would look like:
+
+.. code-block:: c
+
+    union rte_ipsec_sad_key key;
+
+    key.v4.spi = rte_cpu_to_be_32(necessary_spi);
+    if (key_type >= RTE_IPSEC_SAD_SPI_DIP) /* DIP is optional*/
+        key.v4.dip = rte_cpu_to_be_32(necessary_dip);
+    if (key_type == RTE_IPSEC_SAD_SPI_DIP_SIP) /* SIP is optional*/
+        key.v4.sip = rte_cpu_to_be_32(necessary_sip);
+
+    rte_ipsec_sad_del(sad, &key, key_type);
+
+
+Lookup
+~~~~~~
+Library provides lookup by the given {SPI,DIP,SIP} tuple of
+inbound ipsec packet as a key.
+
+The search key is represented by:
+
+.. code-block:: c
+
+    union rte_ipsec_sad_key {
+        struct rte_ipsec_sadv4_key  v4;
+        struct rte_ipsec_sadv6_key  v6;
+    };
+
+where v4 is a tuple for IPv4:
+
+.. code-block:: c
+
+    struct rte_ipsec_sadv4_key {
+        uint32_t spi;
+        uint32_t dip;
+        uint32_t sip;
+    };
+
+and v6 is a tuple for IPv6:
+
+.. code-block:: c
+
+    struct rte_ipsec_sadv6_key {
+        uint32_t spi;
+        uint8_t dip[16];
+        uint8_t sip[16];
+    };
+
+As an example, lookup related code could look like that:
+
+.. code-block:: c
+
+    int i;
+    union rte_ipsec_sad_key keys[BURST_SZ];
+    const union rte_ipsec_sad_key *keys_p[BURST_SZ];
+    void *vals[BURST_SZ];
+
+    for (i = 0; i < BURST_SZ_MAX; i++) {
+        keys[i].v4.spi = esp_hdr[i]->spi;
+        keys[i].v4.dip = ipv4_hdr[i]->dst_addr;
+        keys[i].v4.sip = ipv4_hdr[i]->src_addr;
+        keys_p[i] = &keys[i];
+    }
+    rte_ipsec_sad_lookup(sad, keys_p, vals, BURST_SZ);
+
+    for (i = 0; i < BURST_SZ_MAX; i++) {
+        if (vals[i] == NULL)
+            printf("SA not found for key index %d\n", i);
+        else
+            printf("SA pointer is %p\n", vals[i]);
+    }
+
 
 Supported features
 ------------------
diff --git a/doc/guides/rel_notes/release_19_11.rst b/doc/guides/rel_notes/release_19_11.rst
index 85953b9..581f311 100644
--- a/doc/guides/rel_notes/release_19_11.rst
+++ b/doc/guides/rel_notes/release_19_11.rst
@@ -115,6 +115,9 @@ New Features
   Added eBPF JIT support for arm64 architecture to improve the eBPF program
   performance.
 
+* **Updated the IPSec library.**
+
+ Added SA Database API to ``librte_ipsec``
 
 Removed Items
 -------------
-- 
2.7.4


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

* Re: [dpdk-dev] [PATCH v6 6/6] doc/ipsec: update ipsec programmer's guide
  2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 6/6] doc/ipsec: update ipsec programmer's guide Vladimir Medvedkin
@ 2019-10-18 10:09             ` Ananyev, Konstantin
  0 siblings, 0 replies; 60+ messages in thread
From: Ananyev, Konstantin @ 2019-10-18 10:09 UTC (permalink / raw)
  To: Medvedkin, Vladimir, dev; +Cc: Iremonger, Bernard, akhil.goyal



> 
> Add section about ipsec SAD
> Update release notes
> 
> Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
> ---

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> --
> 2.7.4


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

end of thread, back to index

Thread overview: 60+ 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
2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 0/5] ipsec: add inbound SAD Vladimir Medvedkin
2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 " Vladimir Medvedkin
2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 " Vladimir Medvedkin
2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 " Vladimir Medvedkin
2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 " Vladimir Medvedkin
2019-10-11 11:34           ` Akhil Goyal
2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 0/6] " Vladimir Medvedkin
2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 1/6] ipsec: add inbound SAD API Vladimir Medvedkin
2019-10-17 15:47           ` [dpdk-dev] [PATCH v6 2/6] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 3/6] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 4/6] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 5/6] app: add test-sad application Vladimir Medvedkin
2019-10-17 15:48           ` [dpdk-dev] [PATCH v6 6/6] doc/ipsec: update ipsec programmer's guide Vladimir Medvedkin
2019-10-18 10:09             ` Ananyev, Konstantin
2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
2019-10-11 10:42           ` Akhil Goyal
2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
2019-10-10 16:49         ` [dpdk-dev] [PATCH v5 5/5] app: add test-sad application Vladimir Medvedkin
2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
2019-10-09 10:49         ` Ananyev, Konstantin
2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
2019-10-09 10:56         ` Ananyev, Konstantin
2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
2019-10-08 16:55       ` [dpdk-dev] [PATCH v4 5/5] app: add test-sad application Vladimir Medvedkin
2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
2019-10-08  9:40     ` [dpdk-dev] [PATCH v3 5/5] app: add test-sad application Vladimir Medvedkin
2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
2019-10-02 11:24     ` Ananyev, Konstantin
2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 2/5] ipsec: add SAD create/destroy implementation Vladimir Medvedkin
2019-10-02 11:55     ` Ananyev, Konstantin
2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
2019-10-02 12:04     ` Ananyev, Konstantin
2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
2019-10-02 11:16     ` Ananyev, Konstantin
2019-10-01 17:25   ` [dpdk-dev] [PATCH v2 5/5] app: add test-sad application Vladimir Medvedkin
2019-10-02 13:27     ` Ananyev, Konstantin
2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 1/5] ipsec: add inbound SAD API Vladimir Medvedkin
2019-09-14 23:05   ` Ananyev, Konstantin
2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 2/5] ipsec: add SAD create/free API Vladimir Medvedkin
2019-09-12 18:08   ` Ananyev, Konstantin
2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 3/5] ipsec: add SAD add/delete/lookup implementation Vladimir Medvedkin
2019-09-12 17:58   ` Ananyev, Konstantin
2019-10-01 17:24     ` Medvedkin, Vladimir
2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 4/5] test/ipsec: add ipsec SAD autotests Vladimir Medvedkin
2019-09-03 16:55 ` [dpdk-dev] [PATCH v1 5/5] app: add test-sad application Vladimir Medvedkin
2019-09-12 18:30   ` Ananyev, Konstantin
2019-09-12 18:33     ` Ananyev, Konstantin
2019-09-12 18:34 ` [dpdk-dev] [RFC 0/5] ipsec: add inbound SAD Ananyev, Konstantin

DPDK patches and discussions

Archives are clonable:
	git clone --mirror http://inbox.dpdk.org/dev/0 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 dev dev/ http://inbox.dpdk.org/dev \
		dev@dpdk.org
	public-inbox-index dev


Newsgroup available over NNTP:
	nntp://inbox.dpdk.org/inbox.dpdk.dev


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