All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jerin Jacob <jerin.jacob@caviumnetworks.com>
To: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
Cc: dev@dpdk.org
Subject: Re: [PATCH] example/ipsec-secgw: ipsec security gateway
Date: Sun, 31 Jan 2016 20:09:25 +0530	[thread overview]
Message-ID: <20160131143918.GA12763@localhost.localdomain> (raw)
In-Reply-To: <1454099352-29040-1-git-send-email-sergio.gonzalez.monroy@intel.com>

On Fri, Jan 29, 2016 at 08:29:12PM +0000, Sergio Gonzalez Monroy wrote:
> Sample app implementing an IPsec Security Geteway.
> The main goal of this app is to show the use of cryptodev framework
> in a real world application.
> 
> Currently only supported static IPv4 IPsec tunnels using AES-CBC
> and HMAC-SHA1.
> 
> Also, currently not supported:
> - SA auto negotiation (No IKE support)
> - chained mbufs
> 
> Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>

Hi Sergio,

Great effort, one of the best highly integrated sample DPDK application
(ethernet, acl, crypto and lpm)

> ---
>  MAINTAINERS                              |    4 +
>  doc/guides/sample_app_ug/index.rst       |    1 +
>  doc/guides/sample_app_ug/ipsec_secgw.rst |  440 +++++++++++
>  examples/Makefile                        |    2 +
>  examples/ipsec-secgw/Makefile            |   58 ++
>  examples/ipsec-secgw/esp.c               |  256 +++++++
>  examples/ipsec-secgw/esp.h               |   66 ++
>  examples/ipsec-secgw/ipip.h              |  100 +++
>  examples/ipsec-secgw/ipsec-secgw.c       | 1218 ++++++++++++++++++++++++++++++
>  examples/ipsec-secgw/ipsec.c             |  138 ++++
>  examples/ipsec-secgw/ipsec.h             |  184 +++++
>  examples/ipsec-secgw/rt.c                |  131 ++++
>  examples/ipsec-secgw/sa.c                |  391 ++++++++++
>  examples/ipsec-secgw/sp.c                |  324 ++++++++
>  14 files changed, 3313 insertions(+)
>  create mode 100644 doc/guides/sample_app_ug/ipsec_secgw.rst
>  create mode 100644 examples/ipsec-secgw/Makefile
>  create mode 100644 examples/ipsec-secgw/esp.c
>  create mode 100644 examples/ipsec-secgw/esp.h
>  create mode 100644 examples/ipsec-secgw/ipip.h
>  create mode 100644 examples/ipsec-secgw/ipsec-secgw.c
>  create mode 100644 examples/ipsec-secgw/ipsec.c
>  create mode 100644 examples/ipsec-secgw/ipsec.h
>  create mode 100644 examples/ipsec-secgw/rt.c
>  create mode 100644 examples/ipsec-secgw/sa.c
>  create mode 100644 examples/ipsec-secgw/sp.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b90aeea..996eda6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -596,3 +596,7 @@ F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
>  M: Pablo de Lara <pablo.de.lara.guarch@intel.com>
>  M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
>  F: examples/ptpclient/
> +
> +M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
> +F: doc/guides/sample_app_ug/ipsec-secgw.rst
> +F: examples/ipsec-secgw/
> diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
> index 8a646dd..88375d2 100644
> --- a/doc/guides/sample_app_ug/index.rst
> +++ b/doc/guides/sample_app_ug/index.rst
> @@ -73,6 +73,7 @@ Sample Applications User Guide
>      proc_info
>      ptpclient
>      performance_thread
> +    ipsec_secgw
>  
>  **Figures**
>  
> diff --git a/doc/guides/sample_app_ug/ipsec_secgw.rst b/doc/guides/sample_app_ug/ipsec_secgw.rst
> new file mode 100644
> index 0000000..7be3f7e
> --- /dev/null
> +++ b/doc/guides/sample_app_ug/ipsec_secgw.rst
> @@ -0,0 +1,440 @@
> +..  BSD LICENSE
> +    Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> +    All rights reserved.
> +
> +    Redistribution and use in source and binary forms, with or without
> +    modification, are permitted provided that the following conditions
> +    are met:
> +
> +    * Redistributions of source code must retain the above copyright
> +    notice, this list of conditions and the following disclaimer.
> +    * Redistributions in binary form must reproduce the above copyright
> +    notice, this list of conditions and the following disclaimer in
> +    the documentation and/or other materials provided with the
> +    distribution.
> +    * Neither the name of Intel Corporation nor the names of its
> +    contributors may be used to endorse or promote products derived
> +    from this software without specific prior written permission.
> +
> +    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> +    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> +    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> +    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> +    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> +    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> +    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> +    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> +    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> +    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +
> +IPsec Security Gateway Sample Application
> +=========================================
> +
> +The IPsec Security Gateway application is a minimal example of real work
> +application using DPDK cryptodev framework.
> +
> +Overview
> +--------
> +
> +The application demonstrates the implementation of a minimal Security Gateway
> +(see Constraints bellow) using DPDK based on RFC4301, RFC4303, RFC3602 and
> +RFC2404.
> +
> +Note that IKE is not supported (therefore not IPsec compliant), so only manual
> +setting of Security Policies and Associations is allowed.
> +
> +The Security Policies (SP) are implemented as ACL rules, the Security
> +Associations (SA) are stored in a table and the Routing is implemented
> +using LPM.
> +
> +The application splits the ports between Protected and Unprotected.
> +Based on the port the trafic is coming from, the traffic would be consider
> +IPsec Inbound or Outbound.
> +Traffic from Unprotected ports is consider IPsec Inbound, and traffic from
> +Protected ports is consider IPsec Outbound.
> +
> +Path for IPsec Inbound traffic:
> +*  Read packets from the port
> +*  Classify packets between IPv4 and ESP.
> +*  Inbound SA lookup for ESP packets based on their SPI
> +*  Verification/Decryption
> +*  Removal of ESP and outter IP header
> +*  Inbound SP check using ACL of decrypted packets and any other IPv4 packet
> +we read.
> +*  Routing
> +*  Write packet to port
> +
> +Path for IPsec Outbound traffic:
> +*  Read packets from the port
> +*  Outbound SP check using ACL of all IPv4 traffic
> +*  Outbound SA lookup for packets that need IPsec protection
> +*  Add ESP and outter IP header
> +*  Encryption/Digest
> +*  Routing
> +*  Write packet to port
> +
> +Constraints
> +-----------
> +*  IPv4 traffic
> +*  ESP tunnel mode
> +*  EAS-CBC and HMAC-SHA1
> +*  Each SA must be handle by a unique lcore (1 RX queue per port)
> +*  No chained mbufs
> +
> +Compiling the Application
> +-------------------------
> +
> +To compile the application:
> +
> +#. Go to the sample application directory:
> +
> +   .. code-block:: console
> +
> +      export RTE_SDK=/path/to/rte_sdk
> +      cd ${RTE_SDK}/examples/ipsec-secgw
> +
> +#. Set the target (a default target is used if not specified). For example:
> +
> +   .. code-block:: console
> +
> +      export RTE_TARGET=x86_64-native-linuxapp-gcc
> +
> +    See the *DPDK Getting Started Guide* for possible RTE_TARGET values.
> +
> +#. Build the application:
> +
> +   .. code-block:: console
> +
> +       make
> +
> +Running the Application
> +-----------------------
> +
> +The application has a number of command line options:
> +
> +.. code-block:: console
> +
> +   ./build/ipsec-secgw [EAL options] -- -p PORTMASK -P -u PORTMASK --config
> +   (port,queue,lcore)[,(port,queue,lcore] --EP0|--EP1 --cdev AESNI|QAT
> +
> +where,
> +
> +*   -p PORTMASK: Hexadecimal bitmask of ports to configure
> +
> +*   -P: optional, sets all ports to promiscuous mode so that packets are
> +    accepted regardless of the packet's Ethernet MAC destination address.
> +    Without this option, only packets with the Ethernet MAC destination address
> +    set to the Ethernet address of the port are accepted (default is enabled).
> +
> +*   -u PORTMASK: hexadecimal bitmask of unprotected ports
> +
> +*   --config (port,queue,lcore)[,(port,queue,lcore)]: determines which queues
> +    from which ports are mapped to which cores
> +
> +*   --cdev AESNI/QAT: choose whether to use QAT HW crypto or EASNI SW crypto
> +
> +*   --EP0: configure the app as Endpoint 0.
> +
> +*   --EP1: configure the app as Endpoint 1.
> +
> +Either --EP0 or --EP1 must be specified.
> +
> +The mapping of lcores to port/queues is similar to other l3fwd applications.
> +
> +For example, given the following command line:
> +.. code-block:: console
> +
> +    ./build/ipsec-secgw -l 20,21 -n 4 --socket-mem 0,2048 -- -p 0xf -P -u 0x3
> +    --config="(0,0,20),(1,0,20),(2,0,21),(3,0,21)" --cdev AESNI --EP0
> +
> +where each options means:
> +
> +*   The -l option enables cores 20 and 21
> +
> +*   The -n option sets memory 4 channels
> +
> +*   The --socket-mem to use 2GB on socket 1
> +
> +*   The -p option enables ports (detected) 0, 1, 2 and 3
> +
> +*   The -P option enables promiscous mode
> +
> +*   The -u option sets ports 1 and 2 as unprotected, leaving 2 and 3 as protected
> +
> +*   The --config option enables one queue per port with the following mapping:
> +
> ++----------+-----------+-----------+---------------------------------------+
> +| **Port** | **Queue** | **lcore** | **Description**                       |
> +|          |           |           |                                       |
> ++----------+-----------+-----------+---------------------------------------+
> +| 0        | 0         | 20        | Map queue 0 from port 0 to lcore 20.  |
> +|          |           |           |                                       |
> ++----------+-----------+-----------+---------------------------------------+
> +| 1        | 0         | 20        | Map queue 0 from port 1 to lcore 20.  |
> +|          |           |           |                                       |
> ++----------+-----------+-----------+---------------------------------------+
> +| 2        | 0         | 21        | Map queue 0 from port 2 to lcore 21.  |
> +|          |           |           |                                       |
> ++----------+-----------+-----------+---------------------------------------+
> +| 3        | 0         | 21        | Map queue 0 from port 3 to lcore 21.  |
> +|          |           |           |                                       |
> ++----------+-----------+-----------+---------------------------------------+
> +
> +Refer to the *DPDK Getting Started Guide* for general information on running applications and the Environment Abstraction Layer (EAL) options.
> +
> +Configurations
> +--------------
> +
> +The following sections provide some details on the default values used to
> +initialize the SP, SA and Routing tables.
> +Currently all the configuration is hardcoded into the application.
> +
> +Security Policy Initialization
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +As mention in the overview, the Security Policies are ACL rules.
> +There are two ACLs, Inbound and Outboud.
> +The application would also replicate the set of two ACLs per socket in use.
> +
> +Following are the default rules:
> +
> +Endpoint 0 Outbound Security Policies:
> +
> ++---------+------------------+-----------+------------+
> +| **Src** | **Dst**          | **proto** | **SA idx** |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.105.0/24 | Any       | 5          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.106.0/24 | Any       | 6          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.107.0/24 | Any       | 7          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.108.0/24 | Any       | 8          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +
> +Endpoint 0 Inbound Security Policies:
> +
> ++---------+------------------+-----------+------------+
> +| **Src** | **Dst**          | **proto** | **SA idx** |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.115.0/24 | Any       | 5          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.116.0/24 | Any       | 6          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.117.0/24 | Any       | 7          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.118.0/24 | Any       | 8          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +
> +Endpoint 1 Outbound Security Policies:
> +
> ++---------+------------------+-----------+------------+
> +| **Src** | **Dst**          | **proto** | **SA idx** |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.115.0/24 | Any       | 5          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.116.0/24 | Any       | 6          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.117.0/24 | Any       | 7          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.118.0/24 | Any       | 8          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +
> +Endpoint 1 Inbound Security Policies:
> +
> ++---------+------------------+-----------+------------+
> +| **Src** | **Dst**          | **proto** | **SA idx** |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.105.0/24 | Any       | 5          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.106.0/24 | Any       | 6          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.107.0/24 | Any       | 7          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +| Any     | 192.168.108.0/24 | Any       | 8          |
> +|         |                  |           |            |
> ++---------+------------------+-----------+------------+
> +
> +
> +Security Association Initialization
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The SAs are kept in a array table.
> +
> +For Inbound, the SPI is used as index module the table size.
> +This means that on a table for 100 SA, SPI 5 and 105 would use the same index
> +and that is not currently supported.
> +
> +Notice that it is not an issue for Outbound traffic as we store the index and
> +not the SPI in the Security Policy.
> +
> +All SAs are configured with the same cipher algo, block size and key (AES-CBC
> +16B block), and same authentication algo, digest size and key (HMAC-SHA1, 12B
> +digest).
> +
> +Following are the default values:
> +
> +Endpoint 0 Outbound Security Associations:
> +
> ++---------+----------------+------------------+
> +| **SPI** | **Tunnel src** | **Tunnel dst**   |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 5       | 172.16.1.5     | 172.16.2.5       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 6       | 172.16.1.6     | 172.16.2.6       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 7       | 172.16.1.7     | 172.16.2.7       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 8       | 172.16.1.8     | 172.16.2.8       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +
> +Endpoint 0 Inbound Security Associations:
> +
> ++---------+----------------+------------------+
> +| **SPI** | **Tunnel src** | **Tunnel dst**   |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 5       | 172.16.2.5     | 172.16.1.5       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 6       | 172.16.2.6     | 172.16.1.6       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 7       | 172.16.2.7     | 172.16.1.7       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 8       | 172.16.2.8     | 172.16.1.8       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +
> +Endpoint 1 Outbound Security Associations:
> +
> ++---------+----------------+------------------+
> +| **SPI** | **Tunnel src** | **Tunnel dst**   |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 5       | 172.16.2.5     | 172.16.1.5       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 6       | 172.16.2.6     | 172.16.1.6       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 7       | 172.16.2.7     | 172.16.1.7       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 8       | 172.16.2.8     | 172.16.1.8       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +
> +Endpoint 1 Inbound Security Associations:
> +
> ++---------+----------------+------------------+
> +| **SPI** | **Tunnel src** | **Tunnel dst**   |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 5       | 172.16.1.5     | 172.16.2.5       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 6       | 172.16.1.6     | 172.16.2.6       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 7       | 172.16.1.7     | 172.16.2.7       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +| 8       | 172.16.1.8     | 172.16.2.8       |
> +|         |                |                  |
> ++---------+----------------+------------------+
> +
> +Routing Initialization
> +~~~~~~~~~~~~~~~~~~~~~~
> +
> +The Routing is implemented using LPM table.
> +
> +Following default values:
> +
> +Endpoint 0 Routing Table:
> +
> ++------------------+----------+
> +| **Dst addr**     | **Port** |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.2.5/32    | 0        |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.2.6/32    | 0        |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.2.7/32    | 1        |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.2.8/32    | 1        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.115.0/24 | 2        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.116.0/24 | 2        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.117.0/24 | 3        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.118.0/24 | 3        |
> +|                  |          |
> ++------------------+----------+
> +
> +Endpoint 1 Routing Table:
> +
> ++------------------+----------+
> +| **Dst addr**     | **Port** |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.1.5/32    | 2        |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.1.6/32    | 2        |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.1.7/32    | 3        |
> +|                  |          |
> ++------------------+----------+
> +| 172.16.1.8/32    | 3        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.105.0/24 | 0        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.106.0/24 | 0        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.107.0/24 | 1        |
> +|                  |          |
> ++------------------+----------+
> +| 192.168.108.0/24 | 1        |
> +|                  |          |
> ++------------------+----------+
> diff --git a/examples/Makefile b/examples/Makefile
> index 1cb4785..65ce6ce 100644
> --- a/examples/Makefile
> +++ b/examples/Makefile
> @@ -1,6 +1,7 @@
>  #   BSD LICENSE
>  #
>  #   Copyright(c) 2014 6WIND S.A.
> +#   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
>  #
>  #   Redistribution and use in source and binary forms, with or without
>  #   modification, are permitted provided that the following conditions
> @@ -78,5 +79,6 @@ DIRS-y += vmdq
>  DIRS-y += vmdq_dcb
>  DIRS-$(CONFIG_RTE_LIBRTE_POWER) += vm_power_manager
>  DIRS-$(CONFIG_RTE_LIBRTE_CRYPTODEV) += l2fwd-crypto
> +DIRS-$(CONFIG_RTE_LIBRTE_CRYPTODEV) += ipsec-secgw
>  
>  include $(RTE_SDK)/mk/rte.extsubdir.mk
> diff --git a/examples/ipsec-secgw/Makefile b/examples/ipsec-secgw/Makefile
> new file mode 100644
> index 0000000..5f893b8
> --- /dev/null
> +++ b/examples/ipsec-secgw/Makefile
> @@ -0,0 +1,58 @@
> +#   BSD LICENSE
> +#
> +#   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> +#   All rights reserved.
> +#
> +#   Redistribution and use in source and binary forms, with or without
> +#   modification, are permitted provided that the following conditions
> +#   are met:
> +#
> +#     * Redistributions of source code must retain the above copyright
> +#       notice, this list of conditions and the following disclaimer.
> +#     * Redistributions in binary form must reproduce the above copyright
> +#       notice, this list of conditions and the following disclaimer in
> +#       the documentation and/or other materials provided with the
> +#       distribution.
> +#     * Neither the name of Intel Corporation nor the names of its
> +#       contributors may be used to endorse or promote products derived
> +#       from this software without specific prior written permission.
> +#
> +#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> +#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> +#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> +#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> +#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> +#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> +#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> +#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> +#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> +#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +
> +ifeq ($(RTE_SDK),)
> +	$(error "Please define RTE_SDK environment variable")
> +endif
> +
> +# Default target, can be overridden by command line or environment
> +RTE_TARGET ?= x86_64-native-linuxapp-gcc
> +
> +include $(RTE_SDK)/mk/rte.vars.mk
> +
> +APP = ipsec-secgw
> +
> +CFLAGS += -O3 -gdwarf-2
> +CFLAGS += $(WERROR_FLAGS)
> +
> +VPATH += $(SRCDIR)/librte_ipsec
> +
> +#
> +# all source are stored in SRCS-y
> +#
> +SRCS-y += ipsec.c
> +SRCS-y += esp.c
> +SRCS-y += sp.c
> +SRCS-y += sa.c
> +SRCS-y += rt.c
> +SRCS-y += ipsec-secgw.c
> +
> +include $(RTE_SDK)/mk/rte.extapp.mk
> diff --git a/examples/ipsec-secgw/esp.c b/examples/ipsec-secgw/esp.c
> new file mode 100644
> index 0000000..1be0621
> --- /dev/null
> +++ b/examples/ipsec-secgw/esp.c
> @@ -0,0 +1,256 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <netinet/ip.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +#include <rte_memcpy.h>
> +#include <rte_crypto.h>
> +#include <rte_cryptodev.h>
> +#include <rte_random.h>
> +
> +#include "ipsec.h"
> +#include "esp.h"
> +#include "ipip.h"
> +
> +static inline void
> +random_iv_u64(uint64_t *buf, uint16_t n)
> +{
> +	unsigned left = n & 0x7;
> +	unsigned i;
> +
> +	IPSEC_ASSERT((n & 0x3) == 0);
> +
> +	for (i = 0; i < (n >> 3); i++)
> +		buf[i] = rte_rand();
> +
> +	if (left)
> +		*((uint32_t *)&buf[i]) = (uint32_t)lrand48();
> +}
> +
> +/* IPv4 Tunnel */
> +int
> +esp4_tunnel_inbound_pre_crypto(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop)
> +{
> +	uint16_t digest_len, esp_len, iphdr_len;
> +	int32_t payload_len;
> +
> +	IPSEC_ASSERT(m != NULL);
> +
> +	iphdr_len = sizeof(struct ip);
> +	esp_len = sizeof(struct esp_hdr) + sa->iv_len;
> +	digest_len = sa->digest_len;
> +	payload_len = rte_pktmbuf_pkt_len(m) - iphdr_len - esp_len - digest_len;
> +
> +	if ((payload_len & (sa->block_size - 1)) || (payload_len <= 0)) {
> +		IPSEC_LOG(DEBUG, IPSEC_ESP, "payload %d not a multiple of %u\n",
> +				payload_len, sa->block_size);
> +		return -EINVAL;
> +	}
> +
> +	cop->data.to_cipher.offset = iphdr_len + esp_len;
> +	cop->data.to_cipher.length = payload_len;
> +
> +	cop->data.to_hash.offset = iphdr_len;
> +	if (sa->auth_algo == RTE_CRYPTO_AUTH_AES_GCM)
> +		cop->data.to_hash.length = esp_len - sa->iv_len;
> +	else
> +		cop->data.to_hash.length = esp_len + payload_len;
> +
> +	cop->iv.data = rte_pktmbuf_mtod_offset(m, void*,
> +			iphdr_len + esp_len - sa->iv_len);
> +	cop->iv.phys_addr = rte_pktmbuf_mtophys_offset(m,
> +			iphdr_len + esp_len - sa->iv_len);
> +
> +	cop->iv.length = sa->iv_len;
> +
> +	cop->digest.data = rte_pktmbuf_mtod_offset(m, void*,
> +			iphdr_len + esp_len + payload_len);
> +	cop->digest.phys_addr = rte_pktmbuf_mtophys_offset(m,
> +			iphdr_len + esp_len + payload_len);
> +	cop->digest.length = digest_len;
> +
> +	return 0;
> +}
> +
> +int
> +esp4_tunnel_inbound_post_crypto(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop)
> +{
> +	uint16_t digest_len, esp_len, iphdr_len;
> +	uint8_t *nexthdr, *pad_len;
> +	uint8_t *padding;
> +	uint16_t i;
> +
> +	IPSEC_ASSERT(m != NULL);
> +
> +	iphdr_len = sizeof(struct ip);
> +	esp_len = sizeof(struct esp_hdr) + sa->iv_len;
> +	digest_len = sa->digest_len;
> +
> +	if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
> +		IPSEC_LOG(ERR, IPSEC_ESP, "Failed crypto op\n");
> +		return -1;
> +	}
> +
> +	nexthdr = rte_pktmbuf_mtod_offset(m, uint8_t*,
> +			rte_pktmbuf_pkt_len(m) - digest_len - 1);
> +	pad_len = nexthdr - 1;
> +
> +	padding = pad_len - *pad_len;
> +	for (i = 0; i < *pad_len; i++) {
> +		if (padding[i] != i) {
> +			IPSEC_LOG(ERR, IPSEC_ESP, "invalid pad_len field\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (rte_pktmbuf_trim(m, *pad_len + 2 + digest_len)) {
> +		IPSEC_LOG(ERR, IPSEC_ESP,
> +				"failed to remove pad_len + digest\n");
> +		return -EINVAL;
> +	}
> +
> +	return ip4ip_inbound(m, iphdr_len + esp_len);
> +}
> +
> +int
> +esp4_tunnel_outbound_pre_crypto(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop)
> +{
> +	uint16_t digest_len, esp_len, payload_len, block_sz, pad_len;
> +	int32_t pad_payload_len;
> +	struct ip *ip;
> +	struct esp_hdr *esp;
> +	int i;
> +	char *padding;
> +
> +	rte_prefetch0(rte_pktmbuf_mtod(m, uint8_t *) - RTE_CACHE_LINE_SIZE);
> +	rte_prefetch0(rte_pktmbuf_mtod(m, uint8_t *));
> +
> +	IPSEC_ASSERT(m != NULL);
> +	IPSEC_ASSERT(sa != NULL);
> +
> +	/* Payload length */
> +	payload_len = rte_pktmbuf_pkt_len(m);
> +
> +	block_sz = sa->block_size;
> +
> +	/* Per RFC4303:
> +	 *  padded payload needs to be multiple of 4 bytes.
> +	 * All application supported block sizes must be power of 2
> +	 */
> +	pad_len = (payload_len + 2) & (block_sz - 1);
> +	pad_len = ((block_sz - pad_len) & (block_sz - 1)) + 2;
> +
> +	/* Padded payload length */
> +	pad_payload_len = payload_len + pad_len;
> +	/* ESP header length */
> +	esp_len = sizeof(struct esp_hdr) + sa->iv_len;
> +	/* Digest length */
> +	digest_len = sa->digest_len;
> +
> +	IPSEC_LOG(DEBUG, IPSEC_ESP,
> +			"pktlen %u, esp_len %u, digest_len %u, payload_len %u,"
> +			" pad_payload_len %u, block_sz %u, pad_len %u\n",
> +			rte_pktmbuf_pkt_len(m), esp_len, digest_len,
> +			payload_len, pad_payload_len, block_sz,	pad_len);
> +
> +	/* Check maximum packet size */
> +	if (unlikely(esp_len + pad_payload_len + digest_len > IP_MAXPACKET)) {
> +		IPSEC_LOG(DEBUG, IPSEC_ESP, "ipsec packet is too big\n");
> +		return -EINVAL;
> +	}
> +
> +	padding = rte_pktmbuf_append(m, pad_len + digest_len);
> +
> +	IPSEC_ASSERT(padding != NULL);
> +
> +	ip = ip4ip_outbound(m, esp_len, sa->src, sa->dst);
> +
> +	esp = (struct esp_hdr *)(ip + 1);
> +	esp->spi = sa->spi;
> +
> +	esp->seq = htonl(sa->seq++);
> +
> +	IPSEC_LOG(DEBUG, IPSEC_ESP, "pktlen %u\n", rte_pktmbuf_pkt_len(m));
> +
> +	/* Fill pad_len using default sequential scheme */
> +	for (i = 0; i < pad_len - 2; i++)
> +		padding[i] = i + 1;
> +
> +	padding[pad_len - 2] = pad_len - 2;
> +	padding[pad_len - 1] = IPPROTO_IPIP;
> +	ip->ip_p = IPPROTO_ESP;
> +
> +	cop->data.to_cipher.offset = sizeof(struct ip) + esp_len;
> +	cop->data.to_cipher.length = pad_payload_len;
> +
> +	cop->data.to_hash.offset = sizeof(struct ip);
> +	cop->data.to_hash.length = esp_len + pad_payload_len;
> +
> +	cop->iv.data = rte_pktmbuf_mtod_offset(m, void*,
> +			sizeof(struct ip) + esp_len - sa->iv_len);
> +	cop->iv.phys_addr = rte_pktmbuf_mtophys_offset(m,
> +			sizeof(struct ip) + esp_len - sa->iv_len);
> +	cop->iv.length = sa->iv_len;
> +
> +	cop->digest.data = rte_pktmbuf_mtod_offset(m, void*,
> +			sizeof(struct ip) + esp_len + pad_payload_len);
> +	cop->digest.phys_addr = rte_pktmbuf_mtophys_offset(m,
> +			sizeof(struct ip) + esp_len + pad_payload_len);
> +	cop->digest.length = digest_len;
> +
> +	random_iv_u64((uint64_t *)cop->iv.data, cop->iv.length);
> +
> +	return 0;
> +}
> +
> +int
> +esp4_tunnel_outbound_post_crypto(struct rte_mbuf *m __rte_unused,
> +		struct ipsec_sa *sa __rte_unused,
> +		struct rte_crypto_op *cop)
> +{
> +	if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
> +		IPSEC_LOG(ERR, IPSEC_ESP, "Failed crypto op\n");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> diff --git a/examples/ipsec-secgw/esp.h b/examples/ipsec-secgw/esp.h
> new file mode 100644
> index 0000000..d7c8ba6
> --- /dev/null
> +++ b/examples/ipsec-secgw/esp.h
> @@ -0,0 +1,66 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +#ifndef __RTE_IPSEC_XFORM_ESP_H__
> +#define __RTE_IPSEC_XFORM_ESP_H__
> +
> +struct mbuf;
> +
> +/* RFC4303 */
> +struct esp_hdr {
> +	uint32_t spi;
> +	uint32_t seq;
> +	/* Payload */
> +	/* Padding */
> +	/* Pad Length */
> +	/* Next Header */
> +	/* Integrity Check Value - ICV */
> +};
> +
> +/* IPv4 Tunnel */
> +int
> +esp4_tunnel_inbound_pre_crypto(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop);
> +
> +int
> +esp4_tunnel_inbound_post_crypto(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop);
> +
> +int
> +esp4_tunnel_outbound_pre_crypto(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop);
> +
> +int
> +esp4_tunnel_outbound_post_crypto(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop);
> +
> +#endif /* __RTE_IPSEC_XFORM_ESP_H__ */
> diff --git a/examples/ipsec-secgw/ipip.h b/examples/ipsec-secgw/ipip.h
> new file mode 100644
> index 0000000..16c5dfb
> --- /dev/null
> +++ b/examples/ipsec-secgw/ipip.h
> @@ -0,0 +1,100 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef __IPIP_H__
> +#define __IPIP_H__
> +
> +#include <stdint.h>
> +#include <netinet/in.h>
> +#include <netinet/ip.h>
> +
> +#include <rte_mbuf.h>
> +
> +static inline  struct ip *
> +ip4ip_outbound(struct rte_mbuf *m, uint32_t offset, uint32_t src, uint32_t dst)
> +{
> +	struct ip *inip, *outip;
> +
> +	inip = rte_pktmbuf_mtod(m, struct ip*);
> +
> +	IPSEC_ASSERT(inip->ip_v == IPVERSION || inip->ip_v == IPV6_VERSION);
> +
> +	offset += sizeof(struct ip);
> +
> +	outip = (struct ip *)rte_pktmbuf_prepend(m, offset);
> +
> +	IPSEC_ASSERT(outip != NULL);
> +
> +	/* Per RFC4301 5.1.2.1 */
> +	outip->ip_len = htons(rte_pktmbuf_data_len(m));
> +	outip->ip_v = IPVERSION;
> +	outip->ip_hl = 5;
> +	outip->ip_tos = inip->ip_tos;
> +
> +	outip->ip_id = 0;
> +	outip->ip_off = 0;
> +
> +	outip->ip_ttl = IPDEFTTL;
> +
> +	outip->ip_dst.s_addr = dst;
> +	outip->ip_src.s_addr = src;
> +
> +	return outip;
> +}
> +
> +static inline int
> +ip4ip_inbound(struct rte_mbuf *m, uint32_t offset)
> +{
> +	struct ip *inip;
> +	struct ip *outip;
> +
> +	outip = rte_pktmbuf_mtod(m, struct ip*);
> +
> +	IPSEC_ASSERT(outip->ip_v == IPVERSION);
> +
> +	offset += sizeof(struct ip);
> +	inip = (struct ip *)rte_pktmbuf_adj(m, offset);
> +	IPSEC_ASSERT(inip->ip_v == IPVERSION || inip->ip_v == IPV6_VERSION);
> +
> +	/* Check packet is still bigger than IP header (inner) */
> +	IPSEC_ASSERT(rte_pktmbuf_pkt_len(m) > sizeof(struct ip));
> +
> +	/* RFC4301 5.1.2.1 Note 6 */
> +	if ((inip->ip_tos & htons(IPTOS_ECN_ECT0 | IPTOS_ECN_ECT1)) &&
> +			((outip->ip_tos & htons(IPTOS_ECN_CE)) == IPTOS_ECN_CE))
> +		inip->ip_tos |= htons(IPTOS_ECN_CE);
> +
> +	return 0;
> +}
> +
> +#endif /* __IPIP_H__ */
> diff --git a/examples/ipsec-secgw/ipsec-secgw.c b/examples/ipsec-secgw/ipsec-secgw.c
> new file mode 100644
> index 0000000..d2e8972
> --- /dev/null
> +++ b/examples/ipsec-secgw/ipsec-secgw.c
> @@ -0,0 +1,1218 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <inttypes.h>
> +#include <sys/types.h>
> +#include <string.h>
> +#include <sys/queue.h>
> +#include <stdarg.h>
> +#include <errno.h>
> +#include <getopt.h>
> +
> +#include <rte_common.h>
> +#include <rte_byteorder.h>
> +#include <rte_log.h>
> +#include <rte_eal.h>
> +#include <rte_launch.h>
> +#include <rte_atomic.h>
> +#include <rte_cycles.h>
> +#include <rte_prefetch.h>
> +#include <rte_lcore.h>
> +#include <rte_per_lcore.h>
> +#include <rte_branch_prediction.h>
> +#include <rte_interrupts.h>
> +#include <rte_pci.h>
> +#include <rte_random.h>
> +#include <rte_debug.h>
> +#include <rte_ether.h>
> +#include <rte_ethdev.h>
> +#include <rte_mempool.h>
> +#include <rte_mbuf.h>
> +#include <rte_acl.h>
> +#include <rte_lpm.h>
> +#include <rte_cryptodev.h>
> +#include <rte_cryptodev.h>
> +
> +#include "ipsec.h"
> +
> +#define RTE_LOGTYPE_IPSEC RTE_LOGTYPE_USER1
> +
> +#define MAX_JUMBO_PKT_LEN  9600
> +
> +#define MEMPOOL_CACHE_SIZE 256
> +
> +#define NB_MBUF	(32000)
> +
> +#define CDEV_MP_NB_OBJS 2048
> +#define CDEV_MP_CACHE_SZ 64
> +#define MAX_QUEUE_PAIRS 1
> +
> +#define OPTION_CONFIG		"config"
> +#define OPTION_EP0		"ep0"
> +#define OPTION_EP1		"ep1"
> +#define OPTION_CDEV_TYPE	"cdev"
> +
> +#define MAX_PKT_BURST 32
> +#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
> +
> +#define NB_SOCKETS 4
> +
> +/* Configure how many packets ahead to prefetch, when reading packets */
> +#define PREFETCH_OFFSET	3
> +
> +#define MAX_RX_QUEUE_PER_LCORE 16
> +
> +#define MAX_LCORE_PARAMS 1024
> +
> +#define UNPROTECTED_PORT(port) (unprotected_port_mask & (1 << portid))
> +
> +/*
> + * Configurable number of RX/TX ring descriptors
> + */
> +#define IPSEC_SECGW_RX_DESC_DEFAULT 128
> +#define IPSEC_SECGW_TX_DESC_DEFAULT 512
> +static uint16_t nb_rxd = IPSEC_SECGW_RX_DESC_DEFAULT;
> +static uint16_t nb_txd = IPSEC_SECGW_TX_DESC_DEFAULT;
> +
> +#if RTE_BYTE_ORDER != RTE_LITTLE_ENDIAN
> +#define __BYTES_TO_UINT64(a, b, c, d, e, f, g, h) \
> +	(((uint64_t)((a) & 0xff) << 56) | \
> +	((uint64_t)((b) & 0xff) << 48) | \
> +	((uint64_t)((c) & 0xff) << 40) | \
> +	((uint64_t)((d) & 0xff) << 32) | \
> +	((uint64_t)((e) & 0xff) << 24) | \
> +	((uint64_t)((f) & 0xff) << 16) | \
> +	((uint64_t)((g) & 0xff) << 8)  | \
> +	((uint64_t)(h) & 0xff))
> +#else
> +#define __BYTES_TO_UINT64(a, b, c, d, e, f, g, h) \
> +	(((uint64_t)((h) & 0xff) << 56) | \
> +	((uint64_t)((g) & 0xff) << 48) | \
> +	((uint64_t)((f) & 0xff) << 40) | \
> +	((uint64_t)((e) & 0xff) << 32) | \
> +	((uint64_t)((d) & 0xff) << 24) | \
> +	((uint64_t)((c) & 0xff) << 16) | \
> +	((uint64_t)((b) & 0xff) << 8) | \
> +	((uint64_t)(a) & 0xff))
> +#endif
> +#define ETHADDR(a, b, c, d, e, f) (__BYTES_TO_UINT64(a, b, c, d, e, f, 0, 0))
> +
> +#define ETHADDR_TO_UINT64(addr) __BYTES_TO_UINT64( \
> +		addr.addr_bytes[0], addr.addr_bytes[1], \
> +		addr.addr_bytes[2], addr.addr_bytes[3], \
> +		addr.addr_bytes[4], addr.addr_bytes[5], \
> +		0, 0)
> +
> +/* port/source ethernet addr and destination ethernet addr */
> +struct ethaddr_info {
> +	uint64_t src, dst;
> +};
> +
> +struct ethaddr_info ethaddr_tbl[RTE_MAX_ETHPORTS] = {
> +	{ 0, ETHADDR(0x00, 0x16, 0x3e, 0x7e, 0x94, 0x9a) },
> +	{ 0, ETHADDR(0x00, 0x16, 0x3e, 0x22, 0xa1, 0xd9) },
> +	{ 0, ETHADDR(0x00, 0x16, 0x3e, 0x08, 0x69, 0x26) },
> +	{ 0, ETHADDR(0x00, 0x16, 0x3e, 0x49, 0x9e, 0xdd) }
> +};
> +
> +/* mask of enabled ports */
> +static uint32_t enabled_port_mask;
> +static uint32_t unprotected_port_mask;
> +static int promiscuous_on = 1;
> +static int numa_on = 1; /**< NUMA is enabled by default. */
> +static int ep = -1; /**< Endpoint configuration (0 or 1) */
> +static unsigned cdev_type = -1;
> +static unsigned nb_lcores;
> +
> +struct buffer {
> +	uint16_t len;
> +	struct rte_mbuf *m_table[MAX_PKT_BURST];
> +};
> +
> +struct lcore_rx_queue {
> +	uint8_t port_id;
> +	uint8_t queue_id;
> +} __rte_cache_aligned;
> +
> +struct lcore_params {
> +	uint8_t port_id;
> +	uint8_t queue_id;
> +	uint8_t lcore_id;
> +} __rte_cache_aligned;
> +
> +static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
> +
> +static struct lcore_params *lcore_params;
> +static uint16_t nb_lcore_params;
> +
> +struct lcore_conf {
> +	uint16_t nb_rx_queue;
> +	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
> +	uint16_t tx_queue_id[RTE_MAX_ETHPORTS];
> +	struct buffer tx_mbufs[RTE_MAX_ETHPORTS];
> +	uint16_t cdev;
> +	uint16_t cdev_q;
> +} __rte_cache_aligned;
> +
> +static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
> +
> +static struct rte_eth_conf port_conf = {
> +	.rxmode = {
> +		.mq_mode	= ETH_MQ_RX_RSS,
> +		.max_rx_pkt_len = ETHER_MAX_LEN,
> +		.split_hdr_size = 0,
> +		.header_split   = 0, /**< Header Split disabled */
> +		.hw_ip_checksum = 1, /**< IP checksum offload enabled */
> +		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
> +		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
> +		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
> +	},
> +	.rx_adv_conf = {
> +		.rss_conf = {
> +			.rss_key = NULL,
> +			.rss_hf = ETH_RSS_IP | ETH_RSS_UDP |
> +				ETH_RSS_TCP | ETH_RSS_SCTP,
> +		},
> +	},
> +	.txmode = {
> +		.mq_mode = ETH_MQ_TX_NONE,
> +	},
> +};
> +
> +static struct socket_ctx socket_ctx[NB_SOCKETS];
> +
> +struct traffic_type {
> +	const uint8_t *data[MAX_PKT_BURST * 2];
> +	struct rte_mbuf *pkts[MAX_PKT_BURST * 2];
> +	uint32_t res[MAX_PKT_BURST * 2];
> +	uint32_t num;
> +};
> +
> +struct ipsec_traffic {
> +	struct traffic_type ipsec4;
> +	struct traffic_type ipv4;
> +};
> +
> +static inline void
> +prepare_one_packet(struct rte_mbuf *pkt, struct ipsec_traffic *t)
> +{
> +	uint8_t *nlp;
> +
> +	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
> +		rte_pktmbuf_adj(pkt, ETHER_HDR_LEN);
> +		nlp = rte_pktmbuf_mtod_offset(pkt, uint8_t *,
> +				offsetof(struct ip, ip_p));
> +		if (*nlp == IPPROTO_ESP)
> +			t->ipsec4.pkts[(t->ipsec4.num)++] = pkt;
> +		else {
> +			t->ipv4.data[t->ipv4.num] = nlp;
> +			t->ipv4.pkts[(t->ipv4.num)++] = pkt;
> +		}
> +	} else {
> +		/* Unknown/Unsupported type, drop the packet */
> +		rte_pktmbuf_free(pkt);
> +	}
> +}
> +
> +static inline void
> +prepare_traffic(struct rte_mbuf **pkts, struct ipsec_traffic *t,
> +		uint16_t nb_pkts)
> +{
> +	int i;
> +
> +	t->ipsec4.num = 0;
> +	t->ipv4.num = 0;
> +
> +	for (i = 0; i < (nb_pkts - PREFETCH_OFFSET); i++) {
> +		rte_prefetch0(rte_pktmbuf_mtod(pkts[i + PREFETCH_OFFSET],
> +					void *));
> +		prepare_one_packet(pkts[i], t);
> +	}
> +	/* Process left packets */
> +	for (; i < nb_pkts; i++)
> +		prepare_one_packet(pkts[i], t);
> +}
> +
> +static inline void
> +prepare_tx_pkt(struct rte_mbuf *pkt, uint8_t port)
> +{
> +	pkt->ol_flags |= PKT_TX_IP_CKSUM | PKT_TX_IPV4;
> +	pkt->l3_len = sizeof(struct ip);
> +	pkt->l2_len = ETHER_HDR_LEN;
> +
> +	uint64_t *ethhdr = (uint64_t *)rte_pktmbuf_prepend(pkt, ETHER_HDR_LEN);
> +#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
> +	ethhdr[0] = ethaddr_tbl[port].dst |
> +		((ethaddr_tbl[port].src & 0xffff) << 48);
> +	ethhdr[1] = ethaddr_tbl[port].src >> 16 |
> +		((uint64_t)rte_cpu_to_be_16(ETHER_TYPE_IPv4) << 32) |
> +		(ethhdr[1] & (0xffffUL << 48));
> +#else
> +	ethhdr[0] = ethaddr_tbl[port].dst |
> +		(ethaddr_tbl[port].src >> 48 & 0xffff);
> +	ethhdr[1] = ethaddr_tbl[port].src << 16 | ((ETHER_TYPE_IPv4)UL << 16) |
> +		(ethhdr[1] & 0xffffUL);
> +#endif
> +}
> +
> +static inline void
> +prepare_tx_burst(struct rte_mbuf *pkts[], uint16_t nb_pkts, uint8_t port)
> +{
> +	int i;
> +	const int prefetch_offset = 2;
> +
> +	for (i = 0; i < (nb_pkts - prefetch_offset); i++) {
> +		rte_prefetch0(pkts[i + prefetch_offset]->cacheline1);
> +		prepare_tx_pkt(pkts[i], port);
> +	}
> +	/* Process left packets */
> +	for (; i < nb_pkts; i++)
> +		prepare_tx_pkt(pkts[i], port);
> +}
> +
> +/* Send burst of packets on an output interface */
> +static inline int
> +send_burst(struct lcore_conf *qconf, uint16_t n, uint8_t port)
> +{
> +	struct rte_mbuf **m_table;
> +	int ret;
> +	uint16_t queueid;
> +
> +	queueid = qconf->tx_queue_id[port];
> +	m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table;
> +
> +	prepare_tx_burst(m_table, n, port);
> +
> +	ret = rte_eth_tx_burst(port, queueid, m_table, n);
> +	if (unlikely(ret < n)) {
> +		do {
> +			rte_pktmbuf_free(m_table[ret]);
> +		} while (++ret < n);
> +	}
> +
> +	return 0;
> +}
> +
> +/* Enqueue a single packet, and send burst if queue is filled */
> +static inline int
> +send_single_packet(struct rte_mbuf *m, uint8_t port)
> +{
> +	uint32_t lcore_id;
> +	uint16_t len;
> +	struct lcore_conf *qconf;
> +
> +	lcore_id = rte_lcore_id();
> +
> +	qconf = &lcore_conf[lcore_id];
> +	len = qconf->tx_mbufs[port].len;
> +	qconf->tx_mbufs[port].m_table[len] = m;
> +	len++;
> +
> +	/* enough pkts to be sent */
> +	if (unlikely(len == MAX_PKT_BURST)) {
> +		send_burst(qconf, MAX_PKT_BURST, port);
> +		len = 0;
> +	}
> +
> +	qconf->tx_mbufs[port].len = len;
> +	return 0;
> +}
> +
> +static inline void
> +process_pkts_inbound(struct socket_ctx *ctx, struct ipsec_traffic *traffic)
> +{
> +	struct sa_ctx *sa_ctx = ctx->sa_ipv4_in;
> +	struct sp_ctx *sp_ctx = ctx->sp_ipv4_in;
> +	struct rte_mbuf *m;
> +	uint16_t idx, nb_pkts_in, i, j;
> +	uint32_t sa_idx, res;
> +	struct ipsec_ctx ipsec_ctx;
> +	struct lcore_conf *qconf;
> +
> +	qconf = &lcore_conf[rte_lcore_id()];
> +	ipsec_ctx.cdev = qconf->cdev;
> +	ipsec_ctx.queue = qconf->cdev_q;
> +	ipsec_ctx.sa_ctx = sa_ctx;
> +
> +	nb_pkts_in = ipsec_inbound(&ipsec_ctx, traffic->ipsec4.pkts,
> +			traffic->ipsec4.num, MAX_PKT_BURST);
> +
> +	/* SP/ACL Inbound check ipsec and ipv4 */
> +	for (i = 0; i < nb_pkts_in; i++) {
> +		idx = traffic->ipv4.num++;
> +		m = traffic->ipsec4.pkts[i];
> +		traffic->ipv4.pkts[idx] = m;
> +		traffic->ipv4.data[idx] = rte_pktmbuf_mtod_offset(m,
> +				uint8_t *, offsetof(struct ip, ip_p));
> +	}
> +
> +	rte_acl_classify((struct rte_acl_ctx *)sp_ctx, traffic->ipv4.data,
> +			traffic->ipv4.res, traffic->ipv4.num,
> +			DEFAULT_MAX_CATEGORIES);
> +
> +	j = 0;
> +	for (i = 0; i < traffic->ipv4.num - nb_pkts_in; i++) {
> +		m = traffic->ipv4.pkts[i];
> +		res = traffic->ipv4.res[i];
> +		if (res & ~BYPASS) {
> +			rte_pktmbuf_free(m);
> +			continue;
> +		}
> +		traffic->ipv4.pkts[j++] = m;
> +	}
> +	/* Check return SA SPI matches pkt SPI */
> +	for ( ; i < traffic->ipv4.num; i++) {
> +		m = traffic->ipv4.pkts[i];
> +		sa_idx = traffic->ipv4.res[i] & PROTECT_MASK;
> +		if (sa_idx == 0 || !inbound_sa_check(sa_ctx, m, sa_idx)) {
> +			rte_pktmbuf_free(m);
> +			continue;
> +		}
> +		traffic->ipv4.pkts[j++] = m;
> +	}
> +	traffic->ipv4.num = j;
> +}
> +
> +static inline void
> +process_pkts_outbound(struct socket_ctx *ctx, struct ipsec_traffic *traffic)
> +{
> +	struct sa_ctx *sa_ctx = ctx->sa_ipv4_out;
> +	struct sp_ctx *sp_ctx = ctx->sp_ipv4_out;
> +	struct rte_mbuf *m;
> +	uint16_t idx, nb_pkts_out, i, j;
> +	uint32_t sa_idx, res;
> +	struct ipsec_ctx ipsec_ctx;
> +	struct lcore_conf *qconf;
> +
> +	qconf = &lcore_conf[rte_lcore_id()];
> +	ipsec_ctx.cdev = qconf->cdev;
> +	ipsec_ctx.queue = qconf->cdev_q;
> +	ipsec_ctx.sa_ctx = sa_ctx;
> +
> +	rte_acl_classify((struct rte_acl_ctx *)sp_ctx, traffic->ipv4.data,
> +			traffic->ipv4.res, traffic->ipv4.num,
> +			DEFAULT_MAX_CATEGORIES);

IMO, an option for single SA based outbound processing would be useful
measuring performance bottlenecks with SA lookup.


> +	j = 0;
> +	for (i = 0; i < traffic->ipv4.num; i++) {
> +		m = traffic->ipv4.pkts[i];
> +		res = traffic->ipv4.res[i];
> +		sa_idx = res & PROTECT_MASK;
> +		if (res & DISCARD) {
> +			rte_pktmbuf_free(m);
> +		} else if (sa_idx != 0) {
> +			traffic->ipsec4.res[traffic->ipsec4.num] = sa_idx;
> +			traffic->ipsec4.pkts[traffic->ipsec4.num++] = m;
> +		} else /* BYPASS */
> +			traffic->ipv4.pkts[j++] = m;
> +	}
> +	traffic->ipv4.num = j;
> +
> +	nb_pkts_out = ipsec_outbound(&ipsec_ctx, traffic->ipsec4.pkts,
> +			traffic->ipsec4.res, traffic->ipsec4.num,
> +			MAX_PKT_BURST);
> +
> +	for (i = 0; i < nb_pkts_out; i++) {
> +		idx = traffic->ipv4.num++;
> +		m = traffic->ipsec4.pkts[i];
> +		traffic->ipv4.pkts[idx] = m;
> +	}
> +}
> +
> +static inline void
> +route_pkts(struct rt_ctx *rt_ctx, struct rte_mbuf *pkts[], uint8_t nb_pkts)
> +{
> +	uint16_t hop[MAX_PKT_BURST * 2];
> +	uint32_t dst_ip[MAX_PKT_BURST * 2];
> +	uint16_t i, offset;
> +
> +	if (nb_pkts == 0)
> +		return;
> +
> +	for (i = 0; i < nb_pkts; i++) {
> +		offset = offsetof(struct ip, ip_dst);
> +		dst_ip[i] = *rte_pktmbuf_mtod_offset(pkts[i],
> +				uint32_t *, offset);
> +		dst_ip[i] = rte_be_to_cpu_32(dst_ip[i]);
> +	}
> +
> +	rte_lpm_lookup_bulk((struct rte_lpm *)rt_ctx, dst_ip, hop, nb_pkts);
> +
> +	for (i = 0; i < nb_pkts; i++) {
> +		if ((hop[i] & RTE_LPM_LOOKUP_SUCCESS) == 0) {
> +			rte_pktmbuf_free(pkts[i]);
> +			continue;
> +		}
> +		send_single_packet(pkts[i], hop[i] & 0xff);
> +	}
> +}
> +
> +static inline void
> +process_pkts(struct socket_ctx *ctx, struct rte_mbuf **pkts,
> +		uint8_t nb_pkts, uint8_t portid)
> +{
> +	struct ipsec_traffic traffic;
> +
> +	prepare_traffic(pkts, &traffic, nb_pkts);
> +
> +	if (UNPROTECTED_PORT(portid))
> +		process_pkts_inbound(ctx, &traffic);
> +	else
> +		process_pkts_outbound(ctx, &traffic);
> +
> +	route_pkts(ctx->rt_ipv4, traffic.ipv4.pkts, traffic.ipv4.num);
> +}
> +
> +static inline void
> +drain_buffers(struct lcore_conf *qconf)
> +{
> +	struct buffer *buf;
> +	unsigned portid;
> +
> +	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
> +		buf = &qconf->tx_mbufs[portid];
> +		if (buf->len == 0)
> +			continue;
> +		send_burst(qconf, buf->len, portid);
> +		buf->len = 0;
> +	}
> +}
> +
> +/* main processing loop */
> +static int
> +main_loop(__attribute__((unused)) void *dummy)
> +{
> +	struct rte_mbuf *pkts[MAX_PKT_BURST];
> +	unsigned lcore_id;
> +	uint64_t prev_tsc, diff_tsc, cur_tsc;
> +	int i, nb_rx;
> +	uint8_t portid, queueid;
> +	struct lcore_conf *qconf;
> +	int socket_id;
> +	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1)
> +			/ US_PER_S * BURST_TX_DRAIN_US;
> +	struct socket_ctx *ctx;
> +	struct lcore_rx_queue *rxql;
> +
> +	prev_tsc = 0;
> +	lcore_id = rte_lcore_id();
> +	qconf = &lcore_conf[lcore_id];
> +	rxql = qconf->rx_queue_list;
> +	socket_id = rte_lcore_to_socket_id(lcore_id);
> +
> +	ctx = &socket_ctx[socket_id];
> +
> +	if (qconf->nb_rx_queue == 0) {
> +		RTE_LOG(INFO, IPSEC, "lcore %u has nothing to do\n", lcore_id);
> +		return 0;
> +	}
> +
> +	RTE_LOG(INFO, IPSEC, "entering main loop on lcore %u\n", lcore_id);
> +
> +	for (i = 0; i < qconf->nb_rx_queue; i++) {
> +		portid = rxql[i].port_id;
> +		queueid = rxql[i].queue_id;
> +		RTE_LOG(INFO, IPSEC,
> +			" -- lcoreid=%u portid=%hhu rxqueueid=%hhu\n",
> +			lcore_id, portid, queueid);
> +	}
> +
> +	while (1) {
> +		cur_tsc = rte_rdtsc();
> +
> +		/* TX queue buffer drain */
> +		diff_tsc = cur_tsc - prev_tsc;
> +
> +		if (unlikely(diff_tsc > drain_tsc)) {
> +			drain_buffers(qconf);
> +			prev_tsc = cur_tsc;
> +		}
> +
> +		/* Read packet from RX queues */
> +		for (i = 0; i < qconf->nb_rx_queue; ++i) {
> +			portid = rxql[i].port_id;
> +			queueid = rxql[i].queue_id;
> +			nb_rx = rte_eth_rx_burst(portid, queueid,
> +					pkts, MAX_PKT_BURST);
> +
> +			if (nb_rx > 0)
> +				process_pkts(ctx, pkts, nb_rx, portid);
> +		}
> +	}
> +}
> +
> +static int
> +check_params(void)
> +{
> +	uint8_t lcore, portid, nb_ports;
> +	uint16_t i;
> +	int socket_id;
> +
> +	if (lcore_params == NULL) {
> +		printf("Error: No port/queue/core mappings\n");
> +		return -1;
> +	}
> +
> +	nb_ports = rte_eth_dev_count();
> +	if (nb_ports > RTE_MAX_ETHPORTS)
> +		nb_ports = RTE_MAX_ETHPORTS;
> +
> +	for (i = 0; i < nb_lcore_params; ++i) {
> +		lcore = lcore_params[i].lcore_id;
> +		if (!rte_lcore_is_enabled(lcore)) {
> +			printf("error: lcore %hhu is not enabled in "
> +				"lcore mask\n", lcore);
> +			return -1;
> +		}
> +		socket_id = rte_lcore_to_socket_id(lcore);
> +		if (socket_id != 0 && numa_on == 0) {
> +			printf("warning: lcore %hhu is on socket %d "
> +				"with numa off\n",
> +				lcore, socket_id);
> +		}
> +		portid = lcore_params[i].port_id;
> +		if ((enabled_port_mask & (1 << portid)) == 0) {
> +			printf("port %u is not enabled in port mask\n", portid);
> +			return -1;
> +		}
> +		if (portid >= nb_ports) {
> +			printf("port %u is not present on the board\n", portid);
> +			return -1;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static uint8_t
> +get_port_nb_rx_queues(const uint8_t port)
> +{
> +	int queue = -1;
> +	uint16_t i;
> +
> +	for (i = 0; i < nb_lcore_params; ++i) {
> +		if (lcore_params[i].port_id == port &&
> +				lcore_params[i].queue_id > queue)
> +			queue = lcore_params[i].queue_id;
> +	}
> +	return (uint8_t)(++queue);
> +}
> +
> +static int
> +init_lcore_rx_queues(void)
> +{
> +	uint16_t i, nb_rx_queue;
> +	uint8_t lcore;
> +
> +	for (i = 0; i < nb_lcore_params; ++i) {
> +		lcore = lcore_params[i].lcore_id;
> +		nb_rx_queue = lcore_conf[lcore].nb_rx_queue;
> +		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
> +			printf("error: too many queues (%u) for lcore: %u\n",
> +					nb_rx_queue + 1, lcore);
> +			return -1;
> +		}
> +		lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
> +			lcore_params[i].port_id;
> +		lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
> +			lcore_params[i].queue_id;
> +		lcore_conf[lcore].nb_rx_queue++;
> +	}
> +	return 0;
> +}
> +
> +/* display usage */
> +static void
> +print_usage(const char *prgname)
> +{
> +	printf("%s [EAL options] -- -p PORTMASK -P -u PORTMASK"
> +		"  --"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]"
> +		" --EP0|--EP1 --cdev AESNI|QAT\n"
> +		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
> +		"  -P : enable promiscuous mode\n"
> +		"  -u PORTMASK: hexadecimal bitmask of unprotected ports\n"
> +		"  --"OPTION_CONFIG": (port,queue,lcore): "
> +		"rx queues configuration\n"
> +		"  --cdev AESNI | QAT\n"
> +		"  --EP0: Configure as Endpoint 0\n"
> +		"  --EP1: Configure as Endpoint 1\n", prgname);
> +}
> +
> +static int
> +parse_portmask(const char *portmask)
> +{
> +	char *end = NULL;
> +	unsigned long pm;
> +
> +	/* parse hexadecimal string */
> +	pm = strtoul(portmask, &end, 16);
> +	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
> +		return -1;
> +
> +	if (pm == 0)
> +		return -1;
> +
> +	return pm;
> +}
> +
> +static int
> +parse_config(const char *q_arg)
> +{
> +	char s[256];
> +	const char *p, *p0 = q_arg;
> +	char *end;
> +	enum fieldnames {
> +		FLD_PORT = 0,
> +		FLD_QUEUE,
> +		FLD_LCORE,
> +		_NUM_FLD
> +	};
> +	unsigned long int_fld[_NUM_FLD];
> +	char *str_fld[_NUM_FLD];
> +	int i;
> +	unsigned size;
> +
> +	nb_lcore_params = 0;
> +
> +	while ((p = strchr(p0, '(')) != NULL) {
> +		++p;
> +		p0 = strchr(p, ')');
> +		if (p0 == NULL)
> +			return -1;
> +
> +		size = p0 - p;
> +		if (size >= sizeof(s))
> +			return -1;
> +
> +		snprintf(s, sizeof(s), "%.*s", size, p);
> +		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
> +				_NUM_FLD)
> +			return -1;
> +		for (i = 0; i < _NUM_FLD; i++) {
> +			errno = 0;
> +			int_fld[i] = strtoul(str_fld[i], &end, 0);
> +			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
> +				return -1;
> +		}
> +		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
> +			printf("exceeded max number of lcore params: %hu\n",
> +				nb_lcore_params);
> +			return -1;
> +		}
> +		lcore_params_array[nb_lcore_params].port_id =
> +			(uint8_t)int_fld[FLD_PORT];
> +		lcore_params_array[nb_lcore_params].queue_id =
> +			(uint8_t)int_fld[FLD_QUEUE];
> +		lcore_params_array[nb_lcore_params].lcore_id =
> +			(uint8_t)int_fld[FLD_LCORE];
> +		++nb_lcore_params;
> +	}
> +	lcore_params = lcore_params_array;
> +	return 0;
> +}
> +
> +#define __STRNCMP(name, opt) (!strncmp(name, opt, sizeof(opt)))
> +static int
> +parse_args_long_options(struct option *lgopts, int option_index)
> +{
> +	int ret = -1;
> +	const char *optname = lgopts[option_index].name;
> +
> +	if (__STRNCMP(optname, OPTION_CONFIG)) {
> +		ret = parse_config(optarg);
> +		if (ret)
> +			printf("invalid config\n");
> +	}
> +
> +	if (__STRNCMP(optname, OPTION_EP0)) {
> +		printf("endpoint 0\n");
> +		ep = 0;
> +		ret = 0;
> +	}
> +
> +	if (__STRNCMP(optname, OPTION_EP1)) {
> +		printf("endpoint 1\n");
> +		ep = 1;
> +		ret = 0;
> +	}
> +
> +	if (__STRNCMP(optname, OPTION_CDEV_TYPE)) {
> +		if (__STRNCMP(optarg, "AESNI")) {
> +			cdev_type = RTE_CRYPTODEV_AESNI_MB_PMD;
> +			ret = 0;
> +		} else if (__STRNCMP(optarg, "QAT")) {
> +			cdev_type = RTE_CRYPTODEV_QAT_PMD;
> +			ret = 0;
> +		}
> +	}
> +
> +	return ret;
> +}
> +#undef __STRNCMP
> +
> +static int
> +parse_args(int argc, char **argv)
> +{
> +	int opt, ret;
> +	char **argvopt;
> +	int option_index;
> +	char *prgname = argv[0];
> +	static struct option lgopts[] = {
> +		{OPTION_CONFIG, 1, 0, 0},
> +		{OPTION_EP0, 0, 0, 0},
> +		{OPTION_EP1, 0, 0, 0},
> +		{OPTION_CDEV_TYPE, 1, 0, 0},
> +		{NULL, 0, 0, 0}
> +	};
> +
> +	argvopt = argv;
> +
> +	while ((opt = getopt_long(argc, argvopt, "p:Pu:",
> +				lgopts, &option_index)) != EOF) {
> +
> +		switch (opt) {
> +		case 'p':
> +			enabled_port_mask = parse_portmask(optarg);
> +			if (enabled_port_mask == 0) {
> +				printf("invalid portmask\n");
> +				print_usage(prgname);
> +				return -1;
> +			}
> +			break;
> +		case 'P':
> +			printf("Promiscuous mode selected\n");
> +			promiscuous_on = 1;
> +			break;
> +		case 'u':
> +			unprotected_port_mask = parse_portmask(optarg);
> +			if (unprotected_port_mask == 0) {
> +				printf("invalid unprotected portmask\n");
> +				print_usage(prgname);
> +				return -1;
> +			}
> +			break;
> +		case 0:
> +			if (parse_args_long_options(lgopts, option_index)) {
> +				print_usage(prgname);
> +				return -1;
> +			}
> +			break;
> +		default:
> +			print_usage(prgname);
> +			return -1;
> +		}
> +	}
> +
> +	if (optind >= 0)
> +		argv[optind-1] = prgname;
> +
> +	ret = optind-1;
> +	optind = 0; /* reset getopt lib */
> +	return ret;
> +}
> +
> +static void
> +print_ethaddr(const char *name, const struct ether_addr *eth_addr)
> +{
> +	char buf[ETHER_ADDR_FMT_SIZE];
> +	ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, eth_addr);
> +	printf("%s%s", name, buf);
> +}
> +
> +/* Check the link status of all ports in up to 9s, and print them finally */
> +static void
> +check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
> +{
> +#define CHECK_INTERVAL 100 /* 100ms */
> +#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
> +	uint8_t portid, count, all_ports_up, print_flag = 0;
> +	struct rte_eth_link link;
> +
> +	printf("\nChecking link status");
> +	fflush(stdout);
> +	for (count = 0; count <= MAX_CHECK_TIME; count++) {
> +		all_ports_up = 1;
> +		for (portid = 0; portid < port_num; portid++) {
> +			if ((port_mask & (1 << portid)) == 0)
> +				continue;
> +			memset(&link, 0, sizeof(link));
> +			rte_eth_link_get_nowait(portid, &link);
> +			/* print link status if flag set */
> +			if (print_flag == 1) {
> +				if (link.link_status)
> +					printf("Port %d Link Up - speed %u "
> +						"Mbps - %s\n", (uint8_t)portid,
> +						(unsigned)link.link_speed,
> +				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
> +					("full-duplex") : ("half-duplex\n"));
> +				else
> +					printf("Port %d Link Down\n",
> +						(uint8_t)portid);
> +				continue;
> +			}
> +			/* clear all_ports_up flag if any link down */
> +			if (link.link_status == 0) {
> +				all_ports_up = 0;
> +				break;
> +			}
> +		}
> +		/* after finally printing all link status, get out */
> +		if (print_flag == 1)
> +			break;
> +
> +		if (all_ports_up == 0) {
> +			printf(".");
> +			fflush(stdout);
> +			rte_delay_ms(CHECK_INTERVAL);
> +		}
> +
> +		/* set the print_flag if all ports up or timeout */
> +		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
> +			print_flag = 1;
> +			printf("done\n");
> +		}
> +	}
> +}
> +
> +static uint16_t
> +find_next_cdev(unsigned cdev_type, uint16_t start_cdev_id)
> +{
> +	uint16_t cdev_id, cnt;
> +	struct rte_cryptodev_info info;
> +
> +	cnt = rte_cryptodev_count();
> +	for (cdev_id = start_cdev_id; cdev_id < cnt; cdev_id++) {
> +		rte_cryptodev_info_get(cdev_id, &info);
> +		if (info.dev_type == cdev_type)
> +			return cdev_id;
> +	}
> +
> +	return -1;
> +}
> +
> +static uint16_t
> +find_cdev_socket(int socket_id, unsigned cdev_type)
> +{
> +	uint16_t cdev_id, cnt;
> +	struct rte_cryptodev_info info;
> +	int cdev_socket;
> +
> +	cnt = rte_cryptodev_count();
> +	for (cdev_id = 0; cdev_id < cnt; cdev_id++) {
> +		rte_cryptodev_info_get(cdev_id, &info);
> +		cdev_socket = rte_cryptodev_socket_id(cdev_id);
> +		if ((info.dev_type == cdev_type) && (cdev_socket == socket_id))
> +			return cdev_id;
> +	}
> +
> +	return -1;
> +}
> +
> +static int
> +cryptodevs_init(void)
> +{
> +	struct rte_cryptodev_config dev_conf;
> +	struct rte_cryptodev_qp_conf qp_conf;
> +	struct lcore_conf *qconf;
> +	uint16_t cnt, lcore_id, cdev_id;
> +	int ret, i, socket_id;
> +
> +	if (cdev_type == RTE_CRYPTODEV_QAT_PMD) {
> +		cnt = rte_cryptodev_count_devtype(RTE_CRYPTODEV_QAT_PMD);
> +		printf("Found %u QAT devices\n", cnt);
> +		if (cnt < nb_lcores)
> +			rte_panic("Not enough QAT devices detected, "
> +					"need %u (1 per core), found %d\n",
> +					nb_lcores, cnt);
> +	} else if (cdev_type == RTE_CRYPTODEV_AESNI_MB_PMD) {
> +		printf("Initializing %u AESNI vdevs\n", rte_lcore_count());

Is it possible to remove PMD specific Initialization code from the
application and move driver layer so that new crypto pmd inclusion will
not have any change in this place.

> +		for (i = 0; i < (int)rte_lcore_count(); i++) {
> +			ret = rte_eal_vdev_init(
> +					CRYPTODEV_NAME_AESNI_MB_PMD,
> +					NULL);
> +			if (ret < 0)
> +				rte_panic("Failed to create AESNI-MB vdev\n");
> +		}
> +	} else
> +		rte_panic("Need to set cryptodev type option --cdev\n");
> +
> +	cdev_id = 0;
> +	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> +		if (rte_lcore_is_enabled(lcore_id) == 0)
> +			continue;
> +
> +		socket_id = rte_lcore_to_socket_id(lcore_id);
> +		cdev_id = find_next_cdev(cdev_type, cdev_id);
> +		qconf = &lcore_conf[lcore_id];
> +		qconf->cdev = cdev_id;
> +		qconf->cdev_q = 0;
> +		if (cdev_type == RTE_CRYPTODEV_AESNI_MB_PMD) {
> +			dev_conf.socket_id = socket_id;
> +			printf("Initializing AESNI-MB device %u socket %u\n",
> +					cdev_id, socket_id);
> +		} else {
> +			dev_conf.socket_id = rte_cryptodev_socket_id(cdev_id);
> +			printf("Initialising QAT device %u\n", cdev_id);
> +		}
> +
> +		dev_conf.nb_queue_pairs = 1;
> +		dev_conf.session_mp.nb_objs = CDEV_MP_NB_OBJS;
> +		dev_conf.session_mp.cache_size = CDEV_MP_CACHE_SZ;
> +
> +		if (rte_cryptodev_configure(cdev_id, &dev_conf))
> +			rte_panic("Failed to initialize crypodev %u\n",
> +					cdev_id);
> +
> +		qp_conf.nb_descriptors = CDEV_MP_NB_OBJS;
> +		if (rte_cryptodev_queue_pair_setup(cdev_id, 0, &qp_conf,
> +					dev_conf.socket_id))
> +			rte_panic("Failed to setup queue %u for cdev_id %u\n",
> +					0, cdev_id);
> +		cdev_id++;
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +port_init(uint8_t portid)
> +{
> +	struct rte_eth_dev_info dev_info;
> +	struct rte_eth_txconf *txconf;
> +	uint16_t nb_tx_queue, nb_rx_queue;
> +	uint16_t tx_queueid, rx_queueid, queue, lcore_id;
> +	int ret, socket_id;
> +	struct lcore_conf *qconf;
> +	struct ether_addr ethaddr;
> +
> +	rte_eth_dev_info_get(portid, &dev_info);
> +
> +	printf("Configuring device port %u:\n", portid);
> +
> +	rte_eth_macaddr_get(portid, &ethaddr);
> +	ethaddr_tbl[portid].src = ETHADDR_TO_UINT64(ethaddr);
> +	print_ethaddr("Address: ", &ethaddr);
> +	printf("\n");
> +
> +	nb_rx_queue = get_port_nb_rx_queues(portid);
> +	nb_tx_queue = nb_lcores;
> +
> +	if (nb_rx_queue > dev_info.max_rx_queues)
> +		rte_exit(EXIT_FAILURE, "Error: queue %u not available "
> +				"(max rx queue is %u)\n",
> +				nb_rx_queue, dev_info.max_rx_queues);
> +
> +	if (nb_tx_queue > dev_info.max_tx_queues)
> +		rte_exit(EXIT_FAILURE, "Error: queue %u not available "
> +				"(max tx queue is %u)\n",
> +				nb_tx_queue, dev_info.max_tx_queues);
> +
> +	printf("Creating queues: nb_rx_queue=%d nb_tx_queue=%u...\n",
> +			nb_rx_queue, nb_tx_queue);
> +
> +	ret = rte_eth_dev_configure(portid, nb_rx_queue, nb_tx_queue,
> +			&port_conf);
> +	if (ret < 0)
> +		rte_exit(EXIT_FAILURE, "Cannot configure device: "
> +				"err=%d, port=%d\n", ret, portid);
> +
> +	/* init one TX queue per lcore */
> +	tx_queueid = 0;
> +	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> +		if (rte_lcore_is_enabled(lcore_id) == 0)
> +			continue;
> +
> +		if (numa_on)
> +			socket_id = (uint8_t)rte_lcore_to_socket_id(lcore_id);
> +		else
> +			socket_id = 0;
> +
> +		/* init TX queue */
> +		printf("Setup txq=%u,%d,%d\n", lcore_id, tx_queueid, socket_id);
> +
> +		txconf = &dev_info.default_txconf;
> +		txconf->txq_flags = 0;
> +
> +		ret = rte_eth_tx_queue_setup(portid, tx_queueid, nb_txd,
> +				socket_id, txconf);
> +		if (ret < 0)
> +			rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: "
> +					"err=%d, port=%d\n", ret, portid);
> +
> +		qconf = &lcore_conf[lcore_id];
> +		qconf->tx_queue_id[portid] = tx_queueid;
> +		tx_queueid++;
> +
> +		/* init RX queues */
> +		for (queue = 0; queue < qconf->nb_rx_queue; ++queue) {
> +			if (portid != qconf->rx_queue_list[queue].port_id)
> +				continue;
> +
> +			rx_queueid = qconf->rx_queue_list[queue].queue_id;
> +
> +			printf("Setup rxq=%d,%d,%d\n", portid, rx_queueid,
> +					socket_id);
> +
> +			ret = rte_eth_rx_queue_setup(portid, rx_queueid,
> +					nb_rxd,	socket_id, NULL,
> +					socket_ctx[socket_id].mbuf_pool);
> +			if (ret < 0)
> +				rte_exit(EXIT_FAILURE,
> +					"rte_eth_rx_queue_setup: err=%d, "
> +					"port=%d\n", ret, portid);
> +		}
> +	}
> +	printf("\n");
> +}
> +
> +static void
> +pool_init(struct socket_ctx *ctx, int socket_id, unsigned nb_mbuf)
> +{
> +	char s[64];
> +
> +	snprintf(s, sizeof(s), "mbuf_pool_%d", socket_id);
> +	ctx->mbuf_pool = rte_pktmbuf_pool_create(s, nb_mbuf,
> +			MEMPOOL_CACHE_SIZE, ipsec_metadata_size(),
> +			RTE_MBUF_DEFAULT_BUF_SIZE,
> +			socket_id);
> +	if (ctx->mbuf_pool == NULL)
> +		rte_exit(EXIT_FAILURE, "Cannot init mbuf pool on socket %d\n",
> +				socket_id);
> +	else
> +		printf("Allocated mbuf pool on socket %d\n", socket_id);
> +}
> +
> +int
> +main(int argc, char **argv)
> +{
> +	int ret;
> +	unsigned lcore_id, nb_ports;
> +	uint8_t portid, socket_id;
> +
> +	/* init EAL */
> +	ret = rte_eal_init(argc, argv);
> +	if (ret < 0)
> +		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
> +	argc -= ret;
> +	argv += ret;
> +
> +	/* parse application arguments (after the EAL ones) */
> +	ret = parse_args(argc, argv);
> +	if (ret < 0)
> +		rte_exit(EXIT_FAILURE, "Invalid parameters\n");
> +
> +	if (ep < 0)
> +		rte_exit(EXIT_FAILURE, "need to choose either EP0 or EP1\n");
> +
> +	if ((unprotected_port_mask & enabled_port_mask) !=
> +			unprotected_port_mask)
> +		rte_exit(EXIT_FAILURE, "Invalid unprotected portmask 0x%x\n",
> +				unprotected_port_mask);
> +
> +	nb_ports = rte_eth_dev_count();
> +	if (nb_ports > RTE_MAX_ETHPORTS)
> +		nb_ports = RTE_MAX_ETHPORTS;
> +
> +	if (check_params() < 0)
> +		rte_exit(EXIT_FAILURE, "check_params failed\n");
> +
> +	ret = init_lcore_rx_queues();
> +	if (ret < 0)
> +		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n");
> +
> +	nb_lcores = rte_lcore_count();
> +
> +	cryptodevs_init();
> +
> +	/* Replicate each contex per socket */
> +	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> +		if (rte_lcore_is_enabled(lcore_id) == 0)
> +			continue;
> +
> +		if (numa_on)
> +			socket_id = (uint8_t)rte_lcore_to_socket_id(lcore_id);
> +		else
> +			socket_id = 0;
> +
> +		if (socket_ctx[socket_id].mbuf_pool)
> +			continue;
> +
> +		sa_init(&socket_ctx[socket_id], socket_id, ep,
> +				find_cdev_socket(socket_id, cdev_type));
> +
> +		sp_init(&socket_ctx[socket_id], socket_id, ep);
> +
> +		rt_init(&socket_ctx[socket_id], socket_id, ep);
> +
> +		pool_init(&socket_ctx[socket_id], socket_id, NB_MBUF);
> +	}
> +
> +	for (portid = 0; portid < nb_ports; portid++) {
> +		if ((enabled_port_mask & (1 << portid)) == 0)
> +			continue;
> +
> +		port_init(portid);
> +	}
> +
> +	/* start ports */
> +	for (portid = 0; portid < nb_ports; portid++) {
> +		if ((enabled_port_mask & (1 << portid)) == 0)
> +			continue;
> +
> +		/* Start device */
> +		ret = rte_eth_dev_start(portid);
> +		if (ret < 0)
> +			rte_exit(EXIT_FAILURE, "rte_eth_dev_start: "
> +					"err=%d, port=%d\n", ret, portid);
> +		/*
> +		 * If enabled, put device in promiscuous mode.
> +		 * This allows IO forwarding mode to forward packets
> +		 * to itself through 2 cross-connected  ports of the
> +		 * target machine.
> +		 */
> +		if (promiscuous_on)
> +			rte_eth_promiscuous_enable(portid);
> +	}
> +
> +	check_all_ports_link_status((uint8_t)nb_ports, enabled_port_mask);
> +
> +	/* launch per-lcore init on every lcore */
> +	rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
> +	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
> +		if (rte_eal_wait_lcore(lcore_id) < 0)
> +			return -1;
> +	}
> +
> +	return 0;
> +}
> diff --git a/examples/ipsec-secgw/ipsec.c b/examples/ipsec-secgw/ipsec.c
> new file mode 100644
> index 0000000..83e3326
> --- /dev/null
> +++ b/examples/ipsec-secgw/ipsec.c
> @@ -0,0 +1,138 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <netinet/in.h>
> +#include <netinet/ip.h>
> +
> +#include <rte_branch_prediction.h>
> +#include <rte_log.h>
> +#include <rte_crypto.h>
> +#include <rte_cryptodev.h>
> +#include <rte_mbuf.h>
> +
> +#include "ipsec.h"
> +
> +static inline uint16_t
> +ipsec_processing(uint8_t cdev_id, uint16_t qp_id, struct rte_mbuf *pkts[],
> +		struct ipsec_sa *sas[], uint16_t nb_pkts, uint16_t max_pkts)
> +{
> +	int ret, i, j;
> +	struct ipsec_mbuf_metadata *priv;
> +	struct rte_mbuf_offload *ol;
> +	struct ipsec_sa *sa;
> +
> +	j = 0;
> +	for (i = 0; i < nb_pkts; i++) {
> +		rte_prefetch0(sas[i]);
> +		rte_prefetch0(pkts[i]);
> +
> +		priv = get_priv(pkts[i]);
> +		ol = &priv->ol;
> +		sa = sas[i];
> +		priv->sa = sa;
> +
> +		IPSEC_ASSERT(sa != NULL);
> +
> +		__rte_pktmbuf_offload_reset(ol, RTE_PKTMBUF_OL_CRYPTO);
> +
> +		rte_crypto_op_attach_session(&ol->op.crypto,
> +				sa->crypto_session);
> +
> +		pkts[i]->offload_ops = ol;
> +
> +		ret = sa->pre_crypto(pkts[i], sa, &ol->op.crypto);
> +		if (unlikely(ret))
> +			rte_pktmbuf_free(pkts[i]);
> +		else
> +			pkts[j++] = pkts[i];
> +	}
> +	nb_pkts = j;
> +
> +	ret = rte_cryptodev_enqueue_burst(cdev_id, qp_id, pkts, nb_pkts);
> +	if (ret < nb_pkts) {
> +		IPSEC_LOG(DEBUG, IPSEC, "Cryptodev %u queue %u:"
> +				" enqueued %u packets (out of %u)\n",
> +				 cdev_id, qp_id, ret, nb_pkts);
> +		for (i = ret; i < nb_pkts; i++)
> +			rte_pktmbuf_free(pkts[i]);
> +	}
> +
> +	nb_pkts = rte_cryptodev_dequeue_burst(cdev_id, qp_id, pkts, max_pkts);
> +	if ((nb_pkts != 0) && (nb_pkts < max_pkts))
> +		IPSEC_LOG(DEBUG, IPSEC, "Cryptodev %u queue %u:"
> +				" dequeued %u packets (out of %u)\n",
> +				 cdev_id, qp_id, nb_pkts, max_pkts);
> +
> +	j = 0;
> +	for (i = 0; i < nb_pkts; i++) {
> +		rte_prefetch0(pkts[i]);
> +
> +		priv = get_priv(pkts[i]);
> +		ol = &priv->ol;
> +		sa = priv->sa;
> +		rte_prefetch0(sa);
> +
> +		IPSEC_ASSERT(sa != NULL);
> +
> +		ret = sa->post_crypto(pkts[i], sa, &ol->op.crypto);
> +		if (unlikely(ret))
> +			rte_pktmbuf_free(pkts[i]);
> +		else
> +			pkts[j++] = pkts[i];
> +	}
> +
> +	/* return packets */
> +	return j;
> +}
> +
> +uint16_t
> +ipsec_inbound(struct ipsec_ctx *ctx, struct rte_mbuf *pkts[],
> +		uint16_t nb_pkts, uint16_t len)
> +{
> +	struct ipsec_sa *sas[nb_pkts];
> +
> +	inbound_sa_lookup(ctx->sa_ctx, pkts, sas, nb_pkts);
> +
> +	return ipsec_processing(ctx->cdev, ctx->queue, pkts, sas, nb_pkts, len);
> +}
> +
> +uint16_t
> +ipsec_outbound(struct ipsec_ctx *ctx, struct rte_mbuf *pkts[],
> +		uint32_t sa_idx[], uint16_t nb_pkts, uint16_t len)
> +{
> +	struct ipsec_sa *sas[nb_pkts];
> +
> +	outbound_sa_lookup(ctx->sa_ctx, sa_idx, sas, nb_pkts);
> +
> +	return ipsec_processing(ctx->cdev, ctx->queue, pkts, sas, nb_pkts, len);
> +}
> diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h
> new file mode 100644
> index 0000000..abf9d5f
> --- /dev/null
> +++ b/examples/ipsec-secgw/ipsec.h
> @@ -0,0 +1,184 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef __IPSEC_H__
> +#define __IPSEC_H__
> +
> +#include <stdint.h>
> +#include <netinet/in.h>
> +#include <netinet/ip.h>
> +
> +#include <rte_mbuf_offload.h>
> +#include <rte_byteorder.h>
> +#include <rte_ip.h>
> +
> +#define RTE_LOGTYPE_IPSEC       RTE_LOGTYPE_USER1
> +#define RTE_LOGTYPE_IPSEC_ESP   RTE_LOGTYPE_USER2
> +#define RTE_LOGTYPE_IPSEC_IPIP  RTE_LOGTYPE_USER3
> +
> +#ifdef IPSEC_DEBUG
> +#define IPSEC_ASSERT(exp)                                            \
> +if (!(exp)) {                                                        \
> +	rte_panic("line%d\tassert \"" #exp "\" failed\n", __LINE__); \
> +}
> +
> +#define IPSEC_LOG RTE_LOG
> +#else
> +#define IPSEC_ASSERT(exp) do {} while (0)
> +#define IPSEC_LOG(...) do {} while (0)
> +#endif /* IPSEC_DEBUG */
> +
> +#define MAX_DIGEST_SIZE 32 /* Bytes -- 256 bits */
> +
> +#define uint32_t_to_char(ip, a, b, c, d) do {\
> +		*a = (unsigned char)(ip >> 24 & 0xff);\
> +		*b = (unsigned char)(ip >> 16 & 0xff);\
> +		*c = (unsigned char)(ip >> 8 & 0xff);\
> +		*d = (unsigned char)(ip & 0xff);\
> +	} while (0)
> +
> +#define DEFAULT_MAX_CATEGORIES	1
> +
> +#define IPSEC_SA_MAX_ENTRIES (64) /* must be power of 2, max 2 power 30 */
> +#define SPI2IDX(spi) (spi & (IPSEC_SA_MAX_ENTRIES - 1))
> +#define INVALID_SPI (0)
> +
> +#define DISCARD (0x80000000)
> +#define BYPASS (0x40000000)
> +#define PROTECT_MASK (0x3fffffff)
> +#define PROTECT(sa_idx) (SPI2IDX(sa_idx) & PROTECT_MASK) /* SA idx 30 bits */
> +
> +#define IPSEC_XFORM_MAX 2
> +
> +struct rte_crypto_xform;
> +struct ipsec_xform;
> +struct rte_cryptodev_session;
> +struct rte_mbuf;
> +
> +struct replay {
> +	uint32_t seq_h;
> +};
> +
> +struct lifetime {
> +	uint64_t bytes;
> +	uint64_t seconds;
> +};
> +
> +struct ipsec_sa;
> +
> +typedef int (*ipsec_xform_fn)(struct rte_mbuf *m, struct ipsec_sa *sa,
> +		struct rte_crypto_op *cop);
> +
> +struct ipsec_sa {
> +	uint32_t spi;
> +	uint32_t seq;
> +	uint32_t src;
> +	uint32_t dst;
> +	struct rte_cryptodev_session *crypto_session;
> +	struct rte_crypto_xform *xforms;
> +	enum rte_crypto_cipher_algorithm cipher_algo;
> +	enum rte_crypto_auth_algorithm auth_algo;
> +	uint16_t digest_len;
> +	uint16_t iv_len;
> +	uint16_t block_size;
> +	ipsec_xform_fn pre_crypto;
> +	ipsec_xform_fn post_crypto;
> +	uint32_t flags;
> +	/* does not apply if no automated SA management (IKE) support */
> +	struct lifetime current;
> +	struct lifetime soft;
> +	struct lifetime hard;
> +	struct replay replay;
> +} __rte_cache_aligned;
> +
> +struct ipsec_mbuf_metadata {
> +	struct ipsec_sa *sa;
> +	struct rte_mbuf_offload ol;
> +};
> +
> +struct ipsec_ctx {
> +	uint16_t cdev;
> +	uint16_t queue;
> +	struct sa_ctx *sa_ctx;
> +};
> +
> +struct socket_ctx {
> +	struct sa_ctx *sa_ipv4_in;
> +	struct sa_ctx *sa_ipv4_out;
> +	struct sp_ctx *sp_ipv4_in;
> +	struct sp_ctx *sp_ipv4_out;
> +	struct rt_ctx *rt_ipv4;
> +	struct rte_mempool *mbuf_pool;
> +};
> +
> +uint16_t
> +ipsec_inbound(struct ipsec_ctx *ctx, struct rte_mbuf *pkts[],
> +		uint16_t nb_pkts, uint16_t len);
> +
> +uint16_t
> +ipsec_outbound(struct ipsec_ctx *ctx, struct rte_mbuf *pkts[],
> +		uint32_t sa_idx[], uint16_t nb_pkts, uint16_t len);
> +
> +static inline uint16_t
> +ipsec_metadata_size(void)
> +{
> +	return sizeof(struct ipsec_mbuf_metadata);
> +}
> +
> +static inline struct ipsec_mbuf_metadata *
> +get_priv(struct rte_mbuf *m)
> +{
> +	return RTE_PTR_ADD(m, sizeof(struct rte_mbuf));
> +}
> +
> +int
> +inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx);
> +
> +void
> +inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
> +		struct ipsec_sa *sa[], uint16_t nb_pkts);
> +
> +void
> +outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
> +		struct ipsec_sa *sa[], uint16_t nb_pkts);
> +
> +void
> +sp_init(struct socket_ctx *ctx, int socket_id, unsigned ep);
> +
> +void
> +sa_init(struct socket_ctx *ctx, int socket_id, unsigned ep, uint16_t cdev_id);
> +
> +void
> +rt_init(struct socket_ctx *ctx, int socket_id, unsigned ep);
> +
> +#endif /* __IPSEC_H__ */
> diff --git a/examples/ipsec-secgw/rt.c b/examples/ipsec-secgw/rt.c
> new file mode 100644
> index 0000000..82064b2
> --- /dev/null
> +++ b/examples/ipsec-secgw/rt.c
> @@ -0,0 +1,131 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/*
> + * Routing Table (RT)
> + */
> +#include <rte_lpm.h>
> +#include <rte_errno.h>
> +
> +#include "ipsec.h"
> +
> +#define RT_IPV4_MAX_RULES         64
> +
> +struct ipv4_route {
> +	uint32_t ip;
> +	uint8_t  depth;
> +	uint8_t  if_out;
> +};
> +
> +/* In the default routing table we have:
> + * ep0 protected ports 0 and 1, and unprotected ports 2 and 3.
> + */
> +static struct ipv4_route rt_ipv4_ep0[] = {
> +	{ IPv4(172, 16, 2, 5), 32, 0 },
> +	{ IPv4(172, 16, 2, 6), 32, 0 },
> +	{ IPv4(172, 16, 2, 7), 32, 1 },
> +	{ IPv4(172, 16, 2, 8), 32, 1 },
> +
> +	{ IPv4(192, 168, 115, 0), 24, 2 },
> +	{ IPv4(192, 168, 116, 0), 24, 2 },
> +	{ IPv4(192, 168, 117, 0), 24, 3 },
> +	{ IPv4(192, 168, 118, 0), 24, 3 }
> +};
> +
> +/* In the default routing table we have:
> + * ep1 protected ports 0 and 1, and unprotected ports 2 and 3.
> + */
> +static struct ipv4_route rt_ipv4_ep1[] = {
> +	{ IPv4(172, 16, 1, 5), 32, 2 },
> +	{ IPv4(172, 16, 1, 6), 32, 2 },
> +	{ IPv4(172, 16, 1, 7), 32, 3 },
> +	{ IPv4(172, 16, 1, 8), 32, 3 },
> +
> +	{ IPv4(192, 168, 105, 0), 24, 0 },
> +	{ IPv4(192, 168, 106, 0), 24, 0 },
> +	{ IPv4(192, 168, 107, 0), 24, 1 },
> +	{ IPv4(192, 168, 108, 0), 24, 1 }
> +};
> +
> +void
> +rt_init(struct socket_ctx *ctx, int socket_id, unsigned ep)
> +{
> +	char name[PATH_MAX];
> +	unsigned i;
> +	int ret;
> +	struct rte_lpm *lpm;
> +	struct ipv4_route *rt;
> +	char a, b, c, d;
> +	unsigned nb_routes;
> +
> +	if (ctx == NULL)
> +		rte_exit(EXIT_FAILURE, "NULL context.\n");
> +
> +	if (ctx->rt_ipv4 != NULL)
> +		rte_exit(EXIT_FAILURE, "Routing Table for socket %u already "
> +			"initialized\n", socket_id);
> +
> +	printf("Creating Routing Table (RT) context with %u max routes\n",
> +			RT_IPV4_MAX_RULES);
> +
> +	if (ep == 0) {
> +		rt = rt_ipv4_ep0;
> +		nb_routes = RTE_DIM(rt_ipv4_ep0);
> +	} else if (ep == 1) {
> +		rt = rt_ipv4_ep1;
> +		nb_routes = RTE_DIM(rt_ipv4_ep1);
> +	} else
> +		rte_exit(EXIT_FAILURE, "Invalid EP value %u. Only 0 or 1 "
> +			"supported.\n", ep);
> +
> +	/* create the LPM table */
> +	snprintf(name, sizeof(name), "%s_%u", "rt_ipv4", socket_id);
> +	lpm = rte_lpm_create(name, socket_id, RT_IPV4_MAX_RULES, 0);
> +	if (lpm == NULL)
> +		rte_exit(EXIT_FAILURE, "Unable to create LPM table "
> +			"on socket %d\n", socket_id);
> +
> +	/* populate the LPM table */
> +	for (i = 0; i < nb_routes; i++) {
> +		ret = rte_lpm_add(lpm, rt[i].ip, rt[i].depth, rt[i].if_out);
> +		if (ret < 0)
> +			rte_exit(EXIT_FAILURE, "Unable to add entry num %u to "
> +				"LPM table on socket %d\n", i, socket_id);
> +
> +		uint32_t_to_char(rt[i].ip, &a, &b, &c, &d);
> +		printf("LPM: Adding route %hhu.%hhu.%hhu.%hhu/%hhu (%hhu)\n",
> +				a, b, c, d, rt[i].depth, rt[i].if_out);
> +	}
> +
> +	ctx->rt_ipv4 = (struct rt_ctx *)lpm;
> +}
> diff --git a/examples/ipsec-secgw/sa.c b/examples/ipsec-secgw/sa.c
> new file mode 100644
> index 0000000..5ea7592
> --- /dev/null
> +++ b/examples/ipsec-secgw/sa.c
> @@ -0,0 +1,391 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/*
> + * Security Associations
> + */
> +#include <netinet/ip.h>
> +
> +#include <rte_memzone.h>
> +#include <rte_crypto.h>
> +#include <rte_cryptodev.h>
> +#include <rte_byteorder.h>
> +#include <rte_errno.h>
> +
> +#include "ipsec.h"
> +#include "esp.h"
> +
> +/* SAs EP0 Outbound */
> +const struct ipsec_sa sa_ep0_out[] = {
> +	{ 5, 0, IPv4(172, 16, 1, 5), IPv4(172, 16, 2, 5),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 6, 0, IPv4(172, 16, 1, 6), IPv4(172, 16, 2, 6),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 7, 0, IPv4(172, 16, 1, 7), IPv4(172, 16, 2, 7),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 8, 0, IPv4(172, 16, 1, 8), IPv4(172, 16, 2, 8),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,

IMO, a SA with NULL crypto (rfc2410) operation will be useful for ESP
packet processing debugging and measuring the performance impact with
the crypto block in fast path. 


> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } }
> +};
> +
> +/* SAs EP0 Inbound */
> +const struct ipsec_sa sa_ep0_in[] = {
> +	{ 5, 0, IPv4(172, 16, 2, 5), IPv4(172, 16, 1, 5),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 6, 0, IPv4(172, 16, 2, 6), IPv4(172, 16, 1, 6),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 7, 0, IPv4(172, 16, 2, 7), IPv4(172, 16, 1, 7),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 8, 0, IPv4(172, 16, 2, 8), IPv4(172, 16, 1, 8),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } }
> +};
> +
> +/* SAs EP1 Outbound */
> +const struct ipsec_sa sa_ep1_out[] = {
> +	{ 5, 0, IPv4(172, 16, 2, 5), IPv4(172, 16, 1, 5),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 6, 0, IPv4(172, 16, 2, 6), IPv4(172, 16, 1, 6),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 7, 0, IPv4(172, 16, 2, 7), IPv4(172, 16, 1, 7),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 8, 0, IPv4(172, 16, 2, 8), IPv4(172, 16, 1, 8),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_outbound_pre_crypto,
> +		esp4_tunnel_outbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } }
> +};
> +
> +/* SAs EP1 Inbound */
> +const struct ipsec_sa sa_ep1_in[] = {
> +	{ 5, 0, IPv4(172, 16, 1, 5), IPv4(172, 16, 2, 5),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 6, 0, IPv4(172, 16, 1, 6), IPv4(172, 16, 2, 6),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 7, 0, IPv4(172, 16, 1, 7), IPv4(172, 16, 2, 7),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } },
> +	{ 8, 0, IPv4(172, 16, 1, 8), IPv4(172, 16, 2, 8),
> +		NULL, NULL,
> +		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		12, 16, 16,
> +		esp4_tunnel_inbound_pre_crypto,
> +		esp4_tunnel_inbound_post_crypto,
> +		0, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0 } }
> +};
> +
> +static uint8_t cipher_key[256] = "sixteenbytes key";
> +
> +/* AES CBC xform */
> +const struct rte_crypto_xform aescbc_enc_xf = {
> +	NULL,
> +	RTE_CRYPTO_XFORM_CIPHER,
> +	.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
> +		.key = { cipher_key, 0, 16 } }
> +};
> +
> +const struct rte_crypto_xform aescbc_dec_xf = {
> +	NULL,
> +	RTE_CRYPTO_XFORM_CIPHER,
> +	.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
> +		.key = { cipher_key, 0, 16 } }
> +};
> +
> +static uint8_t auth_key[256] = "twentybytes hash key";
> +
> +/* SHA1 HMAC xform */
> +const struct rte_crypto_xform sha1hmac_gen_xf = {
> +	NULL,
> +	RTE_CRYPTO_XFORM_AUTH,
> +	.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		.key = { auth_key, 0, 20 }, 12, 0 }
> +};
> +
> +const struct rte_crypto_xform sha1hmac_verify_xf = {
> +	NULL,
> +	RTE_CRYPTO_XFORM_AUTH,
> +	.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
> +		.key = { auth_key, 0, 20 }, 12, 0 }
> +};
> +
> +struct sa_ctx {
> +	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
> +	struct {
> +		struct rte_crypto_xform a;
> +		struct rte_crypto_xform b;
> +	} xf[IPSEC_SA_MAX_ENTRIES];
> +};
> +
> +static struct sa_ctx *
> +sa_ipv4_create(const char *name, int socket_id)
> +{
> +	char s[PATH_MAX];
> +	struct sa_ctx *sa_ctx;
> +	unsigned mz_size;
> +	const struct rte_memzone *mz;
> +
> +	snprintf(s, sizeof(s), "%s_%u", name, socket_id);
> +
> +	/* Create SA array table */
> +	printf("Creating SA context with %u maximum entries\n",
> +			IPSEC_SA_MAX_ENTRIES);
> +
> +	mz_size = sizeof(struct sa_ctx);
> +	mz = rte_memzone_reserve(s, mz_size, socket_id,
> +			RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY);
> +	if (mz == NULL) {
> +		printf("Failed to allocate SA DB memory\n");
> +		rte_errno = -ENOMEM;
> +		return NULL;
> +	}
> +
> +	sa_ctx = (struct sa_ctx *)mz->addr;
> +
> +	return sa_ctx;
> +}
> +
> +static int
> +sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
> +		unsigned nb_entries, uint16_t cdev_id, unsigned inbound)
> +{
> +	struct ipsec_sa *sa;
> +	unsigned i, idx;
> +
> +	for (i = 0; i < nb_entries; i++) {
> +		idx = SPI2IDX(entries[i].spi);
> +		sa = &sa_ctx->sa[idx];
> +		if (sa->spi != 0) {
> +			printf("Index %u already in use by SPI %u\n",
> +					idx, sa->spi);
> +			return -EINVAL;
> +		}
> +		*sa = entries[i];
> +		sa->src = rte_cpu_to_be_32(sa->src);
> +		sa->dst = rte_cpu_to_be_32(sa->dst);
> +		if (inbound) {
> +			sa_ctx->xf[idx].a = sha1hmac_verify_xf;
> +			sa_ctx->xf[idx].b = aescbc_dec_xf;
> +		} else { /* outbound */
> +			sa_ctx->xf[idx].a = aescbc_enc_xf;
> +			sa_ctx->xf[idx].b = sha1hmac_gen_xf;
> +		}
> +		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
> +		sa_ctx->xf[idx].b.next = NULL;
> +		sa->xforms = &sa_ctx->xf[idx].a;
> +
> +		sa->crypto_session = rte_cryptodev_session_create(cdev_id,
> +				sa->xforms);
> +	}
> +
> +	return 0;
> +}
> +
> +static inline int
> +sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
> +		unsigned nb_entries, uint16_t cdev_id)
> +{
> +	return sa_add_rules(sa_ctx, entries, nb_entries, cdev_id, 0);
> +}
> +
> +static inline int
> +sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
> +		unsigned nb_entries, uint16_t cdev_id)
> +{
> +	return sa_add_rules(sa_ctx, entries, nb_entries, cdev_id, 1);
> +}
> +
> +void
> +sa_init(struct socket_ctx *ctx, int socket_id, unsigned ep, uint16_t cdev_id)
> +{
> +	const struct ipsec_sa *sa_out_entries, *sa_in_entries;
> +	unsigned nb_out_entries, nb_in_entries;
> +	const char *name;
> +
> +	if (ctx == NULL)
> +		rte_exit(EXIT_FAILURE, "NULL context.\n");
> +
> +	if (ctx->sa_ipv4_in != NULL)
> +		rte_exit(EXIT_FAILURE, "Inbound SA DB for socket %u already "
> +				"initialized\n", socket_id);
> +
> +	if (ctx->sa_ipv4_out != NULL)
> +		rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
> +				"initialized\n", socket_id);
> +
> +	if (ep == 0) {
> +		sa_out_entries = sa_ep0_out;
> +		nb_out_entries = RTE_DIM(sa_ep0_out);
> +		sa_in_entries = sa_ep0_in;
> +		nb_in_entries = RTE_DIM(sa_ep0_in);
> +	} else if (ep == 1) {
> +		sa_out_entries = sa_ep1_out;
> +		nb_out_entries = RTE_DIM(sa_ep1_out);
> +		sa_in_entries = sa_ep1_in;
> +		nb_in_entries = RTE_DIM(sa_ep1_in);
> +	} else
> +		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
> +				"Only 0 or 1 supported.\n", ep);
> +
> +	name = "sa_ipv4_in";
> +	ctx->sa_ipv4_in = sa_ipv4_create(name, socket_id);
> +	if (ctx->sa_ipv4_in == NULL)
> +		rte_exit(EXIT_FAILURE, "Error [%d] creating SA context %s "
> +				"in socket %d\n", rte_errno, name, socket_id);
> +
> +	name = "sa_ipv4_out";
> +	ctx->sa_ipv4_out = sa_ipv4_create(name, socket_id);
> +	if (ctx->sa_ipv4_out == NULL)
> +		rte_exit(EXIT_FAILURE, "Error [%d] creating SA context %s "
> +				"in socket %d\n", rte_errno, name, socket_id);
> +
> +	sa_in_add_rules(ctx->sa_ipv4_in, sa_in_entries,
> +			nb_in_entries, cdev_id);
> +
> +	sa_out_add_rules(ctx->sa_ipv4_out, sa_out_entries,
> +			nb_out_entries, cdev_id);
> +}
> +
> +int
> +inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx)
> +{
> +	struct ipsec_mbuf_metadata *priv;
> +
> +	priv = RTE_PTR_ADD(m, sizeof(struct rte_mbuf));
> +
> +	return (sa_ctx->sa[sa_idx].spi == priv->sa->spi);
> +}
> +
> +void
> +inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
> +		struct ipsec_sa *sa[], uint16_t nb_pkts)
> +{
> +	unsigned i;
> +	uint32_t *src, spi;
> +
> +	for (i = 0; i < nb_pkts; i++) {
> +		spi = rte_pktmbuf_mtod_offset(pkts[i], struct esp_hdr *,
> +				sizeof(struct ip))->spi;
> +		if (spi == INVALID_SPI)
> +			continue;
> +
> +		sa[i] = &sa_ctx->sa[SPI2IDX(spi)];
> +		if (spi != sa[i]->spi) {
> +			sa[i] = NULL;
> +			continue;
> +		}
> +
> +		src = rte_pktmbuf_mtod_offset(pkts[i], uint32_t *,
> +				offsetof(struct ip, ip_src));
> +		if ((sa[i]->src != *src) || (sa[i]->dst != *(src + 1)))
> +			sa[i] = NULL;
> +	}
> +}
> +
> +void
> +outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
> +		struct ipsec_sa *sa[], uint16_t nb_pkts)
> +{
> +	unsigned i;
> +
> +	for (i = 0; i < nb_pkts; i++)
> +		sa[i] = &sa_ctx->sa[sa_idx[i]];
> +}
> diff --git a/examples/ipsec-secgw/sp.c b/examples/ipsec-secgw/sp.c
> new file mode 100644
> index 0000000..219e478
> --- /dev/null
> +++ b/examples/ipsec-secgw/sp.c
> @@ -0,0 +1,324 @@
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/*
> + * Security Policies
> + */
> +#include <netinet/ip.h>
> +
> +#include <rte_acl.h>
> +
> +#include "ipsec.h"
> +
> +#define MAX_ACL_RULE_NUM	1000
> +
> +/*
> + * Rule and trace formats definitions.
> + */
> +enum {
> +	PROTO_FIELD_IPV4,
> +	SRC_FIELD_IPV4,
> +	DST_FIELD_IPV4,
> +	SRCP_FIELD_IPV4,
> +	DSTP_FIELD_IPV4,
> +	NUM_FIELDS_IPV4
> +};
> +
> +/*
> + * That effectively defines order of IPV4 classifications:
> + *  - PROTO
> + *  - SRC IP ADDRESS
> + *  - DST IP ADDRESS
> + *  - PORTS (SRC and DST)
> + */
> +enum {
> +	RTE_ACL_IPV4_PROTO,
> +	RTE_ACL_IPV4_SRC,
> +	RTE_ACL_IPV4_DST,
> +	RTE_ACL_IPV4_PORTS,
> +	RTE_ACL_IPV4_NUM
> +};
> +
> +struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
> +	{
> +	.type = RTE_ACL_FIELD_TYPE_BITMASK,
> +	.size = sizeof(uint8_t),
> +	.field_index = PROTO_FIELD_IPV4,
> +	.input_index = RTE_ACL_IPV4_PROTO,
> +	.offset = 0,
> +	},
> +	{
> +	.type = RTE_ACL_FIELD_TYPE_MASK,
> +	.size = sizeof(uint32_t),
> +	.field_index = SRC_FIELD_IPV4,
> +	.input_index = RTE_ACL_IPV4_SRC,
> +	.offset = offsetof(struct ip, ip_src) -	offsetof(struct ip, ip_p)
> +	},
> +	{
> +	.type = RTE_ACL_FIELD_TYPE_MASK,
> +	.size = sizeof(uint32_t),
> +	.field_index = DST_FIELD_IPV4,
> +	.input_index = RTE_ACL_IPV4_DST,
> +	.offset = offsetof(struct ip, ip_dst) - offsetof(struct ip, ip_p)
> +	},
> +	{
> +	.type = RTE_ACL_FIELD_TYPE_RANGE,
> +	.size = sizeof(uint16_t),
> +	.field_index = SRCP_FIELD_IPV4,
> +	.input_index = RTE_ACL_IPV4_PORTS,
> +	.offset = sizeof(struct ip) - offsetof(struct ip, ip_p)
> +	},
> +	{
> +	.type = RTE_ACL_FIELD_TYPE_RANGE,
> +	.size = sizeof(uint16_t),
> +	.field_index = DSTP_FIELD_IPV4,
> +	.input_index = RTE_ACL_IPV4_PORTS,
> +	.offset = sizeof(struct ip) - offsetof(struct ip, ip_p) +
> +		sizeof(uint16_t)
> +	},
> +};
> +
> +RTE_ACL_RULE_DEF(acl4_rules, RTE_DIM(ipv4_defs));
> +
> +const struct acl4_rules acl4_rules_in[] = {
> +	{
> +	.data = {.userdata = PROTECT(5), .category_mask = 1, .priority = 1},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 105, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	},
> +	{
> +	.data = {.userdata = PROTECT(6), .category_mask = 1, .priority = 2},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 106, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	},
> +	{
> +	.data = {.userdata = PROTECT(7), .category_mask = 1, .priority = 3},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 107, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	},
> +	{
> +	.data = {.userdata = PROTECT(8), .category_mask = 1, .priority = 4},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 108, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	}
> +};
> +
> +const struct acl4_rules acl4_rules_out[] = {
> +	{
> +	.data = {.userdata = PROTECT(5), .category_mask = 1, .priority = 1},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 115, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	},
> +	{
> +	.data = {.userdata = PROTECT(6), .category_mask = 1, .priority = 2},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 116, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	},
> +	{
> +	.data = {.userdata = PROTECT(7), .category_mask = 1, .priority = 3},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 117, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	},
> +	{
> +	.data = {.userdata = PROTECT(8), .category_mask = 1, .priority = 4},
> +	/* destination IPv4 */
> +	.field[2] = {.value.u32 = IPv4(192, 168, 118, 0),
> +				.mask_range.u32 = 24,},
> +	/* source port */
> +	.field[3] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
> +	/* destination port */
> +	.field[4] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
> +	}
> +};
> +
> +static void
> +print_one_ipv4_rule(const struct acl4_rules *rule, int extra)
> +{
> +	unsigned char a, b, c, d;
> +
> +	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
> +			&a, &b, &c, &d);
> +	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
> +			rule->field[SRC_FIELD_IPV4].mask_range.u32);
> +	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
> +			&a, &b, &c, &d);
> +	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
> +			rule->field[DST_FIELD_IPV4].mask_range.u32);
> +	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
> +		rule->field[SRCP_FIELD_IPV4].value.u16,
> +		rule->field[SRCP_FIELD_IPV4].mask_range.u16,
> +		rule->field[DSTP_FIELD_IPV4].value.u16,
> +		rule->field[DSTP_FIELD_IPV4].mask_range.u16,
> +		rule->field[PROTO_FIELD_IPV4].value.u8,
> +		rule->field[PROTO_FIELD_IPV4].mask_range.u8);
> +	if (extra)
> +		printf("0x%x-0x%x-0x%x ",
> +			rule->data.category_mask,
> +			rule->data.priority,
> +			rule->data.userdata);
> +}
> +
> +static inline void
> +dump_ipv4_rules(const struct acl4_rules *rule, int num, int extra)
> +{
> +	int i;
> +
> +	for (i = 0; i < num; i++, rule++) {
> +		printf("\t%d:", i + 1);
> +		print_one_ipv4_rule(rule, extra);
> +		printf("\n");
> +	}
> +}
> +
> +static struct rte_acl_ctx *
> +acl4_init(const char *name, int socketid, const struct acl4_rules *rules,
> +		unsigned rules_nb)
> +{
> +	char s[PATH_MAX];
> +	struct rte_acl_param acl_param;
> +	struct rte_acl_config acl_build_param;
> +	struct rte_acl_ctx *ctx;
> +
> +	printf("Creating SP context with %u max rules\n", MAX_ACL_RULE_NUM);
> +
> +	memset(&acl_param, 0, sizeof(acl_param));
> +
> +	/* Create ACL contexts */
> +	snprintf(s, sizeof(s), "%s_%d", name, socketid);
> +
> +	printf("IPv4 %s entries [%u]:\n", s, rules_nb);
> +	dump_ipv4_rules(rules, rules_nb, 1);
> +
> +	acl_param.name = s;
> +	acl_param.socket_id = socketid;
> +	acl_param.rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ipv4_defs));
> +	acl_param.max_rule_num = MAX_ACL_RULE_NUM;
> +
> +	ctx = rte_acl_create(&acl_param);
> +	if (ctx == NULL)
> +		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
> +
> +	if (rte_acl_add_rules(ctx, (const struct rte_acl_rule *)rules,
> +				rules_nb) < 0)
> +		rte_exit(EXIT_FAILURE, "add rules failed\n");
> +
> +	/* Perform builds */
> +	memset(&acl_build_param, 0, sizeof(acl_build_param));
> +
> +	acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
> +	acl_build_param.num_fields = rules_nb;
> +	memcpy(&acl_build_param.defs, ipv4_defs, sizeof(ipv4_defs));
> +
> +	if (rte_acl_build(ctx, &acl_build_param) != 0)
> +		rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
> +
> +	rte_acl_dump(ctx);
> +
> +	return ctx;
> +}
> +
> +void
> +sp_init(struct socket_ctx *ctx, int socket_id, unsigned ep)
> +{
> +	const char *name;
> +	const struct acl4_rules *rules_out, *rules_in;
> +	unsigned nb_out_rules, nb_in_rules;
> +
> +	if (ctx == NULL)
> +		rte_exit(EXIT_FAILURE, "NULL context.\n");
> +
> +	if (ctx->sp_ipv4_in != NULL)
> +		rte_exit(EXIT_FAILURE, "Inbound SP DB for socket %u already "
> +				"initialized\n", socket_id);
> +
> +	if (ctx->sp_ipv4_out != NULL)
> +		rte_exit(EXIT_FAILURE, "Outbound SP DB for socket %u already "
> +				"initialized\n", socket_id);
> +
> +	if (ep == 0) {
> +		rules_out = acl4_rules_in;
> +		nb_out_rules = RTE_DIM(acl4_rules_in);
> +		rules_in = acl4_rules_out;
> +		nb_in_rules = RTE_DIM(acl4_rules_out);
> +	} else if (ep == 1) {
> +		rules_out = acl4_rules_out;
> +		nb_out_rules = RTE_DIM(acl4_rules_out);
> +		rules_in = acl4_rules_in;
> +		nb_in_rules = RTE_DIM(acl4_rules_in);
> +	} else
> +		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
> +				"Only 0 or 1 supported.\n", ep);
> +
> +	name = "sp_ipv4_in";
> +	ctx->sp_ipv4_in = (struct sp_ctx *)acl4_init(name, socket_id,
> +			rules_in, nb_in_rules);
> +
> +	name = "sp_ipv4_out";
> +	ctx->sp_ipv4_out = (struct sp_ctx *)acl4_init(name, socket_id,
> +			rules_out, nb_out_rules);
> +}
> -- 
> 2.4.3
> 

  reply	other threads:[~2016-01-31 14:39 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-29 20:29 [PATCH] example/ipsec-secgw: ipsec security gateway Sergio Gonzalez Monroy
2016-01-31 14:39 ` Jerin Jacob [this message]
2016-02-01 11:09   ` Sergio Gonzalez Monroy
2016-02-01 11:26     ` Jerin Jacob
2016-02-01 14:09       ` Thomas Monjalon
2016-03-09 23:54       ` Sergio Gonzalez Monroy
2016-03-10  3:41         ` Jerin Jacob
2016-02-24 13:32 ` Thomas Monjalon
2016-02-24 14:49   ` Sergio Gonzalez Monroy
2016-03-11  1:38 ` [PATCH v2] " Sergio Gonzalez Monroy
2016-03-11  2:12   ` De Lara Guarch, Pablo
2016-03-11  2:12   ` [PATCH v3] " Sergio Gonzalez Monroy
2016-03-11 10:01     ` De Lara Guarch, Pablo
2016-03-11 10:07       ` Thomas Monjalon
2016-03-11 10:02     ` Thomas Monjalon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160131143918.GA12763@localhost.localdomain \
    --to=jerin.jacob@caviumnetworks.com \
    --cc=dev@dpdk.org \
    --cc=sergio.gonzalez.monroy@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.