get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/121843/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 121843,
    "url": "http://patchwork.dpdk.org/api/patches/121843/?format=api",
    "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/20230111205608.87953-2-cristian.dumitrescu@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patchwork.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230111205608.87953-2-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230111205608.87953-2-cristian.dumitrescu@intel.com",
    "date": "2023-01-11T20:55:58",
    "name": "[01/11] pipeline: add IPsec support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "cf19edff8aeb09691f279ebd06bf674786f7dc41",
    "submitter": {
        "id": 19,
        "url": "http://patchwork.dpdk.org/api/people/19/?format=api",
        "name": "Cristian Dumitrescu",
        "email": "cristian.dumitrescu@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patchwork.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/20230111205608.87953-2-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 26482,
            "url": "http://patchwork.dpdk.org/api/series/26482/?format=api",
            "web_url": "http://patchwork.dpdk.org/project/dpdk/list/?series=26482",
            "date": "2023-01-11T20:55:57",
            "name": "pipeline: add IPsec support",
            "version": 1,
            "mbox": "http://patchwork.dpdk.org/series/26482/mbox/"
        }
    ],
    "comments": "http://patchwork.dpdk.org/api/patches/121843/comments/",
    "check": "fail",
    "checks": "http://patchwork.dpdk.org/api/patches/121843/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id EF12A423AF;\n\tWed, 11 Jan 2023 21:56:20 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id ED0B342D27;\n\tWed, 11 Jan 2023 21:56:14 +0100 (CET)",
            "from mga12.intel.com (mga12.intel.com [192.55.52.136])\n by mails.dpdk.org (Postfix) with ESMTP id 462FF40A7D\n for <dev@dpdk.org>; Wed, 11 Jan 2023 21:56:12 +0100 (CET)",
            "from orsmga003.jf.intel.com ([10.7.209.27])\n by fmsmga106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 11 Jan 2023 12:56:11 -0800",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.222.53])\n by orsmga003.jf.intel.com with ESMTP; 11 Jan 2023 12:56:10 -0800"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1673470572; x=1705006572;\n h=from:to:cc:subject:date:message-id:in-reply-to:\n references:mime-version:content-transfer-encoding;\n bh=9x/amUTJyOpEI6l+weL/iz4LOOW3W0iIEblKH9xRKpI=;\n b=noGFmheAAmJEgJwxPMZUiW8I+sZrzmc31BOqwnj+s/+Ys4iH2fndhzjb\n XgAqTSYcsUwNzlgBS/nuhPeSTrZhRglZUMghvNN3fpAV4p0ziNopSs7hm\n 9evnEvr/Bf7z4jfdvz2sd+i56OsT2KGSxjbcaDu3REh31MZa2yry2uFxs\n 7a2xS00c1AVV7qYlD1JuDDNbskOy6XaaYef+fcuR/TU4QmKUYMoiwYINX\n dzWeJKeFaip4miRQdK/rB/Ha5Ux1S4VJlnppxBk6nydp04VDRni+B3Zxj\n OgxnwF2l7zQLWL1ZdSuxrq+zRpzya+xVdnB+0a+8LWmOlHndQjpYctR/w Q==;",
        "X-IronPort-AV": [
            "E=McAfee;i=\"6500,9779,10586\"; a=\"303229765\"",
            "E=Sophos;i=\"5.96,317,1665471600\"; d=\"scan'208\";a=\"303229765\"",
            "E=McAfee;i=\"6500,9779,10586\"; a=\"607518831\"",
            "E=Sophos;i=\"5.96,317,1665471600\"; d=\"scan'208\";a=\"607518831\""
        ],
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "Kamalakannan R <kamalakannan.r@intel.com>",
        "Subject": "[PATCH 01/11] pipeline: add IPsec support",
        "Date": "Wed, 11 Jan 2023 20:55:58 +0000",
        "Message-Id": "<20230111205608.87953-2-cristian.dumitrescu@intel.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20230111205608.87953-1-cristian.dumitrescu@intel.com>",
        "References": "<20230111205608.87953-1-cristian.dumitrescu@intel.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "This block is providing IPsec support to the SWX pipeline. The IPsec\nblock is external to the pipeline, so it needs to be explicitly\ninstantiated and connected to a pipeline through the I/O ports.\n\nSigned-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>\nSigned-off-by: Kamalakannan R <kamalakannan.r@intel.com>\n---\n lib/pipeline/meson.build     |    4 +-\n lib/pipeline/rte_swx_ipsec.c | 1851 ++++++++++++++++++++++++++++++++++\n lib/pipeline/rte_swx_ipsec.h |  383 +++++++\n lib/pipeline/version.map     |    9 +\n 4 files changed, 2246 insertions(+), 1 deletion(-)\n create mode 100644 lib/pipeline/rte_swx_ipsec.c\n create mode 100644 lib/pipeline/rte_swx_ipsec.h",
    "diff": "diff --git a/lib/pipeline/meson.build b/lib/pipeline/meson.build\nindex 3ca98ed194..aa3fd0c2b8 100644\n--- a/lib/pipeline/meson.build\n+++ b/lib/pipeline/meson.build\n@@ -11,6 +11,7 @@ sources = files(\n         'rte_pipeline.c',\n         'rte_port_in_action.c',\n         'rte_table_action.c',\n+\t'rte_swx_ipsec.c',\n         'rte_swx_pipeline.c',\n         'rte_swx_pipeline_spec.c',\n         'rte_swx_ctl.c',\n@@ -19,8 +20,9 @@ headers = files(\n         'rte_pipeline.h',\n         'rte_port_in_action.h',\n         'rte_table_action.h',\n+\t'rte_swx_ipsec.h',\n         'rte_swx_pipeline.h',\n         'rte_swx_extern.h',\n         'rte_swx_ctl.h',\n )\n-deps += ['port', 'table', 'meter', 'sched', 'cryptodev']\n+deps += ['port', 'table', 'meter', 'sched', 'cryptodev', 'ipsec']\ndiff --git a/lib/pipeline/rte_swx_ipsec.c b/lib/pipeline/rte_swx_ipsec.c\nnew file mode 100644\nindex 0000000000..7ad2ee7227\n--- /dev/null\n+++ b/lib/pipeline/rte_swx_ipsec.c\n@@ -0,0 +1,1851 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2022 Intel Corporation\n+ */\n+#include <stdlib.h>\n+#include <stdio.h>\n+#include <errno.h>\n+#include <arpa/inet.h>\n+\n+#include <rte_common.h>\n+#include <rte_ip.h>\n+#include <rte_tailq.h>\n+#include <rte_eal_memconfig.h>\n+#include <rte_ring.h>\n+#include <rte_mbuf.h>\n+#include <rte_cryptodev.h>\n+#include <rte_ipsec.h>\n+\n+#include \"rte_swx_ipsec.h\"\n+\n+#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE\n+\n+#include <rte_malloc.h>\n+\n+static void *\n+env_calloc(size_t size, size_t alignment, int numa_node)\n+{\n+\treturn rte_zmalloc_socket(NULL, size, alignment, numa_node);\n+}\n+\n+static void\n+env_free(void *start, size_t size __rte_unused)\n+{\n+\trte_free(start);\n+}\n+\n+#else\n+\n+#include <numa.h>\n+\n+static void *\n+env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)\n+{\n+\tvoid *start;\n+\n+\tif (numa_available() == -1)\n+\t\treturn NULL;\n+\n+\tstart = numa_alloc_onnode(size, numa_node);\n+\tif (!start)\n+\t\treturn NULL;\n+\n+\tmemset(start, 0, size);\n+\treturn start;\n+}\n+\n+static void\n+env_free(void *start, size_t size)\n+{\n+\tif ((numa_available() == -1) || !start)\n+\t\treturn;\n+\n+\tnuma_free(start, size);\n+}\n+\n+#endif\n+\n+#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE\n+#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256\n+#endif\n+\n+/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires\n+ * the mempool size to be at least 1.5 times the size of the mempool cache.\n+ */\n+#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)\n+\n+struct ipsec_sa {\n+\tstruct rte_ipsec_session s;\n+\tint valid;\n+};\n+\n+struct ipsec_pkts_in {\n+\tstruct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tstruct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tstruct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tstruct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tstruct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tuint32_t n_cops;\n+};\n+\n+struct ipsec_pkts_out {\n+\tstruct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tstruct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tstruct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tstruct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];\n+\tuint32_t n_pkts;\n+};\n+\n+struct rte_swx_ipsec {\n+\t/*\n+\t * Parameters.\n+\t */\n+\n+\t/* IPsec instance name. */\n+\tchar name[RTE_SWX_IPSEC_NAME_SIZE];\n+\n+\t/* Input packet queue. */\n+\tstruct rte_ring *ring_in;\n+\n+\t/* Output packet queue. */\n+\tstruct rte_ring *ring_out;\n+\n+\t/* Crypto device ID. */\n+\tuint8_t dev_id;\n+\n+\t/* Crypto device queue pair ID. */\n+\tuint16_t qp_id;\n+\n+\t/* Burst sizes. */\n+\tstruct rte_swx_ipsec_burst_size bsz;\n+\n+\t/* SA table size. */\n+\tsize_t n_sa_max;\n+\n+\t/*\n+\t * Internals.\n+\t */\n+\t/* Crypto device buffer pool for sessions. */\n+\tstruct rte_mempool *mp_session;\n+\n+\t/* Crypto device bufer pool for session private data. */\n+\tstruct rte_mempool *mp_session_priv;\n+\n+\t/* Pre-crypto packets. */\n+\tstruct ipsec_pkts_in in;\n+\n+\t/* Post-crypto packets. */\n+\tstruct ipsec_pkts_out out;\n+\n+\t/* Crypto device enqueue threshold. */\n+\tuint32_t crypto_wr_threshold;\n+\n+\t/* Packets currently under crypto device processing. */\n+\tuint32_t n_pkts_crypto;\n+\n+\t/* List of free SADB positions. */\n+\tuint32_t *sa_free_id;\n+\n+\t/* Number of elements in the SADB list of free positions. */\n+\tsize_t n_sa_free_id;\n+\n+\t/* Allocated memory total size in bytes. */\n+\tsize_t total_size;\n+\n+\t/* Flag for registration to the global list of instances. */\n+\tint registered;\n+\n+\t/*\n+\t * Table memory.\n+\t */\n+\tuint8_t memory[] __rte_cache_aligned;\n+};\n+\n+static inline struct ipsec_sa *\n+ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)\n+{\n+\tstruct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;\n+\n+\treturn &sadb[sa_id & (ipsec->n_sa_max - 1)];\n+}\n+\n+/* Global list of instances. */\n+TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);\n+\n+static struct rte_tailq_elem rte_swx_ipsec_tailq = {\n+\t.name = \"RTE_SWX_IPSEC\",\n+};\n+\n+EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)\n+\n+struct rte_swx_ipsec *\n+rte_swx_ipsec_find(const char *name)\n+{\n+\tstruct rte_swx_ipsec_list *ipsec_list;\n+\tstruct rte_tailq_entry *te = NULL;\n+\n+\tif (!name ||\n+\t    !name[0] ||\n+\t    (strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))\n+\t\treturn NULL;\n+\n+\tipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);\n+\n+\trte_mcfg_tailq_read_lock();\n+\n+\tTAILQ_FOREACH(te, ipsec_list, next) {\n+\t\tstruct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;\n+\n+\t\tif (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {\n+\t\t\trte_mcfg_tailq_read_unlock();\n+\t\t\treturn ipsec;\n+\t\t}\n+\t}\n+\n+\trte_mcfg_tailq_read_unlock();\n+\treturn NULL;\n+}\n+\n+static int\n+ipsec_register(struct rte_swx_ipsec *ipsec)\n+{\n+\tstruct rte_swx_ipsec_list *ipsec_list;\n+\tstruct rte_tailq_entry *te = NULL;\n+\n+\tipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);\n+\n+\trte_mcfg_tailq_write_lock();\n+\n+\tTAILQ_FOREACH(te, ipsec_list, next) {\n+\t\tstruct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;\n+\n+\t\tif (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {\n+\t\t\trte_mcfg_tailq_write_unlock();\n+\t\t\treturn -EEXIST;\n+\t\t}\n+\t}\n+\n+\tte = calloc(1, sizeof(struct rte_tailq_entry));\n+\tif (!te) {\n+\t\trte_mcfg_tailq_write_unlock();\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tte->data = (void *)ipsec;\n+\tTAILQ_INSERT_TAIL(ipsec_list, te, next);\n+\trte_mcfg_tailq_write_unlock();\n+\treturn 0;\n+}\n+\n+static void\n+ipsec_unregister(struct rte_swx_ipsec *ipsec)\n+{\n+\tstruct rte_swx_ipsec_list *ipsec_list;\n+\tstruct rte_tailq_entry *te = NULL;\n+\n+\tipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);\n+\n+\trte_mcfg_tailq_write_lock();\n+\n+\tTAILQ_FOREACH(te, ipsec_list, next) {\n+\t\tif (te->data == (void *)ipsec) {\n+\t\t\tTAILQ_REMOVE(ipsec_list, te, next);\n+\t\t\trte_mcfg_tailq_write_unlock();\n+\t\t\tfree(te);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\trte_mcfg_tailq_write_unlock();\n+}\n+\n+static void\n+ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);\n+\n+void\n+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)\n+{\n+\tsize_t i;\n+\n+\tif (!ipsec)\n+\t\treturn;\n+\n+\t/* Remove the current instance from the global list. */\n+\tif (ipsec->registered)\n+\t\tipsec_unregister(ipsec);\n+\n+\t/* SADB. */\n+\tfor (i = 0; i < ipsec->n_sa_max; i++) {\n+\t\tstruct ipsec_sa *sa = ipsec_sa_get(ipsec, i);\n+\n+\t\tif (!sa->valid)\n+\t\t\tcontinue;\n+\n+\t\t/* SA session. */\n+\t\tipsec_session_free(ipsec, &sa->s);\n+\t}\n+\n+\t/* Crypto device buffer pools. */\n+\trte_mempool_free(ipsec->mp_session);\n+\trte_mempool_free(ipsec->mp_session_priv);\n+\n+\t/* IPsec object memory. */\n+\tenv_free(ipsec, ipsec->total_size);\n+}\n+\n+int\n+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,\n+\t\t     const char *name,\n+\t\t     struct rte_swx_ipsec_params *params,\n+\t\t     int numa_node)\n+{\n+\tchar resource_name[RTE_SWX_IPSEC_NAME_SIZE];\n+\tstruct rte_swx_ipsec *ipsec = NULL;\n+\tstruct rte_ring *ring_in, *ring_out;\n+\tstruct rte_cryptodev_info dev_info;\n+\tsize_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;\n+\tint dev_id, status = 0;\n+\n+\t/* Check input parameters. */\n+\tif (!ipsec_out ||\n+\t    !name ||\n+\t    !name[0] ||\n+\t    (strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||\n+\t    !params ||\n+\t    (params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||\n+\t    (params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||\n+\t    (params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||\n+\t    (params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||\n+\t    !params->n_sa_max) {\n+\t\tstatus = -EINVAL;\n+\t\tgoto error;\n+\t}\n+\n+\tring_in = rte_ring_lookup(params->ring_in_name);\n+\tif (!ring_in) {\n+\t\tstatus = -EINVAL;\n+\t\tgoto error;\n+\t}\n+\n+\tring_out = rte_ring_lookup(params->ring_out_name);\n+\tif (!ring_out) {\n+\t\tstatus = -EINVAL;\n+\t\tgoto error;\n+\t}\n+\n+\tdev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);\n+\tif (dev_id == -1) {\n+\t\tstatus = -EINVAL;\n+\t\tgoto error;\n+\t}\n+\n+\trte_cryptodev_info_get(dev_id, &dev_info);\n+\tif (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {\n+\t\tstatus = -EINVAL;\n+\t\tgoto error;\n+\t}\n+\n+\t/* Memory allocation. */\n+\tn_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));\n+\n+\tsadb_offset = sizeof(struct rte_swx_ipsec);\n+\tsadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));\n+\n+\tsa_free_id_offset = sadb_offset + sadb_size;\n+\tsa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));\n+\n+\ttotal_size = sa_free_id_offset + sa_free_id_size;\n+\tipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);\n+\tif (!ipsec) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto error;\n+\t}\n+\n+\t/* Initialization. */\n+\tstrcpy(ipsec->name, name);\n+\tipsec->ring_in = ring_in;\n+\tipsec->ring_out = ring_out;\n+\tipsec->dev_id = (uint8_t)dev_id;\n+\tipsec->qp_id = params->crypto_dev_queue_pair_id;\n+\tmemcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));\n+\tipsec->n_sa_max = n_sa_max;\n+\n+\tipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;\n+\n+\tipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];\n+\tfor (i = 0; i < n_sa_max; i++)\n+\t\tipsec->sa_free_id[i] = n_sa_max - 1 - i;\n+\tipsec->n_sa_free_id = n_sa_max;\n+\n+\tipsec->total_size = total_size;\n+\n+\t/* Crypto device memory pools. */\n+\tsnprintf(resource_name, sizeof(resource_name), \"%s_mp\", name);\n+\tipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,\n+\t\tn_sa_max, /* number of pool elements */\n+\t\t0, /* pool element size */\n+\t\tRTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */\n+\t\t0, /* pool element private data size */\n+\t\tnuma_node);\n+\tif (!ipsec->mp_session) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto error;\n+\t}\n+\n+\tsnprintf(resource_name, sizeof(resource_name), \"%s_mp_priv\", name);\n+\tipsec->mp_session_priv = rte_mempool_create(resource_name,\n+\t\tn_sa_max, /* number of pool elements */\n+\t\trte_cryptodev_sym_get_private_session_size(dev_id), /* pool element size */\n+\t\tRTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */\n+\t\t0, /* pool private data size */\n+\t\tNULL,\n+\t\tNULL,\n+\t\tNULL,\n+\t\tNULL,\n+\t\tnuma_node,\n+\t\t0); /* pool flags */\n+\tif (!ipsec->mp_session_priv) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto error;\n+\t}\n+\n+\t/* Add the current instance to the global list. */\n+\tstatus = ipsec_register(ipsec);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tipsec->registered = 1;\n+\n+\t*ipsec_out = ipsec;\n+\treturn 0;\n+\n+error:\n+\trte_swx_ipsec_free(ipsec);\n+\treturn status;\n+}\n+\n+static inline int\n+ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)\n+{\n+\tstruct ipsec_sa *sa;\n+\tstruct rte_ipsec_group *g;\n+\tint n_groups, n_pkts_in_group, i;\n+\n+\tsa = ipsec->in.sa[0];\n+\n+\tg = &ipsec->in.groups[0];\n+\tg->id.ptr = sa;\n+\tg->m = &ipsec->in.pkts[0];\n+\tn_pkts_in_group = 1;\n+\tn_groups = 1;\n+\n+\tfor (i = 1; i < n_pkts; i++) {\n+\t\tstruct ipsec_sa *sa_new = ipsec->in.sa[i];\n+\n+\t\t/* Same SA => Add the current pkt to the same group. */\n+\t\tif (sa_new == sa) {\n+\t\t\tn_pkts_in_group++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Different SA => Close the current group & add the current pkt to a new group. */\n+\t\tg->cnt = n_pkts_in_group;\n+\t\tsa = sa_new;\n+\n+\t\tg++;\n+\t\tg->id.ptr = sa;\n+\t\tg->m = &ipsec->in.pkts[i];\n+\t\tn_pkts_in_group = 1;\n+\t\tn_groups++;\n+\t}\n+\n+\t/* Close the last group. */\n+\tg->cnt = n_pkts_in_group;\n+\n+\treturn n_groups;\n+}\n+\n+static inline void\n+ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)\n+{\n+\tstruct rte_crypto_op **dst0 = ipsec->in.cops, **dst;\n+\tstruct rte_crypto_op **src = ipsec->in.group_cops;\n+\n+\tuint32_t n_pkts_crypto = ipsec->n_pkts_crypto;\n+\tuint32_t n_dst = ipsec->in.n_cops;\n+\tuint32_t n_dst_max = ipsec->bsz.crypto_wr;\n+\tuint32_t n_dst_avail = n_dst_max - n_dst;\n+\tuint32_t n_src = n_cops;\n+\tuint32_t i;\n+\n+\tdst = &dst0[n_dst];\n+\n+\t/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly\n+\t * instead of moving the SRC to DST first and then using DST.\n+\t */\n+\tif (!n_dst && n_src >= ipsec->crypto_wr_threshold) {\n+\t\tuint16_t n_ok;\n+\n+\t\tn_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);\n+\t\tipsec->n_pkts_crypto = n_pkts_crypto + n_ok;\n+\n+\t\tfor (i = n_ok; i < n_src; i++) {\n+\t\t\tstruct rte_crypto_op *cop = src[i];\n+\t\t\tstruct rte_mbuf *m = cop->sym->m_src;\n+\n+\t\t\trte_pktmbuf_free(m);\n+\t\t}\n+\n+\t\treturn;\n+\t}\n+\n+\t/* Move from SRC to DST. Every time DST gets full, send burst from DST. */\n+\tfor ( ; n_src >= n_dst_avail; ) {\n+\t\tuint32_t n_ok;\n+\n+\t\t/* Move from SRC to DST. */\n+\t\tfor (i = 0; i < n_dst_avail; i++)\n+\t\t\t*dst++ = *src++;\n+\n+\t\tn_src -= n_dst_avail;\n+\n+\t\t/* DST full: send burst from DST. */\n+\t\tn_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);\n+\t\tn_pkts_crypto += n_ok;\n+\n+\t\tfor (i = n_ok ; i < n_dst_max; i++) {\n+\t\t\tstruct rte_crypto_op *cop = dst0[i];\n+\t\t\tstruct rte_mbuf *m = cop->sym->m_src;\n+\n+\t\t\trte_pktmbuf_free(m);\n+\t\t}\n+\n+\t\t/* Next iteration. */\n+\t\tdst = dst0;\n+\t\tn_dst = 0;\n+\t\tn_dst_avail = n_dst_max;\n+\t}\n+\n+\tipsec->n_pkts_crypto = n_pkts_crypto;\n+\n+\t/* Move from SRC to DST. Not enough elements in SRC to get DST full. */\n+\tfor (i = 0; i < n_src; i++)\n+\t\t*dst++ = *src++;\n+\n+\tn_dst += n_src;\n+\n+\tipsec->in.n_cops = n_dst;\n+}\n+\n+/**\n+ * Packet buffer anatomy:\n+ *\n+ * +----------+---------+--------------------------------------------------------------------------+\n+ * | Offset   | Size    | Description                                                              |\n+ * | (Byte #) | (Bytes) |                                                                          |\n+ * +==========+=========+==========================================================================+\n+ * | 0        | 128     | Meta-data: struct rte_mbuf.                                              |\n+ * |          |         | The buf_addr field points to the start of the packet section.            |\n+ * +----------+---------+--------------------------------------------------------------------------+\n+ * | 128      | 128     | Meta-data: struct ipsec_mbuf (see below).                                |\n+ * +----------+---------+--------------------------------------------------------------------------+\n+ * | 256      |         | Packet section.                                                          |\n+ * |          |         | The first packet byte is placed at the offset indicated by the struct    |\n+ * |          |         | rte_mbuf::data_off field relative to the start of the packet section.    |\n+ * +----------+---------+--------------------------------------------------------------------------+\n+ */\n+struct ipsec_mbuf {\n+\tstruct ipsec_sa *sa;\n+\tstruct rte_crypto_op cop;\n+\tstruct rte_crypto_sym_op sym_cop;\n+\tuint8_t buffer[32]; /* The crypto IV is placed here. */\n+};\n+\n+/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */\n+#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))\n+\n+#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)\n+\n+static inline void\n+rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)\n+{\n+\tint n_pkts, n_groups, i;\n+\n+\t/* Read packets from the input ring. */\n+\tn_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,\n+\t\t\t\t\t   (void **)ipsec->in.pkts,\n+\t\t\t\t\t   ipsec->bsz.ring_rd,\n+\t\t\t\t\t   NULL);\n+\tif (!n_pkts)\n+\t\treturn;\n+\n+\t/* Get the SA for each packet. */\n+\tfor (i = 0; i < n_pkts; i++) {\n+\t\tstruct rte_mbuf *m = ipsec->in.pkts[i];\n+\t\tstruct rte_swx_ipsec_input_packet_metadata *meta;\n+\t\tstruct rte_ipv4_hdr *ipv4_hdr;\n+\t\tuint32_t sa_id;\n+\n+\t\tmeta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);\n+\t\tipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);\n+\n+\t\t/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */\n+\t\tsa_id = ntohl(meta->sa_id);\n+\n+\t\t/* Consume the IPsec meta-data. */\n+\t\tm->data_off += META_LENGTH;\n+\t\tm->data_len -= META_LENGTH;\n+\t\tm->pkt_len -= META_LENGTH;\n+\n+\t\t/* Set the fields required by the IPsec library. */\n+\t\tm->l2_len = 0;\n+\t\tm->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?\n+\t\t\tsizeof(struct rte_ipv4_hdr) :\n+\t\t\tsizeof(struct rte_ipv6_hdr);\n+\n+\t\t/* Get the SA. */\n+\t\tipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);\n+\t}\n+\n+\t/* Group packets that share the same SA. */\n+\tn_groups = ipsec_sa_group(ipsec, n_pkts);\n+\n+\t/* Write each group of packets sharing the same SA to the crypto device. */\n+\tfor (i = 0; i < n_groups; i++) {\n+\t\tstruct rte_ipsec_group *g = &ipsec->in.groups[i];\n+\t\tstruct ipsec_sa *sa = g->id.ptr;\n+\t\tstruct rte_ipsec_session *s = &sa->s;\n+\t\tuint32_t j;\n+\t\tuint16_t n_pkts_ok;\n+\n+\t\t/* Prepare the crypto ops for the current group. */\n+\t\tfor (j = 0; j < g->cnt; j++) {\n+\t\t\tstruct rte_mbuf *m = g->m[j];\n+\t\t\tstruct ipsec_mbuf *priv = rte_mbuf_to_priv(m);\n+\n+\t\t\tpriv->sa = sa;\n+\t\t\tipsec->in.group_cops[j] = &priv->cop;\n+\t\t}\n+\n+\t\tn_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);\n+\n+\t\tfor (j = n_pkts_ok; j < g->cnt; j++) {\n+\t\t\tstruct rte_mbuf *m = g->m[j];\n+\n+\t\t\trte_pktmbuf_free(m);\n+\t\t}\n+\n+\t\t/* Write the crypto ops of the current group to the crypto device. */\n+\t\tipsec_crypto_enqueue(ipsec, n_pkts_ok);\n+\t}\n+}\n+\n+static inline void\n+ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)\n+{\n+\tstruct rte_mbuf **dst0 = ipsec->out.pkts, **dst;\n+\tstruct rte_mbuf **src = g->m;\n+\n+\tuint32_t n_dst = ipsec->out.n_pkts;\n+\tuint32_t n_dst_max = ipsec->bsz.ring_wr;\n+\tuint32_t n_dst_avail = n_dst_max - n_dst;\n+\tuint32_t n_src = n_pkts;\n+\tuint32_t i;\n+\n+\tdst = &dst0[n_dst];\n+\n+\t/* Move from SRC to DST. Every time DST gets full, send burst from DST. */\n+\tfor ( ; n_src >= n_dst_avail; ) {\n+\t\tuint32_t n_ok;\n+\n+\t\t/* Move from SRC to DST. */\n+\t\tfor (i = 0; i < n_dst_avail; i++)\n+\t\t\t*dst++ = *src++;\n+\n+\t\tn_src -= n_dst_avail;\n+\n+\t\t/* DST full: send burst from DST. */\n+\t\tn_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);\n+\n+\t\tfor (i = n_ok ; i < n_dst_max; i++) {\n+\t\t\tstruct rte_mbuf *m = dst[i];\n+\n+\t\t\trte_pktmbuf_free(m);\n+\t\t}\n+\n+\t\t/* Next iteration. */\n+\t\tdst = dst0;\n+\t\tn_dst = 0;\n+\t\tn_dst_avail = n_dst_max;\n+\t}\n+\n+\t/* Move from SRC to DST. Not enough elements in SRC to get DST full. */\n+\tfor (i = 0; i < n_src; i++)\n+\t\t*dst++ = *src++;\n+\n+\tn_dst += n_src;\n+\n+\tipsec->out.n_pkts = n_dst;\n+}\n+\n+static inline void\n+rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)\n+{\n+\tuint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, n_groups, i;\n+\n+\t/* Read the crypto ops from the crypto device. */\n+\tif (!n_pkts_crypto)\n+\t\treturn;\n+\n+\tn_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,\n+\t\t\t\t\t     ipsec->qp_id,\n+\t\t\t\t\t     ipsec->out.cops,\n+\t\t\t\t\t     ipsec->bsz.crypto_rd);\n+\tif (!n_pkts)\n+\t\treturn;\n+\n+\tipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;\n+\n+\t/* Group packets that share the same SA. */\n+\tn_groups = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,\n+\t\t\t\t\t      ipsec->out.group_pkts,\n+\t\t\t\t\t      ipsec->out.groups,\n+\t\t\t\t\t      n_pkts);\n+\n+\t/* Peform post-crypto IPsec processing for each group of packets that share the same SA.\n+\t * Write each group of packets to the output ring.\n+\t */\n+\tfor (i = 0, n_pkts = 0; i < n_groups; i++) {\n+\t\tstruct rte_ipsec_group *g = &ipsec->out.groups[i];\n+\t\tstruct rte_ipsec_session *s = g->id.ptr;\n+\t\tuint32_t n_pkts_ok, j;\n+\n+\t\t/* Perform post-crypto IPsec processing for the current group. */\n+\t\tn_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);\n+\n+\t\tfor (j = n_pkts_ok; j < g->cnt; j++) {\n+\t\t\tstruct rte_mbuf *m = g->m[j];\n+\n+\t\t\trte_pktmbuf_free(m);\n+\t\t}\n+\n+\t\t/* Write the packets of the current group to the output ring. */\n+\t\tipsec_ring_enqueue(ipsec, g, n_pkts_ok);\n+\t}\n+}\n+\n+void\n+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)\n+{\n+\trte_swx_ipsec_pre_crypto(ipsec);\n+\trte_swx_ipsec_post_crypto(ipsec);\n+}\n+\n+/**\n+ * IPsec Control Plane API\n+ */\n+struct cipher_alg {\n+\tconst char *name;\n+\tenum rte_crypto_cipher_algorithm alg;\n+\tuint32_t iv_size;\n+\tuint32_t block_size;\n+\tuint32_t key_size;\n+};\n+\n+struct auth_alg {\n+\tconst char *name;\n+\tenum rte_crypto_auth_algorithm alg;\n+\tuint32_t iv_size;\n+\tuint32_t digest_size;\n+\tuint32_t key_size;\n+};\n+\n+struct aead_alg {\n+\tconst char *name;\n+\tenum rte_crypto_aead_algorithm alg;\n+\tuint32_t iv_size;\n+\tuint32_t block_size;\n+\tuint32_t digest_size;\n+\tuint32_t key_size;\n+\tuint32_t aad_size;\n+};\n+\n+static struct cipher_alg cipher_algs[] = {\n+\t[0] = {\n+\t\t.name = \"null\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_NULL,\n+\t\t.iv_size = 0,\n+\t\t.block_size = 4,\n+\t\t.key_size = 0,\n+\t},\n+\n+\t[1] = {\n+\t\t.name = \"aes-cbc-128\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_AES_CBC,\n+\t\t.iv_size = 16,\n+\t\t.block_size = 16,\n+\t\t.key_size = 16,\n+\t},\n+\n+\t[2] = {\n+\t\t.name = \"aes-cbc-192\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_AES_CBC,\n+\t\t.iv_size = 16,\n+\t\t.block_size = 16,\n+\t\t.key_size = 24,\n+\t},\n+\n+\t[3] = {\n+\t\t.name = \"aes-cbc-256\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_AES_CBC,\n+\t\t.iv_size = 16,\n+\t\t.block_size = 16,\n+\t\t.key_size = 32,\n+\t},\n+\n+\t[4] = {\n+\t\t.name = \"aes-ctr-128\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_AES_CTR,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 4,\n+\t\t.key_size = 20,\n+\t},\n+\n+\t[5] = {\n+\t\t.name = \"aes-ctr-192\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_AES_CTR,\n+\t\t.iv_size = 16,\n+\t\t.block_size = 16,\n+\t\t.key_size = 28,\n+\t},\n+\n+\t[6] = {\n+\t\t.name = \"aes-ctr-256\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_AES_CTR,\n+\t\t.iv_size = 16,\n+\t\t.block_size = 16,\n+\t\t.key_size = 36,\n+\t},\n+\n+\t[7] = {\n+\t\t.name = \"3des-cbc\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_3DES_CBC,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 8,\n+\t\t.key_size = 24,\n+\t},\n+\n+\t[8] = {\n+\t\t.name = \"des-cbc\",\n+\t\t.alg = RTE_CRYPTO_CIPHER_DES_CBC,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 8,\n+\t\t.key_size = 8,\n+\t},\n+};\n+\n+static struct auth_alg auth_algs[] = {\n+\t[0] = {\n+\t\t.name = \"null\",\n+\t\t.alg = RTE_CRYPTO_AUTH_NULL,\n+\t\t.iv_size = 0,\n+\t\t.digest_size = 0,\n+\t\t.key_size = 0,\n+\t},\n+\n+\t[1] = {\n+\t\t.name = \"sha1-hmac\",\n+\t\t.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,\n+\t\t.iv_size = 0,\n+\t\t.digest_size = 12,\n+\t\t.key_size = 20,\n+\t},\n+\n+\t[2] = {\n+\t\t.name = \"sha256-hmac\",\n+\t\t.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,\n+\t\t.iv_size = 0,\n+\t\t.digest_size = 16,\n+\t\t.key_size = 32,\n+\t},\n+\n+\t[3] = {\n+\t\t.name = \"sha384-hmac\",\n+\t\t.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,\n+\t\t.iv_size = 0,\n+\t\t.digest_size = 24,\n+\t\t.key_size = 48,\n+\t},\n+\n+\t[4] = {\n+\t\t.name = \"sha512-hmac\",\n+\t\t.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,\n+\t\t.iv_size = 0,\n+\t\t.digest_size = 32,\n+\t\t.key_size = 64,\n+\t},\n+\n+\t[5] = {\n+\t\t.name = \"aes-gmac\",\n+\t\t.alg = RTE_CRYPTO_AUTH_AES_GMAC,\n+\t\t.iv_size = 8,\n+\t\t.digest_size = 16,\n+\t\t.key_size = 20,\n+\t},\n+\n+\t[6] = {\n+\t\t.name = \"aes-xcbc-mac-96\",\n+\t\t.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,\n+\t\t.iv_size = 0,\n+\t\t.digest_size = 12,\n+\t\t.key_size = 16,\n+\t},\n+};\n+\n+static struct aead_alg aead_algs[] = {\n+\t[0] = {\n+\t\t.name = \"aes-gcm-128\",\n+\t\t.alg = RTE_CRYPTO_AEAD_AES_GCM,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 4,\n+\t\t.key_size = 20,\n+\t\t.digest_size = 16,\n+\t\t.aad_size = 8,\n+\t},\n+\n+\t[1] = {\n+\t\t.name = \"aes-gcm-192\",\n+\t\t.alg = RTE_CRYPTO_AEAD_AES_GCM,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 4,\n+\t\t.key_size = 28,\n+\t\t.digest_size = 16,\n+\t\t.aad_size = 8,\n+\t},\n+\n+\t[2] = {\n+\t\t.name = \"aes-gcm-256\",\n+\t\t.alg = RTE_CRYPTO_AEAD_AES_GCM,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 4,\n+\t\t.key_size = 36,\n+\t\t.digest_size = 16,\n+\t\t.aad_size = 8,\n+\t},\n+\n+\t[3] = {\n+\t\t.name = \"aes-ccm-128\",\n+\t\t.alg = RTE_CRYPTO_AEAD_AES_CCM,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 4,\n+\t\t.key_size = 20,\n+\t\t.digest_size = 16,\n+\t\t.aad_size = 8,\n+\t},\n+\n+\t[4] = {\n+\t\t.name = \"aes-ccm-192\",\n+\t\t.alg = RTE_CRYPTO_AEAD_AES_CCM,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 4,\n+\t\t.key_size = 28,\n+\t\t.digest_size = 16,\n+\t\t.aad_size = 8,\n+\t},\n+\n+\t[5] = {\n+\t\t.name = \"aes-ccm-256\",\n+\t\t.alg = RTE_CRYPTO_AEAD_AES_CCM,\n+\t\t.iv_size = 8,\n+\t\t.block_size = 4,\n+\t\t.key_size = 36,\n+\t\t.digest_size = 16,\n+\t\t.aad_size = 8,\n+\t},\n+\n+\t[6] = {\n+\t\t.name = \"chacha20-poly1305\",\n+\t\t.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,\n+\t\t.iv_size = 12,\n+\t\t.block_size = 64,\n+\t\t.key_size = 36,\n+\t\t.digest_size = 16,\n+\t\t.aad_size = 8,\n+\t},\n+};\n+\n+static struct cipher_alg *\n+cipher_alg_find(const char *name)\n+{\n+\tsize_t i;\n+\n+\tfor (i = 0; i < RTE_DIM(cipher_algs); i++) {\n+\t\tstruct cipher_alg *alg = &cipher_algs[i];\n+\n+\t\tif (!strcmp(name, alg->name))\n+\t\t\treturn alg;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static struct cipher_alg *\n+cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)\n+{\n+\tsize_t i;\n+\n+\tfor (i = 0; i < RTE_DIM(cipher_algs); i++) {\n+\t\tstruct cipher_alg *alg = &cipher_algs[i];\n+\n+\t\tif (alg->alg == alg_id && alg->key_size == key_size)\n+\t\t\treturn alg;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static struct auth_alg *\n+auth_alg_find(const char *name)\n+{\n+\tsize_t i;\n+\n+\tfor (i = 0; i < RTE_DIM(auth_algs); i++) {\n+\t\tstruct auth_alg *alg = &auth_algs[i];\n+\n+\t\tif (!strcmp(name, alg->name))\n+\t\t\treturn alg;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static struct auth_alg *\n+auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)\n+{\n+\tsize_t i;\n+\n+\tfor (i = 0; i < RTE_DIM(auth_algs); i++) {\n+\t\tstruct auth_alg *alg = &auth_algs[i];\n+\n+\t\tif (alg->alg == alg_id && alg->key_size == key_size)\n+\t\t\treturn alg;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static struct aead_alg *\n+aead_alg_find(const char *name)\n+{\n+\tsize_t i;\n+\n+\tfor (i = 0; i < RTE_DIM(aead_algs); i++) {\n+\t\tstruct aead_alg *alg = &aead_algs[i];\n+\n+\t\tif (!strcmp(name, alg->name))\n+\t\t\treturn alg;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static struct aead_alg *\n+aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)\n+{\n+\tsize_t i;\n+\n+\tfor (i = 0; i < RTE_DIM(aead_algs); i++) {\n+\t\tstruct aead_alg *alg = &aead_algs[i];\n+\n+\t\tif (alg->alg == alg_id && alg->key_size == key_size)\n+\t\t\treturn alg;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static int\n+char_to_hex(char c, uint8_t *val)\n+{\n+\tif (c >= '0' && c <= '9') {\n+\t\t*val = c - '0';\n+\t\treturn 0;\n+\t}\n+\n+\tif (c >= 'A' && c <= 'F') {\n+\t\t*val = c - 'A' + 10;\n+\t\treturn 0;\n+\t}\n+\n+\tif (c >= 'a' && c <= 'f') {\n+\t\t*val = c - 'a' + 10;\n+\t\treturn 0;\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+\n+static int\n+hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)\n+{\n+\tuint32_t i;\n+\n+\t/* Check input arguments. */\n+\tif (!src || !src[0] || !dst || !n_dst_bytes)\n+\t\treturn -EINVAL;\n+\n+\t/* Skip any leading \"0x\" or \"0X\" in the src string. */\n+\tif ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))\n+\t\tsrc += 2;\n+\n+\t/* Convert each group of two hex characters in the src string to one byte in dst array. */\n+\tfor (i = 0; i < n_dst_bytes; i++) {\n+\t\tuint8_t a, b;\n+\t\tint status;\n+\n+\t\tstatus = char_to_hex(*src, &a);\n+\t\tif (status)\n+\t\t\treturn status;\n+\t\tsrc++;\n+\n+\t\tstatus = char_to_hex(*src, &b);\n+\t\tif (status)\n+\t\t\treturn status;\n+\t\tsrc++;\n+\n+\t\tdst[i] = a * 16 + b;\n+\t}\n+\n+\t/* Check for the end of the src string. */\n+\tif (*src)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+static int\n+token_is_comment(const char *token)\n+{\n+\tif ((token[0] == '#') ||\n+\t    (token[0] == ';') ||\n+\t    ((token[0] == '/') && (token[1] == '/')))\n+\t\treturn 1; /* TRUE. */\n+\n+\treturn 0; /* FALSE. */\n+}\n+\n+#define MAX_TOKENS 64\n+\n+#define CHECK(condition, msg)          \\\n+do {                                   \\\n+\tif (!(condition)) {            \\\n+\t\tif (errmsg)            \\\n+\t\t\t*errmsg = msg; \\\n+\t\tgoto error;            \\\n+\t}                              \\\n+} while (0)\n+\n+struct rte_swx_ipsec_sa_params *\n+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,\n+\t\t      const char *string,\n+\t\t      int *is_blank_or_comment,\n+\t\t      const char **errmsg)\n+{\n+\tchar *token_array[MAX_TOKENS], **t;\n+\tstruct rte_swx_ipsec_sa_params *p = NULL;\n+\tchar *s0 = NULL, *s;\n+\tuint32_t n_tokens = 0;\n+\tint blank_or_comment = 0;\n+\n+\t/* Check input arguments. */\n+\tCHECK(string && string[0], \"NULL input\");\n+\n+\t/* Memory allocation. */\n+\ts0 = strdup(string);\n+\tp = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));\n+\tCHECK(s0 && p, \"Not enough memory\");\n+\n+\t/* Parse the string into tokens. */\n+\tfor (s = s0; ; ) {\n+\t\tchar *token;\n+\n+\t\ttoken = strtok_r(s, \" \\f\\n\\r\\t\\v\", &s);\n+\t\tif (!token || token_is_comment(token))\n+\t\t\tbreak;\n+\n+\t\tCHECK(n_tokens < RTE_DIM(token_array), \"Too many tokens\");\n+\n+\t\ttoken_array[n_tokens] = token;\n+\t\tn_tokens++;\n+\t}\n+\n+\tt = token_array;\n+\tif (!n_tokens) {\n+\t\tblank_or_comment = 1;\n+\t\tgoto error;\n+\t}\n+\n+\t/*\n+\t * Crypto operation.\n+\t */\n+\tif (!strcmp(t[0], \"encrypt\"))\n+\t\tp->encrypt = 1;\n+\telse if (!strcmp(t[0], \"decrypt\"))\n+\t\tp->encrypt = 0;\n+\telse\n+\t\tCHECK(0, \"Missing \\\"encrypt\\\"/\\\"decrypt\\\" keyword\");\n+\n+\tt++;\n+\tn_tokens--;\n+\n+\t/*\n+\t * Crypto parameters.\n+\t */\n+\tCHECK(n_tokens >= 2, \"Not enough tokens\");\n+\n+\tif (!strcmp(t[0], \"cipher\")) {\n+\t\tstruct cipher_alg *cipher_alg;\n+\t\tstruct auth_alg *auth_alg;\n+\t\tuint32_t key_size;\n+\n+\t\tp->crypto.is_aead = 0;\n+\n+\t\t/* cipher. */\n+\t\tcipher_alg = cipher_alg_find(t[1]);\n+\t\tCHECK(cipher_alg, \"Unsupported cipher algorithm\");\n+\n+\t\tkey_size = cipher_alg->key_size;\n+\t\tp->crypto.cipher_auth.cipher.alg = cipher_alg->alg;\n+\t\tp->crypto.cipher_auth.cipher.key_size = key_size;\n+\n+\t\tt += 2;\n+\t\tn_tokens -= 2;\n+\n+\t\tif (key_size) {\n+\t\t\tint status;\n+\n+\t\t\tCHECK(n_tokens >= 2, \"Not enough tokens\");\n+\t\t\tCHECK(!strcmp(t[0], \"key\"), \"Missing cipher \\\"key\\\" keyword\");\n+\t\t\tCHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),\n+\t\t\t\t\"Cipher algorithm key too big\");\n+\n+\t\t\tstatus = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);\n+\t\t\tCHECK(!status, \"Cipher key invalid format\");\n+\n+\t\t\tt += 2;\n+\t\t\tn_tokens -= 2;\n+\t\t}\n+\n+\t\t/* authentication. */\n+\t\tCHECK(n_tokens >= 2, \"Not enough tokens\");\n+\t\tCHECK(!strcmp(t[0], \"auth\"), \"Missing \\\"auth\\\" keyword\");\n+\n+\t\tauth_alg = auth_alg_find(t[1]);\n+\t\tCHECK(auth_alg, \"Unsupported authentication algorithm\");\n+\n+\t\tkey_size = auth_alg->key_size;\n+\t\tp->crypto.cipher_auth.auth.alg = auth_alg->alg;\n+\t\tp->crypto.cipher_auth.auth.key_size = key_size;\n+\n+\t\tt += 2;\n+\t\tn_tokens -= 2;\n+\n+\t\tif (key_size) {\n+\t\t\tint status;\n+\n+\t\t\tCHECK(n_tokens >= 2, \"Not enough tokens\");\n+\t\t\tCHECK(!strcmp(t[0], \"key\"), \"Missing authentication \\\"key\\\" keyword\");\n+\t\t\tCHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),\n+\t\t\t\t\"Authentication algorithm key too big\");\n+\n+\t\t\tstatus = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);\n+\t\t\tCHECK(!status, \"Authentication key invalid format\");\n+\n+\t\t\tt += 2;\n+\t\t\tn_tokens -= 2;\n+\t\t}\n+\t} else if (!strcmp(t[0], \"aead\")) {\n+\t\tstruct aead_alg *alg;\n+\t\tuint32_t key_size;\n+\t\tint status;\n+\n+\t\tp->crypto.is_aead = 1;\n+\n+\t\tCHECK(n_tokens >= 4, \"Not enough tokens\");\n+\t\talg = aead_alg_find(t[1]);\n+\t\tCHECK(alg, \"Unsupported AEAD algorithm\");\n+\n+\t\tkey_size = alg->key_size;\n+\t\tp->crypto.aead.alg = alg->alg;\n+\t\tp->crypto.aead.key_size = key_size;\n+\n+\t\tCHECK(!strcmp(t[2], \"key\"), \"Missing AEAD \\\"key\\\" keyword\");\n+\t\tCHECK(key_size <= RTE_DIM(p->crypto.aead.key),\n+\t\t\t\"AEAD algorithm key too big\");\n+\n+\t\tstatus = hex_string_parse(t[3], p->crypto.aead.key, key_size);\n+\t\tCHECK(!status, \"AEAD key invalid format\");\n+\n+\t\tt += 4;\n+\t\tn_tokens -= 4;\n+\t} else\n+\t\tCHECK(0, \"Missing \\\"cipher\\\"/\\\"aead\\\" keyword\");\n+\n+\t/*\n+\t * Packet ecapsulation parameters.\n+\t */\n+\tCHECK(n_tokens >= 4, \"Not enough tokens\");\n+\tCHECK(!strcmp(t[0], \"esp\"), \"Missing \\\"esp\\\" keyword\");\n+\tCHECK(!strcmp(t[1], \"spi\"), \"Missing \\\"spi\\\" keyword\");\n+\n+\tp->encap.esp.spi = strtoul(t[2], &t[2], 0);\n+\tCHECK(!t[2][0], \"ESP SPI field invalid format\");\n+\n+\tt += 3;\n+\tn_tokens -= 3;\n+\n+\tif (!strcmp(t[0], \"tunnel\")) {\n+\t\tp->encap.tunnel_mode = 1;\n+\n+\t\tCHECK(n_tokens >= 6, \"Not enough tokens\");\n+\n+\t\tif (!strcmp(t[1], \"ipv4\")) {\n+\t\t\tuint32_t addr;\n+\n+\t\t\tp->encap.tunnel_ipv4 = 1;\n+\n+\t\t\tCHECK(!strcmp(t[2], \"srcaddr\"), \"Missing \\\"srcaddr\\\" keyword\");\n+\n+\t\t\taddr = strtoul(t[3], &t[3], 0);\n+\t\t\tCHECK(!t[3][0], \"Tunnel IPv4 source address invalid format\");\n+\t\t\tp->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);\n+\n+\t\t\tCHECK(!strcmp(t[4], \"dstaddr\"), \"Missing \\\"dstaddr\\\" keyword\");\n+\n+\t\t\taddr = strtoul(t[5], &t[5], 0);\n+\t\t\tCHECK(!t[5][0], \"Tunnel IPv4 destination address invalid format\");\n+\t\t\tp->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);\n+\n+\t\t\tt += 6;\n+\t\t\tn_tokens -= 6;\n+\t\t} else if (!strcmp(t[1], \"ipv6\")) {\n+\t\t\tint status;\n+\n+\t\t\tp->encap.tunnel_ipv4 = 0;\n+\n+\t\t\tCHECK(!strcmp(t[2], \"srcaddr\"), \"Missing \\\"srcaddr\\\" keyword\");\n+\n+\t\t\tstatus = hex_string_parse(t[3],\n+\t\t\t\t\t\t  p->encap.tunnel.ipv6.src_addr.s6_addr,\n+\t\t\t\t\t\t  16);\n+\t\t\tCHECK(!status, \"Tunnel IPv6 source address invalid format\");\n+\n+\t\t\tCHECK(!strcmp(t[4], \"dstaddr\"), \"Missing \\\"dstaddr\\\" keyword\");\n+\n+\t\t\tstatus = hex_string_parse(t[5],\n+\t\t\t\t\t\t  p->encap.tunnel.ipv6.dst_addr.s6_addr,\n+\t\t\t\t\t\t  16);\n+\t\t\tCHECK(!status, \"Tunnel IPv6 destination address invalid format\");\n+\n+\t\t\tt += 6;\n+\t\t\tn_tokens -= 6;\n+\t\t} else\n+\t\t\tCHECK(0, \"Missing \\\"ipv4\\\"/\\\"ipv6\\\" keyword\");\n+\t} else if (!strcmp(t[0], \"transport\")) {\n+\t\tp->encap.tunnel_mode = 0;\n+\n+\t\tt++;\n+\t\tn_tokens--;\n+\t} else\n+\t\tCHECK(0, \"Missing \\\"tunnel\\\"/\\\"transport\\\" keyword\");\n+\n+\t/*\n+\t * Any other parameters.\n+\t */\n+\tCHECK(!n_tokens, \"Unexpected trailing tokens\");\n+\n+\tfree(s0);\n+\treturn p;\n+\n+error:\n+\tfree(p);\n+\tfree(s0);\n+\tif (is_blank_or_comment)\n+\t\t*is_blank_or_comment = blank_or_comment;\n+\treturn NULL;\n+}\n+\n+static void\n+tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)\n+{\n+\tstruct rte_ipv4_hdr ipv4_hdr = {\n+\t\t.version_ihl = 0x45,\n+\t\t.type_of_service = 0,\n+\t\t.total_length = 0, /* Cannot be pre-computed. */\n+\t\t.packet_id = 0,\n+\t\t.fragment_offset = 0,\n+\t\t.time_to_live = 64,\n+\t\t.next_proto_id = IPPROTO_ESP,\n+\t\t.hdr_checksum = 0, /* Cannot be pre-computed. */\n+\t\t.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,\n+\t\t.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,\n+\t};\n+\n+\tmemcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));\n+}\n+\n+static void\n+tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)\n+{\n+\tstruct rte_ipv6_hdr ipv6_hdr = {\n+\t\t.vtc_flow = 0x60000000,\n+\t\t.payload_len = 0, /* Cannot be pre-computed. */\n+\t\t.proto = IPPROTO_ESP,\n+\t\t.hop_limits = 64,\n+\t\t.src_addr = {0},\n+\t\t.dst_addr = {0},\n+\t};\n+\n+\tmemcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));\n+\tmemcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);\n+\tmemcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);\n+}\n+\n+/* IPsec library SA parameters. */\n+static struct rte_crypto_sym_xform *\n+crypto_xform_get(struct rte_swx_ipsec_sa_params *p,\n+\t\tstruct rte_crypto_sym_xform *xform,\n+\t\tuint32_t *salt_out)\n+{\n+\tif (p->crypto.is_aead) {\n+\t\tstruct aead_alg *alg;\n+\t\tuint32_t key_size, salt, iv_length;\n+\n+\t\talg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);\n+\t\tif (!alg)\n+\t\t\treturn NULL;\n+\n+\t\t/* salt and salt-related key size adjustment. */\n+\t\tkey_size = p->crypto.aead.key_size - 4;\n+\t\tmemcpy(&salt, &p->crypto.aead.key[key_size], 4);\n+\n+\t\t/* IV length. */\n+\t\tiv_length = 12;\n+\t\tif (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)\n+\t\t\tiv_length = 11 ;\n+\n+\t\t/* xform. */\n+\t\txform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;\n+\t\txform[0].aead.op = p->encrypt ?\n+\t\t\tRTE_CRYPTO_AEAD_OP_ENCRYPT :\n+\t\t\tRTE_CRYPTO_AEAD_OP_DECRYPT;\n+\t\txform[0].aead.algo = p->crypto.aead.alg;\n+\t\txform[0].aead.key.data = p->crypto.aead.key;\n+\t\txform[0].aead.key.length = key_size;\n+\t\txform[0].aead.iv.offset = IV_OFFSET;\n+\t\txform[0].aead.iv.length = iv_length;\n+\t\txform[0].aead.digest_length = alg->digest_size;\n+\t\txform[0].aead.aad_length = alg->aad_size;\n+\t\txform[0].next = NULL;\n+\n+\t\t*salt_out = salt;\n+\t\treturn &xform[0];\n+\t} else {\n+\t\tstruct cipher_alg *cipher_alg;\n+\t\tstruct auth_alg *auth_alg;\n+\t\tuint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;\n+\n+\t\tcipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,\n+\t\t\t\t\t\t   p->crypto.cipher_auth.cipher.key_size);\n+\t\tif (!cipher_alg)\n+\t\t\treturn NULL;\n+\n+\t\tauth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,\n+\t\t\t\t\t       p->crypto.cipher_auth.auth.key_size);\n+\t\tif (!auth_alg)\n+\t\t\treturn NULL;\n+\n+\t\t/* salt and salt-related key size adjustment. */\n+\t\tcipher_key_size = p->crypto.cipher_auth.cipher.key_size;\n+\t\tauth_key_size = p->crypto.cipher_auth.auth.key_size;\n+\n+\t\tswitch (p->crypto.cipher_auth.cipher.alg) {\n+\t\tcase RTE_CRYPTO_CIPHER_AES_CBC:\n+\t\tcase RTE_CRYPTO_CIPHER_3DES_CBC:\n+\t\t\tsalt = (uint32_t)rand();\n+\t\t\tbreak;\n+\n+\t\tcase RTE_CRYPTO_CIPHER_AES_CTR:\n+\t\t\tcipher_key_size -= 4;\n+\t\t\tmemcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tsalt = 0;\n+\t\t}\n+\n+\t\tif (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {\n+\t\t\tauth_key_size -= 4;\n+\t\t\tmemcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);\n+\t\t}\n+\n+\t\t/* IV length. */\n+\t\tauth_iv_length = cipher_alg->iv_size;\n+\t\tif (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)\n+\t\t\tauth_iv_length = 12;\n+\n+\t\t/* xform. */\n+\t\tif (p->encrypt) {\n+\t\t\txform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;\n+\t\t\txform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;\n+\t\t\txform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;\n+\t\t\txform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;\n+\t\t\txform[0].cipher.key.length = cipher_key_size;\n+\t\t\txform[0].cipher.iv.offset = IV_OFFSET;\n+\t\t\txform[0].cipher.iv.length = cipher_alg->iv_size;\n+\t\t\txform[0].cipher.dataunit_len = 0;\n+\t\t\txform[0].next = &xform[1];\n+\n+\t\t\txform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;\n+\t\t\txform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;\n+\t\t\txform[1].auth.algo = p->crypto.cipher_auth.auth.alg;\n+\t\t\txform[1].auth.key.data = p->crypto.cipher_auth.auth.key;\n+\t\t\txform[1].auth.key.length = auth_key_size;\n+\t\t\txform[1].auth.iv.offset = IV_OFFSET;\n+\t\t\txform[1].auth.iv.length = auth_iv_length;\n+\t\t\txform[1].auth.digest_length = auth_alg->digest_size;\n+\t\t\txform[1].next = NULL;\n+\t\t} else {\n+\t\t\txform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;\n+\t\t\txform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;\n+\t\t\txform[0].auth.algo = p->crypto.cipher_auth.auth.alg;\n+\t\t\txform[0].auth.key.data = p->crypto.cipher_auth.auth.key;\n+\t\t\txform[0].auth.key.length = auth_key_size;\n+\t\t\txform[0].auth.iv.offset = IV_OFFSET;\n+\t\t\txform[0].auth.iv.length = auth_iv_length;\n+\t\t\txform[0].auth.digest_length = auth_alg->digest_size;\n+\t\t\txform[0].next = &xform[1];\n+\n+\t\t\txform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;\n+\t\t\txform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;\n+\t\t\txform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;\n+\t\t\txform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;\n+\t\t\txform[1].cipher.key.length = cipher_key_size;\n+\t\t\txform[1].cipher.iv.offset = IV_OFFSET;\n+\t\t\txform[1].cipher.iv.length = cipher_alg->iv_size;\n+\t\t\txform[1].cipher.dataunit_len = 0;\n+\t\t\txform[1].next = NULL;\n+\t\t}\n+\n+\t\t*salt_out = salt;\n+\n+\t\tif (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {\n+\t\t\tif (p->encrypt)\n+\t\t\t\treturn &xform[1];\n+\n+\t\t\txform[0].next = NULL;\n+\t\t\treturn &xform[0];\n+\t\t}\n+\n+\t\treturn &xform[0];\n+\t}\n+}\n+\n+static void\n+ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,\n+\t\tstruct rte_security_ipsec_xform *ipsec_xform,\n+\t\tuint32_t salt)\n+{\n+\tipsec_xform->spi = p->encap.esp.spi;\n+\n+\tipsec_xform->salt = salt;\n+\n+\tipsec_xform->options.esn = 0;\n+\tipsec_xform->options.udp_encap = 0;\n+\tipsec_xform->options.copy_dscp = 1;\n+\tipsec_xform->options.copy_flabel = 0;\n+\tipsec_xform->options.copy_df = 0;\n+\tipsec_xform->options.dec_ttl = 0;\n+\tipsec_xform->options.ecn = 1;\n+\tipsec_xform->options.stats = 0;\n+\tipsec_xform->options.iv_gen_disable = 0;\n+\tipsec_xform->options.tunnel_hdr_verify = 0;\n+\tipsec_xform->options.udp_ports_verify = 0;\n+\tipsec_xform->options.ip_csum_enable = 0;\n+\tipsec_xform->options.l4_csum_enable = 0;\n+\tipsec_xform->options.ip_reassembly_en = 0;\n+\tipsec_xform->options.reserved_opts = 0;\n+\n+\tipsec_xform->direction = p->encrypt ?\n+\t\tRTE_SECURITY_IPSEC_SA_DIR_EGRESS :\n+\t\tRTE_SECURITY_IPSEC_SA_DIR_INGRESS;\n+\n+\tipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;\n+\n+\tipsec_xform->mode = p->encap.tunnel_mode ?\n+\t\tRTE_SECURITY_IPSEC_SA_MODE_TUNNEL :\n+\t\tRTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;\n+\n+\tipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?\n+\t\tRTE_SECURITY_IPSEC_TUNNEL_IPV4 :\n+\t\tRTE_SECURITY_IPSEC_TUNNEL_IPV6;\n+\n+\tif (p->encap.tunnel_mode) {\n+\t\tif (p->encap.tunnel_ipv4) {\n+\t\t\tipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;\n+\t\t\tipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;\n+\t\t\tipsec_xform->tunnel.ipv4.dscp = 0;\n+\t\t\tipsec_xform->tunnel.ipv4.df = 0;\n+\t\t\tipsec_xform->tunnel.ipv4.ttl = 64;\n+\t\t} else {\n+\t\t\tipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;\n+\t\t\tipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;\n+\t\t\tipsec_xform->tunnel.ipv6.dscp = 0;\n+\t\t\tipsec_xform->tunnel.ipv6.flabel = 0;\n+\t\t\tipsec_xform->tunnel.ipv6.hlimit = 64;\n+\t\t}\n+\t}\n+\n+\tipsec_xform->life.packets_soft_limit = 0;\n+\tipsec_xform->life.bytes_soft_limit = 0;\n+\tipsec_xform->life.packets_hard_limit = 0;\n+\tipsec_xform->life.bytes_hard_limit = 0;\n+\n+\tipsec_xform->replay_win_sz = 0;\n+\n+\tipsec_xform->esn.value = 0;\n+\n+\tipsec_xform->udp.dport = 0;\n+\tipsec_xform->udp.sport = 0;\n+}\n+\n+static int\n+ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,\n+\t\t struct rte_ipsec_sa_prm *sa_prm,\n+\t\t struct rte_ipv4_hdr *ipv4_hdr,\n+\t\t struct rte_ipv6_hdr *ipv6_hdr,\n+\t\t struct rte_crypto_sym_xform *crypto_xform)\n+{\n+\tuint32_t salt;\n+\n+\tmemset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */\n+\n+\tsa_prm->userdata = 0; /* Not used. */\n+\n+\tsa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */\n+\n+\t/*\n+\t * crypto_xform.\n+\t */\n+\tsa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);\n+\tif (!sa_prm->crypto_xform)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * ipsec_xform.\n+\t */\n+\tipsec_xform_get(p, &sa_prm->ipsec_xform, salt);\n+\n+\t/*\n+\t * tunnel / transport.\n+\t *\n+\t * Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,\n+\t * the input packet type should be added to the SA configuration parameters.\n+\t */\n+\tif (p->encap.tunnel_mode) {\n+\t\tif (p->encap.tunnel_ipv4) {\n+\t\t\tsa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);\n+\t\t\tsa_prm->tun.hdr_l3_off = 0;\n+\t\t\tsa_prm->tun.next_proto = IPPROTO_IPIP; /* Assume input IP packet type as IPv4. */\n+\t\t\tsa_prm->tun.hdr = ipv4_hdr;\n+\t\t} else {\n+\t\t\tsa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);\n+\t\t\tsa_prm->tun.hdr_l3_off = 0;\n+\t\t\tsa_prm->tun.next_proto = IPPROTO_IPIP; /* Assume input IP packet type as IPv4. */\n+\t\t\tsa_prm->tun.hdr = ipv6_hdr;\n+\t\t}\n+\t} else {\n+\t\tsa_prm->trs.proto = IPPROTO_IPIP; /* Assume input IP packet type as IPv4. */\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+ipsec_session_create(struct rte_swx_ipsec *ipsec,\n+\t\t     struct rte_swx_ipsec_sa_params *p,\n+\t\t     struct rte_ipsec_session *s)\n+{\n+\tstruct rte_ipv4_hdr ipv4_hdr;\n+\tstruct rte_ipv6_hdr ipv6_hdr;\n+\tstruct rte_crypto_sym_xform crypto_xform[2];\n+\tstruct rte_ipsec_sa_prm sa_prm;\n+\tstruct rte_ipsec_sa *sa = NULL;\n+\tstruct rte_cryptodev_sym_session *crypto_session = NULL;\n+\tint sa_size;\n+\tint sa_valid = 0, crypto_session_valid = 0, status = 0;\n+\n+\ttunnel_ipv4_header_set(&ipv4_hdr, p);\n+\ttunnel_ipv6_header_set(&ipv6_hdr, p);\n+\n+\t/* IPsec library SA setup. */\n+\tstatus = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tsa_size = rte_ipsec_sa_size(&sa_prm);\n+\tif (sa_size < 0) {\n+\t\tstatus = sa_size;\n+\t\tgoto error;\n+\t}\n+\tif (!sa_size) {\n+\t\tstatus = -EINVAL;\n+\t\tgoto error;\n+\t}\n+\n+\tsa = calloc(1, sa_size);\n+\tif (!sa) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto error;\n+\t}\n+\n+\tsa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);\n+\tif (sa_size < 0) {\n+\t\tstatus = sa_size;\n+\t\tgoto error;\n+\t}\n+\tif (!sa_size) {\n+\t\tstatus = -EINVAL;\n+\t\tgoto error;\n+\t}\n+\n+\tsa_valid = 1;\n+\n+\t/* Cryptodev library session setup. */\n+\tcrypto_session = rte_cryptodev_sym_session_create(ipsec->mp_session);\n+\tif (!crypto_session) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto error;\n+\t}\n+\n+\tstatus = rte_cryptodev_sym_session_init(ipsec->dev_id,\n+\t\t\t\t\t\tcrypto_session,\n+\t\t\t\t\t\tsa_prm.crypto_xform,\n+\t\t\t\t\t\tipsec->mp_session_priv);\n+\tif (status)\n+\t\tgoto error;\n+\n+\tcrypto_session_valid = 1;\n+\n+\t/* IPsec library session setup. */\n+\ts->sa = sa;\n+\ts->type = RTE_SECURITY_ACTION_TYPE_NONE;\n+\ts->crypto.ses = crypto_session;\n+\ts->crypto.dev_id = ipsec->dev_id;\n+\ts->pkt_func.prepare.async = NULL;\n+\ts->pkt_func.process = NULL;\n+\n+\treturn rte_ipsec_session_prepare(s);\n+\n+error:\n+\t/* sa. */\n+\tif (sa_valid)\n+\t\trte_ipsec_sa_fini(sa);\n+\n+\tfree(sa);\n+\n+\t/* crypto_session. */\n+\tif (crypto_session_valid)\n+\t\trte_cryptodev_sym_session_clear(ipsec->dev_id, crypto_session);\n+\n+\tif (crypto_session)\n+\t\trte_cryptodev_sym_session_free(crypto_session);\n+\n+\t/* s. */\n+\tmemset(s, 0, sizeof(*s));\n+\n+\treturn status;\n+}\n+\n+static void\n+ipsec_session_free(struct rte_swx_ipsec *ipsec,\n+\t\t   struct rte_ipsec_session *s)\n+{\n+\tif (!s)\n+\t\treturn;\n+\n+\t/* IPsec library SA. */\n+\tif (s->sa)\n+\t\trte_ipsec_sa_fini(s->sa);\n+\tfree(s->sa);\n+\n+\t/* Cryptodev library session. */\n+\tif (s->crypto.ses) {\n+\t\trte_cryptodev_sym_session_clear(ipsec->dev_id, s->crypto.ses);\n+\t\trte_cryptodev_sym_session_free(s->crypto.ses);\n+\t}\n+\n+\t/* IPsec library session. */\n+\tmemset(s, 0, sizeof(*s));\n+}\n+\n+int\n+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,\n+\t\t     struct rte_swx_ipsec_sa_params *sa_params,\n+\t\t     uint32_t *id)\n+{\n+\tstruct ipsec_sa *sa;\n+\tuint32_t sa_id;\n+\tint status;\n+\n+\t/* Check the input parameters. */\n+\tif (!ipsec || !sa_params || !id)\n+\t\treturn -EINVAL;\n+\n+\t/* Allocate a free SADB entry. */\n+\tif (!ipsec->n_sa_free_id)\n+\t\treturn -ENOSPC;\n+\n+\tsa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];\n+\tipsec->n_sa_free_id--;\n+\n+\t/* Aquire the SA resources. */\n+\tsa = ipsec_sa_get(ipsec, sa_id);\n+\n+\tstatus = ipsec_session_create(ipsec, sa_params, &sa->s);\n+\tif (status) {\n+\t\t/* Free the allocated SADB entry. */\n+\t\tipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;\n+\t\tipsec->n_sa_free_id++;\n+\n+\t\treturn status;\n+\t}\n+\n+\t/* Validate the new SA. */\n+\tsa->valid = 1;\n+\t*id = sa_id;\n+\n+\treturn 0;\n+}\n+\n+void\n+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,\n+\t\t\tuint32_t sa_id)\n+{\n+\tstruct ipsec_sa *sa;\n+\n+\t/* Check the input parameters. */\n+\tif (!ipsec || (sa_id >= ipsec->n_sa_max))\n+\t\treturn;\n+\n+\t/* Release the SA resources. */\n+\tsa = ipsec_sa_get(ipsec, sa_id);\n+\n+\tipsec_session_free(ipsec, &sa->s);\n+\n+\t/* Free the SADB entry. */\n+\tipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;\n+\tipsec->n_sa_free_id++;\n+\n+\t/* Invalidate the SA. */\n+\tsa->valid = 0;\n+}\ndiff --git a/lib/pipeline/rte_swx_ipsec.h b/lib/pipeline/rte_swx_ipsec.h\nnew file mode 100644\nindex 0000000000..c2c638a5d7\n--- /dev/null\n+++ b/lib/pipeline/rte_swx_ipsec.h\n@@ -0,0 +1,383 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2022 Intel Corporation\n+ */\n+#ifndef __INCLUDE_RTE_SWX_IPSEC_H__\n+#define __INCLUDE_RTE_SWX_IPSEC_H__\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * @file\n+ * RTE SWX Internet Protocol Security (IPsec)\n+ *\n+ * This block is used to provide IPsec support to the SWX pipeline. The block is external to the\n+ * pipeline, hence it needs to be explicitly instantiated by the user and connected to a pipeline\n+ * instance using the pipeline I/O ports.\n+ *\n+ * Main features:\n+ * - IPsec inbound (encrypted input packets -> clear text output packets) and outbound (clear text\n+ *   input packets -> encrypted output packets) processing support for tunnel and transport modes.\n+ *\n+ * Security Association (SA):\n+ * - Each IPsec block instance has its own set of SAs used to process the input packets. Each SA is\n+ *   identified by its unique SA ID. The IPsec inbound and outbound SAs share the same ID space.\n+ * - Each input packet is first mapped to one of the existing SAs by using the SA ID and then\n+ *   processed according to the identified SA. The SA ID is read from input packet. The SA ID field\n+ *   is typically written by the pipeline before sending the packet to the IPsec block.\n+ *\n+ * Packet format:\n+ * - IPsec block input packet (i.e. pipeline output packet):\n+ * \t- IPsec block meta-data header: @see struct rte_swx_ipsec_input_packet_metadata.\n+ * \t- IPv4 header.\n+ * \t- IPv4 payload: on the inbound path, it includes the encrypted ESP packet.\n+ * - IPsec block output packet (i.e. pipeline input packet):\n+ * \t- IPv4 header.\n+ * \t- IPv4 payload: on the outbound path, it includes the encrypted ESP packet.\n+ *\n+ * SA update procedure:\n+ * - To add a new SA, @see function rte_swx_ipsec_sa_add().\n+ * - To delete an existing SA, @see function rte_swx_ipsec_sa_delete().\n+ * - To update an existing SA, the control plane has to follow the following steps:\n+ *   1. Add a new SA with potentially a different set of configuration parameters. This step can\n+ *      fail, for example when the SA table is full.\n+ *   2. Wait until no more packets are using the old SA.\n+ *   3. Delete the old SA.\n+ */\n+\n+#include <stdint.h>\n+#include <stdio.h>\n+#include <netinet/in.h>\n+\n+#include <rte_compat.h>\n+#include <rte_crypto_sym.h>\n+\n+/**\n+ * IPsec Setup API\n+ */\n+\n+/** IPsec instance opaque data structure. */\n+struct rte_swx_ipsec;\n+\n+/** Name size. */\n+#ifndef RTE_SWX_IPSEC_NAME_SIZE\n+#define RTE_SWX_IPSEC_NAME_SIZE 64\n+#endif\n+\n+/** Maximum burst size. */\n+#ifndef RTE_SWX_IPSEC_BURST_SIZE_MAX\n+#define RTE_SWX_IPSEC_BURST_SIZE_MAX 256\n+#endif\n+\n+/** IPsec burst sizes. */\n+struct rte_swx_ipsec_burst_size {\n+\t/** Input ring read burst size. */\n+\tuint32_t ring_rd;\n+\n+\t/** Output ring write burst size. */\n+\tuint32_t ring_wr;\n+\n+\t/** Crypto device request queue write burst size. */\n+\tuint32_t crypto_wr;\n+\n+\t/** Crypto device response queue read burst size. */\n+\tuint32_t crypto_rd;\n+};\n+\n+/**\n+ * IPsec instance configuration parameters\n+ *\n+ */\n+struct rte_swx_ipsec_params {\n+\t/** Input packet queue. */\n+\tconst char *ring_in_name;\n+\n+\t/** Output packet queue.  */\n+\tconst char *ring_out_name;\n+\n+\t/** Crypto device name. */\n+\tconst char *crypto_dev_name;\n+\n+\t/** Crypto device queue pair ID. */\n+\tuint32_t crypto_dev_queue_pair_id;\n+\n+\t/** Burst size. */\n+\tstruct rte_swx_ipsec_burst_size bsz;\n+\n+\t/** Maximum number of SAs. */\n+\tuint32_t n_sa_max;\n+};\n+\n+/**\n+ * IPsec input packet meta-data\n+ *\n+ */\n+struct rte_swx_ipsec_input_packet_metadata {\n+\t/* SA ID. */\n+\tuint32_t sa_id;\n+};\n+\n+/**\n+ * IPsec instance find\n+ *\n+ * @param[in] name\n+ *   IPsec instance name.\n+ * @return\n+ *   Valid IPsec instance handle if found or NULL otherwise.\n+ */\n+__rte_experimental\n+struct rte_swx_ipsec *\n+rte_swx_ipsec_find(const char *name);\n+\n+/**\n+ * IPsec instance create\n+ *\n+ * @param[out] ipsec\n+ *   IPsec instance handle. Must point to valid memory. Contains valid pipeline handle once this\n+ *   function returns successfully.\n+ * @param[in] name\n+ *   IPsec instance unique name.\n+ * @param[in] params\n+ *   IPsec instance configuration parameters.\n+ * @param[in] numa_node\n+ *   Non-Uniform Memory Access (NUMA) node.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough space/cannot allocate memory;\n+ *   -EEXIST: Pipeline with this name already exists.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec,\n+\t\t     const char *name,\n+\t\t     struct rte_swx_ipsec_params *params,\n+\t\t     int numa_node);\n+\n+/**\n+ * IPsec instance free\n+ *\n+ * @param[in] ipsec\n+ *   IPsec instance handle.\n+ */\n+__rte_experimental\n+void\n+rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec);\n+\n+/**\n+ * IPsec Data Plane API\n+ */\n+\n+/**\n+ * IPsec instance run\n+ *\n+ * @param[in] ipsec\n+ *   IPsec instance handle.\n+ */\n+__rte_experimental\n+void\n+rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec);\n+\n+/*\n+ * IPsec Control Plane API\n+ */\n+\n+/** Maximum key size in bytes. */\n+#define RTE_SWX_IPSEC_KEY_SIZE_MAX 64\n+\n+/** IPsec SA crypto cipher parameters. */\n+struct rte_swx_ipsec_sa_cipher_params {\n+\t/** Cipher algorithm. */\n+\tenum rte_crypto_cipher_algorithm alg;\n+\n+\t/** Cipher key. */\n+\tuint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];\n+\n+\t/** Cipher key size in bytes. */\n+\tuint32_t key_size;\n+};\n+\n+/** IPsec SA crypto authentication parameters. */\n+struct rte_swx_ipsec_sa_authentication_params {\n+\t/** Authentication algorithm. */\n+\tenum rte_crypto_auth_algorithm alg;\n+\n+\t/** Authentication key. */\n+\tuint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];\n+\n+\t/** Authentication key size in bytes. */\n+\tuint32_t key_size;\n+};\n+\n+/** IPsec SA crypto Authenticated Encryption with Associated Data (AEAD) parameters. */\n+struct rte_swx_ipsec_sa_aead_params {\n+\t/** AEAD algorithm. */\n+\tenum rte_crypto_aead_algorithm alg;\n+\n+\t/** AEAD key. */\n+\tuint8_t key[RTE_SWX_IPSEC_KEY_SIZE_MAX];\n+\n+\t/** AEAD key size in bytes. */\n+\tuint32_t key_size;\n+};\n+\n+/** IPsec protocol encapsulation parameters. */\n+struct rte_swx_ipsec_sa_encap_params {\n+\t/** Encapsulating Security Payload (ESP) header. */\n+\tstruct {\n+\t\t/** Security Parameters Index (SPI) field. */\n+\t\tuint32_t spi;\n+\t} esp;\n+\n+\t/** Tunnel mode when non-zero, transport mode when zero. */\n+\tint tunnel_mode;\n+\n+\t/** Tunnel type: Non-zero for IPv4, zero for IPv6. Valid for tunnel mode only. */\n+\tint tunnel_ipv4;\n+\n+\t/** Tunnel parameters. Valid for tunnel mode only. */\n+\tunion {\n+\t\t/** IPv4 header. */\n+\t\tstruct {\n+\t\t\t/** Source address. */\n+\t\t\tstruct in_addr src_addr;\n+\n+\t\t\t/** Destination address. */\n+\t\t\tstruct in_addr dst_addr;\n+\t\t} ipv4;\n+\n+\t\t/** IPv6 header. */\n+\t\tstruct {\n+\t\t\t/** Source address. */\n+\t\t\tstruct in6_addr src_addr;\n+\n+\t\t\t/** Destination address. */\n+\t\t\tstruct in6_addr dst_addr;\n+\t\t} ipv6;\n+\t} tunnel;\n+};\n+\n+/** IPsec Security Association (SA) parameters. */\n+struct rte_swx_ipsec_sa_params {\n+\t/** Crypto operation: encrypt when non-zero, decrypt when zero. */\n+\tint encrypt;\n+\n+\t/** Crypto operation parameters. */\n+\tstruct {\n+\t\tRTE_STD_C11\n+\t\tunion {\n+\t\t\tstruct {\n+\t\t\t\t/** Crypto cipher operation parameters. */\n+\t\t\t\tstruct rte_swx_ipsec_sa_cipher_params cipher;\n+\n+\t\t\t\t/** Crypto authentication operation parameters. */\n+\t\t\t\tstruct rte_swx_ipsec_sa_authentication_params auth;\n+\t\t\t} cipher_auth;\n+\n+\t\t\t/** Crypto AEAD operation parameters. */\n+\t\t\tstruct rte_swx_ipsec_sa_aead_params aead;\n+\t\t};\n+\n+\t\t/** Non-zero for AEAD, zero for cipher & authentication. */\n+\t\tint is_aead;\n+\t} crypto;\n+\n+\t/** Packet encasulation parameters. */\n+\tstruct rte_swx_ipsec_sa_encap_params encap;\n+};\n+\n+/**\n+ * IPsec SA add\n+ *\n+ * @param[in] ipsec\n+ *   IPsec instance handle.\n+ * @param[in] sa_params\n+ *   SA parameters.\n+ * @params[out] sa_id.\n+ *   On success, the SA ID.\n+ * @return\n+ *   0 on success or error code otherwise.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,\n+\t\t     struct rte_swx_ipsec_sa_params *sa_params,\n+\t\t     uint32_t *sa_id);\n+\n+/**\n+ * IPsec SA delete\n+ *\n+ * It is the responibility of the Control Plane to make sure the SA to be deleted is no longer used\n+ * by the Data Plane.\n+ *\n+ * @param[in] ipsec\n+ *   IPsec instance handle.\n+ * @params[in] sa_id.\n+ *   The SA ID.\n+ */\n+__rte_experimental\n+void\n+rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,\n+\t\t\tuint32_t sa_id);\n+\n+/**\n+ * IPsec SA read from string\n+ *\n+ * IPsec SA syntax:\n+ *\n+ * <sa>\n+ *    : encrypt <crypto_params> <encap_params>\n+ *    | decrypt <crypto_params> <encap_params>\n+ *    ;\n+ *\n+ * <crypto_params>\n+ *    : <cipher> <auth>\n+ *    | <aead>\n+ *    ;\n+ *\n+ * <cipher>\n+ *    : cipher <ciher_alg> key <cipher_key>\n+ *    | cipher <cipher_alg>\n+ *    ;\n+ *\n+ * <auth>\n+ *    : auth <authentication_alg> key <authentication_key>\n+ *    | auth <authentication_alg>\n+ *    ;\n+ *\n+ * <aead>\n+ *    : aead <aead_alg> key <aead_key>\n+ *    ;\n+ *\n+ * <encap_params>\n+ *    : esp spi <spi> tunnel ipv4 srcaddr <ipv4_src_addr> dstaddr <ipv4_dst_addr>\n+ *    | esp spi <spi> tunnel ipv6 srcaddr <ipv6_src_addr> dstaddr <ipv6_dst_addr>\n+ *    | esp spi <spi> transport\n+ *    ;\n+ *\n+ * @param[in] ipsec\n+ *   IPsec instance handle.\n+ * @param[in] string\n+ *   String containing the SA.\n+ * @param[inout] is_blank_or_comment\n+ *   On error, when its input value is not NULL, this argument is set to a non-zero value when\n+ *   *string* contains a blank or comment line and to zero otherwise.\n+ * @param[inout] errmsg\n+ *   On error, when its input value is not NULL, this argument points to a string with details on\n+ *   the detected error.\n+ * @return\n+ *   Pointer to valid IPsec SA parameters data structure on success or NULL on error.\n+ */\n+__rte_experimental\n+struct rte_swx_ipsec_sa_params *\n+rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec,\n+\t\t      const char *string,\n+\t\t      int *is_blank_or_comment,\n+\t\t      const char **errmsg);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif\ndiff --git a/lib/pipeline/version.map b/lib/pipeline/version.map\nindex 184f45a6b7..58e34459c3 100644\n--- a/lib/pipeline/version.map\n+++ b/lib/pipeline/version.map\n@@ -155,4 +155,13 @@ EXPERIMENTAL {\n \trte_swx_pipeline_build_from_lib;\n \trte_swx_pipeline_codegen;\n \trte_swx_pipeline_find;\n+\n+\t#added in 23.03\n+\trte_swx_ipsec_create;\n+\trte_swx_ipsec_find;\n+\trte_swx_ipsec_free;\n+\trte_swx_ipsec_run;\n+\trte_swx_ipsec_sa_add;\n+\trte_swx_ipsec_sa_delete;\n+\trte_swx_ipsec_sa_read;\n };\n",
    "prefixes": [
        "01/11"
    ]
}