All of lore.kernel.org
 help / color / mirror / Atom feed
From: Fan Zhang <roy.fan.zhang@intel.com>
To: dev@dpdk.org
Cc: sergio.gonzalez.monroy@intel.com
Subject: [PATCH v2 1/2] examples/ipsec-secgw: add configuration file support
Date: Mon, 11 Jul 2016 15:43:32 +0100	[thread overview]
Message-ID: <1468248213-13100-2-git-send-email-roy.fan.zhang@intel.com> (raw)
In-Reply-To: <1468248213-13100-1-git-send-email-roy.fan.zhang@intel.com>

This patch adds the configuration file support to ipsec_secgw
sample application. Instead of hard-coded rules, the users can
specify their own SP, SA, and routing rules in the configuration
file. An command line option "-f" is added to pass the
configuration file location to the application.

Configuration item formats:

SP rule format:
sp <ip_ver> <dir> esp <action> <priority> <src_ip> <dst_ip> \
<proto> <sport> <dport>

SA rule format:
sa <dir> <spi> <cipher_algo> <auth_algo> <mode> <src_ip> <dst_ip>

Routing rule format:
rt <ip_ver> <src_ip> <dst_ip> <port>

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 doc/guides/sample_app_ug/ipsec_secgw.rst | 809 ++++++++++++-------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ipsec-secgw.c       |  58 +--
 examples/ipsec-secgw/ipsec.h             |   8 +-
 examples/ipsec-secgw/parser.c            | 602 +++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 +++++-----
 examples/ipsec-secgw/sa.c                | 462 ++++++++++--------
 examples/ipsec-secgw/sp4.c               | 539 +++++++++++---------
 examples/ipsec-secgw/sp6.c               | 540 ++++++++++++++-------
 10 files changed, 2119 insertions(+), 1271 deletions(-)
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

diff --git a/doc/guides/sample_app_ug/ipsec_secgw.rst b/doc/guides/sample_app_ug/ipsec_secgw.rst
index fcb33c2..2757df5 100644
--- a/doc/guides/sample_app_ug/ipsec_secgw.rst
+++ b/doc/guides/sample_app_ug/ipsec_secgw.rst
@@ -122,7 +122,7 @@ The application has a number of command line options::
                         -p PORTMASK -P -u PORTMASK
                         --config (port,queue,lcore)[,(port,queue,lcore]
                         --single-sa SAIDX
-			--ep0|--ep1
+                        -f CONFIG_FILE_PATH
 
 Where:
 
@@ -142,14 +142,11 @@ Where:
     on both Inbound and Outbound. This option is meant for debugging/performance
     purposes.
 
-*   ``--ep0``: configure the app as Endpoint 0.
+*   ``-f CONFIG_FILE_PATH``: the full path of text-based file containing all
+    configuration items for running the application (See Configuration file
+    syntax section below). ``-f CONFIG_FILE_PATH`` **must** be specified.
+    **ONLY** the UNIX format configuration file is accepted.
 
-*   ``--ep1``: configure the app as Endpoint 1.
-
-Either one of ``--ep0`` or ``--ep1`` **must** be specified.
-The main purpose of these options is to easily configure two systems
-back-to-back that would forward traffic through an IPsec tunnel (see
-:ref:`figure_ipsec_endpoints`).
 
 The mapping of lcores to port/queues is similar to other l3fwd applications.
 
@@ -157,7 +154,8 @@ For example, given the following command line::
 
     ./build/ipsec-secgw -l 20,21 -n 4 --socket-mem 0,2048       \
            --vdev "cryptodev_null_pmd" -- -p 0xf -P -u 0x3      \
-           --config="(0,0,20),(1,0,20),(2,0,21),(3,0,21)" --ep0 \
+           --config="(0,0,20),(1,0,20),(2,0,21),(3,0,21)"       \
+           -f /path/to/config_file                              \
 
 where each options means:
 
@@ -194,8 +192,12 @@ where each options means:
     |          |           |           |                                       |
     +----------+-----------+-----------+---------------------------------------+
 
-*   The ``--ep0`` options configures the app with a given set of SP, SA and Routing
-    entries as explained below in more detail.
+*   The ``-f /path/to/config_file`` option enables the application read and
+    parse the configuration file specified, and configures the application
+    with a given set of SP, SA and Routing entries accordingly. The syntax of
+    the configuration file will be explained below in more detail. Please
+    **note** the parser only accepts UNIX format text file. Other formats
+    such as DOS/MAC format will cause a parse error.
 
 Refer to the *DPDK Getting Started Guide* for general information on running
 applications and the Environment Abstraction Layer (EAL) options.
@@ -219,496 +221,321 @@ For example, something like the following command line:
             --vdev "cryptodev_aesni_mb_pmd" --vdev "cryptodev_null_pmd" \
 	    -- \
             -p 0xf -P -u 0x3 --config="(0,0,20),(1,0,20),(2,0,21),(3,0,21)" \
-            --ep0
+            -f sample.cfg
 
 
 Configurations
 --------------
 
-The following sections provide some details on the default values used to
-initialize the SP, SA and Routing tables.
-Currently all configuration information is hard coded into the application.
+The following sections provide the syntax of configurations to initialize
+your SP, SA and Routing tables.
+Configurations shall be specified in the configuration file to be passed to
+the application. The file is then parsed by the application. The successful
+parsing will result in the appropriate rules being applied to the tables
+accordingly.
 
-The following image illustrate a few of the concepts regarding IPSec, such
-as protected/unprotected and inbound/outbound traffic, from the point of
-view of two back-to-back endpoints:
 
-.. _figure_ipsec_endpoints:
+Configuration File Syntax
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. figure:: img/ipsec_endpoints.*
+As mention in the overview, the Security Policies are ACL rules.
+The application parsers the rules specified in the configuration file and
+passes them to the ACL table, and replicates them per socket in use.
 
-   IPSec Inbound/Outbound traffic
+Following are the configuration file syntax.
 
-Note that the above image only displays unidirectional traffic per port
-for illustration purposes.
-The application supports bidirectional traffic on all ports,
+General rule syntax
+^^^^^^^^^^^^^^^^^^^
 
+The parse treats one line in the configuration file as one configuration
+item (unless the line concatenation symbol exists). Every configuration
+item shall follow the syntax of either SP, SA, or Routing rules specified
+below.
 
-Security Policy Initialization
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The configuration parser supports the following special symbols:
 
-As mention in the overview, the Security Policies are ACL rules.
-The application defines two ACLs, one each of Inbound and Outbound, and
-it replicates them per socket in use.
-
-Following are the default rules which show only the relevant information,
-assuming ANY value is valid for the fields not mentioned (src ip, proto,
-src/dst ports).
-
-.. _table_ipsec_endpoint_outbound_sp:
-
-.. table:: Endpoint 0 Outbound Security Policies
-
-   +-----------------------------------+------------+
-   | **Dst**                           | **SA idx** |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.105.0/24                  | 5          |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.106.0/24                  | 6          |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.175.0/24                  | 10         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.176.0/24                  | 11         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.200.0/24                  | 15         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.201.0/24                  | 16         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.55.0/24                   | 25         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.56.0/24                   | 26         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.240.0/24                  | BYPASS     |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.241.0/24                  | BYPASS     |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 0:0:0:0:5555:5555:0:0/96          | 5          |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 0:0:0:0:6666:6666:0:0/96          | 6          |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 0:0:1111:1111:0:0:0:0/96          | 10         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 0:0:1111:1111:1111:1111:0:0/96    | 11         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 0:0:0:0:aaaa:aaaa:0:0/96          | 25         |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 0:0:0:0:bbbb:bbbb:0:0/96          | 26         |
-   |                                   |            |
-   +-----------------------------------+------------+
-
-.. _table_ipsec_endpoint_inbound_sp:
-
-.. table:: Endpoint 0 Inbound Security Policies
-
-   +-----------------------------------+------------+
-   | **Dst**                           | **SA idx** |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.115.0/24                  | 105        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.116.0/24                  | 106        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.185.0/24                  | 110        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.186.0/24                  | 111        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.210.0/24                  | 115        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.211.0/24                  | 116        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.65.0/24                   | 125        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.66.0/24                   | 126        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.245.0/24                  | BYPASS     |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | 192.168.246.0/24                  | BYPASS     |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | ffff:0:0:0:5555:5555:0:0/96       | 105        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | ffff:0:0:0:6666:6666:0:0/96       | 106        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | ffff:0:1111:1111:0:0:0:0/96       | 110        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | ffff:0:1111:1111:1111:1111:0:0/96 | 111        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | ffff:0:0:0:aaaa:aaaa:0:0/96       | 125        |
-   |                                   |            |
-   +-----------------------------------+------------+
-   | ffff:0:0:0:bbbb:bbbb:0:0/96       | 126        |
-   |                                   |            |
-   +-----------------------------------+------------+
-
-For Endpoint 1, we use the same policies in reverse, meaning the Inbound SP
-entries are set as Outbound and vice versa.
-
-
-Security Association Initialization
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The SAs are kept in a array table.
-
-For Inbound, the SPI is used as index modulo 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 configured with AES-CBC and HMAC-SHA1 share the same values for cipher
-block size and key, and authentication digest size and key.
-
-The following are the default values:
-
-.. _table_ipsec_endpoint_outbound_sa:
-
-.. table:: Endpoint 0 Outbound Security Associations
-
-   +---------+----------+------------+-----------+----------------+----------------+
-   | **SPI** | **Mode** | **Cipher** | **Auth**  | **Tunnel src** | **Tunnel dst** |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 5       | Tunnel   | AES-CBC    | HMAC-SHA1 | 172.16.1.5     | 172.16.2.5     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 6       | Tunnel   | AES-CBC    | HMAC-SHA1 | 172.16.1.6     | 172.16.2.6     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 10      | Trans    | AES-CBC    | HMAC-SHA1 | N/A            | N/A            |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 11      | Trans    | AES-CBC    | HMAC-SHA1 | N/A            | N/A            |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 15      | Tunnel   | NULL       | NULL      | 172.16.1.5     | 172.16.2.5     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 16      | Tunnel   | NULL       | NULL      | 172.16.1.6     | 172.16.2.6     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 25      | Tunnel   | AES-CBC    | HMAC-SHA1 | 1111:1111:     | 2222:2222:     |
-   |         |          |            |           | 1111:1111:     | 2222:2222:     |
-   |         |          |            |           | 1111:1111:     | 2222:2222:     |
-   |         |          |            |           | 1111:5555      | 2222:5555      |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 26      | Tunnel   | AES-CBC    | HMAC-SHA1 | 1111:1111:     | 2222:2222:     |
-   |         |          |            |           | 1111:1111:     | 2222:2222:     |
-   |         |          |            |           | 1111:1111:     | 2222:2222:     |
-   |         |          |            |           | 1111:6666      | 2222:6666      |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-
-.. _table_ipsec_endpoint_inbound_sa:
-
-.. table:: Endpoint 0 Inbound Security Associations
-
-   +---------+----------+------------+-----------+----------------+----------------+
-   | **SPI** | **Mode** | **Cipher** | **Auth**  | **Tunnel src** | **Tunnel dst** |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 105     | Tunnel   | AES-CBC    | HMAC-SHA1 | 172.16.2.5     | 172.16.1.5     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 106     | Tunnel   | AES-CBC    | HMAC-SHA1 | 172.16.2.6     | 172.16.1.6     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 110     | Trans    | AES-CBC    | HMAC-SHA1 | N/A            | N/A            |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 111     | Trans    | AES-CBC    | HMAC-SHA1 | N/A            | N/A            |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 115     | Tunnel   | NULL       | NULL      | 172.16.2.5     | 172.16.1.5     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 116     | Tunnel   | NULL       | NULL      | 172.16.2.6     | 172.16.1.6     |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 125     | Tunnel   | AES-CBC    | HMAC-SHA1 | 2222:2222:     | 1111:1111:     |
-   |         |          |            |           | 2222:2222:     | 1111:1111:     |
-   |         |          |            |           | 2222:2222:     | 1111:1111:     |
-   |         |          |            |           | 2222:5555      | 1111:5555      |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-   | 126     | Tunnel   | AES-CBC    | HMAC-SHA1 | 2222:2222:     | 1111:1111:     |
-   |         |          |            |           | 2222:2222:     | 1111:1111:     |
-   |         |          |            |           | 2222:2222:     | 1111:1111:     |
-   |         |          |            |           | 2222:6666      | 1111:6666      |
-   |         |          |            |           |                |                |
-   +---------+----------+------------+-----------+----------------+----------------+
-
-For Endpoint 1, we use the same policies in reverse, meaning the Inbound SP
-entries are set as Outbound and vice versa.
-
-
-Routing Initialization
-~~~~~~~~~~~~~~~~~~~~~~
-
-The Routing is implemented using an LPM table.
-
-Following default values:
-
-.. _table_ipsec_endpoint_outbound_routing:
-
-.. table:: Endpoint 0 Routing Table
-
-   +------------------+----------+
-   | **Dst addr**     | **Port** |
-   |                  |          |
-   +------------------+----------+
-   | 172.16.2.5/32    | 0        |
-   |                  |          |
-   +------------------+----------+
-   | 172.16.2.6/32    | 1        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.175.0/24 | 0        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.176.0/24 | 1        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.240.0/24 | 0        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.241.0/24 | 1        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.115.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.116.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.65.0/24  | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.66.0/24  | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.185.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.186.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.210.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.211.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.245.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.246.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 2222:2222:       | 0        |
-   | 2222:2222:       |          |
-   | 2222:2222:       |          |
-   | 2222:5555/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | 2222:2222:       | 1        |
-   | 2222:2222:       |          |
-   | 2222:2222:       |          |
-   | 2222:6666/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 0        |
-   | 1111:1111:       |          |
-   | 0000:0000:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 1        |
-   | 1111:1111:       |          |
-   | 1111:1111:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 2        |
-   | 0000:0000:       |          |
-   | aaaa:aaaa:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 3        |
-   | 0000:0000:       |          |
-   | bbbb:bbbb:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 2        |
-   | 0000:0000:       |          |
-   | 5555:5555:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 3        |
-   | 0000:0000:       |          |
-   | 6666:6666:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 2        |
-   | 1111:1111:       |          |
-   | 0000:0000:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 3        |
-   | 1111:1111:       |          |
-   | 1111:1111:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
-
-.. _table_ipsec_endpoint_inbound_routing:
-
-.. table:: Endpoint 1 Routing Table
-
-   +------------------+----------+
-   | **Dst addr**     | **Port** |
-   |                  |          |
-   +------------------+----------+
-   | 172.16.1.5/32    | 0        |
-   |                  |          |
-   +------------------+----------+
-   | 172.16.1.6/32    | 1        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.185.0/24 | 0        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.186.0/24 | 1        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.245.0/24 | 0        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.246.0/24 | 1        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.105.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.106.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.55.0/24  | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.56.0/24  | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.175.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.176.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.200.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.201.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.240.0/24 | 2        |
-   |                  |          |
-   +------------------+----------+
-   | 192.168.241.0/24 | 3        |
-   |                  |          |
-   +------------------+----------+
-   | 1111:1111:       | 0        |
-   | 1111:1111:       |          |
-   | 1111:1111:       |          |
-   | 1111:5555/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | 1111:1111:       | 1        |
-   | 1111:1111:       |          |
-   | 1111:1111:       |          |
-   | 1111:6666/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 0        |
-   | 1111:1111:       |          |
-   | 0000:0000:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | ffff:0000:       | 1        |
-   | 1111:1111:       |          |
-   | 1111:1111:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 2        |
-   | 0000:0000:       |          |
-   | aaaa:aaaa:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 3        |
-   | 0000:0000:       |          |
-   | bbbb:bbbb:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 2        |
-   | 0000:0000:       |          |
-   | 5555:5555:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 3        |
-   | 0000:0000:       |          |
-   | 6666:6666:       |          |
-   | 0000:0/116       |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 2        |
-   | 1111:1111:       |          |
-   | 0000:0000:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
-   | 0000:0000:       | 3        |
-   | 1111:1111:       |          |
-   | 1111:1111:       |          |
-   | 0000:0000/116    |          |
-   |                  |          |
-   +------------------+----------+
+ * Comment symbol **#**. Any character from this symbol to the end of
+   line is treated as comment and will not be parsed.
+
+ * Line concatenation symbol **\\**. This symbol shall be placed in the end
+   of the line to be concatenated to the line below. Multiple lines'
+   concatenation is supported.
+
+
+SP rule syntax
+^^^^^^^^^^^^^^
+
+The SP rule syntax is shown as follows:
+
+.. code-block:: console
+
+    sp <ip_ver> <dir> esp <action> <priority> <src_ip> <dst_ip>
+    <proto> <sport> <dport>
+
+
+where each options means:
+
+``<ip_ver>``
+
+ * IP protocol version
+
+ * Optional: No
+
+ * Available options:
+
+   * *ipv4*: IP protocol version 4
+   * *ipv6*: IP protocol version 6
+
+``<dir>``
+
+ * The traffic direction
+
+ * Optional: No
+
+ * Available options:
+
+   * *in*: inbound traffic
+   * *out*: outbound traffic
+
+``<action>``
+
+ * IPsec action
+
+ * Optional: No
+
+ * Available options:
+
+   * *protect <SA_idx>*: the specified traffic is protected by SA rule
+     with id SA_idx
+   * *bypass*: the specified traffic traffic is bypassed
+   * *discard*: the specified traffic is discarded
+
+``<priority>``
+
+ * Rule priority
+
+ * Optional: Yes, default priority 0 will be used
+
+ * Syntax: *pri <id>*
+
+``<src_ip>``
+
+ * The source IP address and mask
+
+ * Optional: Yes, default address 0.0.0.0 and mask of 0 will be used
+
+ * Syntax:
+
+   * *src X.X.X.X/Y* for IPv4
+   * *src XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/Y* for IPv6
+
+``<dst_ip>``
+
+ * The destination IP address and mask
+
+ * Optional: Yes, default address 0.0.0.0 and mask of 0 will be used
+
+ * Syntax:
+
+   * *dst X.X.X.X/Y* for IPv4
+   * *dst XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/Y* for IPv6
+
+``<proto>``
+
+ * The protocol start and end range
+
+ * Optional: yes, default range of 0 to 0 will be used
+
+ * Syntax: *proto X:Y*
+
+``<sport>``
+
+ * The source port start and end range
+
+ * Optional: yes, default range of 0 to 0 will be used
+
+ * Syntax: *sport X:Y*
+
+``<dport>``
+
+ * The destination port start and end range
+
+ * Optional: yes, default range of 0 to 0 will be used
+
+ * Format: *dport X:Y*
+
+Example SP rules:
+
+.. code-block:: console
+
+    sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 \
+    dport 0:65535
+
+    sp ipv6 in esp bypass pri 1 dst 0000:0000:0000:0000:5555:5555:\
+    0000:0000/96 sport 0:65535 dport 0:65535
+
+
+SA rule syntax
+^^^^^^^^^^^^^^
+
+The successfully parsed SA rules will be stored in an array table.
+
+All SAs configured with AES-CBC and HMAC-SHA1 share the same values for
+cipher block size and key, and authentication digest size and key.
+
+The SA rule syntax is shown as follows:
+
+.. code-block:: console
+
+    sa <dir> <spi> <cipher_algo> <auth_algo> <mode> <src_ip> <dst_ip>
+
+where each options means:
+
+``<dir>``
+
+ * The traffic direction
+
+ * Optional: No
+
+ * Available options:
+
+   * *in*: inbound traffic
+   * *out*: outbound traffic
+
+``<spi>``
+
+ * The SPI number
+
+ * Optional: No
+
+ * Syntax: unsigned integer number
+
+``<cipher_algo>``
+
+ * Cipher algorithm
+
+ * Optional: No
+
+ * Available options:
+
+   * *null*: NULL algorithm
+   * *aes-128-cbc*: AES-CBC 128-bit algorithm
+
+``<auth_algo>``
+
+ * Authentication algorithm
+
+ * Optional: No
+
+ * Available options:
+
+    * *null*: NULL algorithm
+    * *sha1-hmac*: HMAC SHA1 algorithm
+
+``<mode>``
+
+ * The operation mode
+
+ * Optional: No
+
+ * Available options:
+
+   * *ipv4-tunnel*: Tunnel mode for IPv4 packets
+   * *ipv6-tunnel*: Tunnel mode for IPv6 packets
+   * *transport*: transport mode
+
+``<src_ip>``
+
+ * The source IP address. This option is not available when
+   transport mode is used
+
+ * Optional: Yes, default address 0.0.0.0 will be used
+
+ * Syntax:
+
+   * *src X.X.X.X* for IPv4
+   * *src XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX* for IPv6
+
+``<dst_ip>``
+
+ * The destination IP address. This option is not available when
+   transport mode is used
+
+ * Optional: Yes, default address 0.0.0.0 will be used
+
+ * Syntax:
+
+   * *dst X.X.X.X* for IPv4
+   * *dst XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX* for IPv6
+
+Example SA rules:
+
+.. code-block:: console
+
+    sa in 5 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+    sa out 125 aes-128-cbc sha1-hmac ipv6-tunnel \
+    src 2222:2222:2222:2222:2222:2222:2222:5555 \
+    dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+
+Routing rule syntax
+^^^^^^^^^^^^^^^^^^^
+
+The Routing rule syntax is shown as follows:
+
+.. code-block:: console
+
+    rt <ip_ver> <src_ip> <dst_ip> <port>
+
+
+where each options means:
+
+``<ip_ver>``
+
+ * IP protocol version
+
+ * Optional: No
+
+ * Available options:
+
+   * *ipv4*: IP protocol version 4
+   * *ipv6*: IP protocol version 6
+
+``<src_ip>``
+
+ * The source IP address and mask
+
+ * Optional: Yes, default address 0.0.0.0 and mask of 0 will be used
+
+ * Syntax:
+
+   * *src X.X.X.X/Y* for IPv4
+   * *src XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/Y* for IPv6
+
+``<dst_ip>``
+
+ * The destination IP address and mask
+
+ * Optional: Yes, default address 0.0.0.0 and mask of 0 will be used
+
+ * Syntax:
+
+   * *dst X.X.X.X/Y* for IPv4
+   * *dst XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/Y* for IPv6
+
+``<port>``
+
+ * The traffic output port id
+
+ * Optional: yes, default output port 0 will be used
+
+ * Syntax: *port X*
+
+Example SP rules:
+
+.. code-block:: console
+
+    rt ipv4 dst 172.16.1.5/32 port 0
+
+    rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
diff --git a/examples/ipsec-secgw/Makefile b/examples/ipsec-secgw/Makefile
index 06b6db1..17e9155 100644
--- a/examples/ipsec-secgw/Makefile
+++ b/examples/ipsec-secgw/Makefile
@@ -53,6 +53,7 @@ endif
 #
 # all source are stored in SRCS-y
 #
+SRCS-y += parser.c
 SRCS-y += ipsec.c
 SRCS-y += esp.c
 SRCS-y += sp4.c
diff --git a/examples/ipsec-secgw/ipsec-secgw.c b/examples/ipsec-secgw/ipsec-secgw.c
index f78743d..5ef1f44 100644
--- a/examples/ipsec-secgw/ipsec-secgw.c
+++ b/examples/ipsec-secgw/ipsec-secgw.c
@@ -72,6 +72,7 @@
 #include <rte_cryptodev.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
 #define RTE_LOGTYPE_IPSEC RTE_LOGTYPE_USER1
 
@@ -88,8 +89,6 @@
 
 #define OPTION_CONFIG		"config"
 #define OPTION_SINGLE_SA	"single-sa"
-#define OPTION_EP0		"ep0"
-#define OPTION_EP1		"ep1"
 
 #define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
 
@@ -158,7 +157,6 @@ static uint32_t enabled_port_mask;
 static uint32_t unprotected_port_mask;
 static int32_t promiscuous_on = 1;
 static int32_t numa_on = 1; /**< NUMA is enabled by default. */
-static int32_t ep = -1; /**< Endpoint configuration (0 or 1) */
 static uint32_t nb_lcores;
 static uint32_t single_sa;
 static uint32_t single_sa_idx;
@@ -832,7 +830,7 @@ print_usage(const char *prgname)
 {
 	printf("%s [EAL options] -- -p PORTMASK -P -u PORTMASK"
 		"  --"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]"
-		" --single-sa SAIDX --ep0|--ep1\n"
+		" --single-sa SAIDX -f CONFIG_FILE\n"
 		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
 		"  -P : enable promiscuous mode\n"
 		"  -u PORTMASK: hexadecimal bitmask of unprotected ports\n"
@@ -840,8 +838,8 @@ print_usage(const char *prgname)
 		"rx queues configuration\n"
 		"  --single-sa SAIDX: use single SA index for outbound, "
 		"bypassing the SP\n"
-		"  --ep0: Configure as Endpoint 0\n"
-		"  --ep1: Configure as Endpoint 1\n", prgname);
+		"  -f CONFIG_FILE: Configuration file path\n",
+		prgname);
 }
 
 static int32_t
@@ -954,18 +952,6 @@ parse_args_long_options(struct option *lgopts, int32_t option_index)
 		}
 	}
 
-	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;
-	}
-
 	return ret;
 }
 #undef __STRNCMP
@@ -980,14 +966,13 @@ parse_args(int32_t argc, char **argv)
 	static struct option lgopts[] = {
 		{OPTION_CONFIG, 1, 0, 0},
 		{OPTION_SINGLE_SA, 1, 0, 0},
-		{OPTION_EP0, 0, 0, 0},
-		{OPTION_EP1, 0, 0, 0},
 		{NULL, 0, 0, 0}
 	};
+	int32_t f_present = 0;
 
 	argvopt = argv;
 
-	while ((opt = getopt_long(argc, argvopt, "p:Pu:",
+	while ((opt = getopt_long(argc, argvopt, "p:Pu:f:",
 				lgopts, &option_index)) != EOF) {
 
 		switch (opt) {
@@ -1011,6 +996,21 @@ parse_args(int32_t argc, char **argv)
 				return -1;
 			}
 			break;
+		case 'f':
+			if (f_present == 1) {
+				printf("\"-f\" option present more than "
+					"once!\n");
+				print_usage(prgname);
+				return -1;
+			}
+			if (parse_cfg_file(optarg) < 0) {
+				printf("parsing file \"%s\" failed\n",
+					optarg);
+				print_usage(prgname);
+				return -1;
+			}
+			f_present = 1;
+			break;
 		case 0:
 			if (parse_args_long_options(lgopts, option_index)) {
 				print_usage(prgname);
@@ -1023,6 +1023,11 @@ parse_args(int32_t argc, char **argv)
 		}
 	}
 
+	if (f_present == 0) {
+		printf("Mandatory option \"-f\" not present\n");
+		return -1;
+	}
+
 	if (optind >= 0)
 		argv[optind-1] = prgname;
 
@@ -1401,9 +1406,6 @@ main(int32_t argc, char **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",
@@ -1433,13 +1435,13 @@ main(int32_t argc, char **argv)
 		if (socket_ctx[socket_id].mbuf_pool)
 			continue;
 
-		sa_init(&socket_ctx[socket_id], socket_id, ep);
+		sa_init(&socket_ctx[socket_id], socket_id);
 
-		sp4_init(&socket_ctx[socket_id], socket_id, ep);
+		sp4_init(&socket_ctx[socket_id], socket_id);
 
-		sp6_init(&socket_ctx[socket_id], socket_id, ep);
+		sp6_init(&socket_ctx[socket_id], socket_id);
 
-		rt_init(&socket_ctx[socket_id], socket_id, ep);
+		rt_init(&socket_ctx[socket_id], socket_id);
 
 		pool_init(&socket_ctx[socket_id], socket_id, NB_MBUF);
 	}
diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h
index 0d2ee25..ce6071d 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -183,15 +183,15 @@ outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
 		struct ipsec_sa *sa[], uint16_t nb_pkts);
 
 void
-sp4_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep);
+sp4_init(struct socket_ctx *ctx, int32_t socket_id);
 
 void
-sp6_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep);
+sp6_init(struct socket_ctx *ctx, int32_t socket_id);
 
 void
-sa_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep);
+sa_init(struct socket_ctx *ctx, int32_t socket_id);
 
 void
-rt_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep);
+rt_init(struct socket_ctx *ctx, int32_t socket_id);
 
 #endif /* __IPSEC_H__ */
diff --git a/examples/ipsec-secgw/parser.c b/examples/ipsec-secgw/parser.c
new file mode 100644
index 0000000..f088b27
--- /dev/null
+++ b/examples/ipsec-secgw/parser.c
@@ -0,0 +1,602 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 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 <rte_common.h>
+#include <rte_crypto.h>
+
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include "ipsec.h"
+#include "parser.h"
+
+#define PARSE_DELIMITER		" \f\n\r\t\v"
+static int
+parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
+{
+	uint32_t i;
+
+	if ((string == NULL) ||
+		(tokens == NULL) ||
+		(*n_tokens < 1))
+		return -EINVAL;
+
+	for (i = 0; i < *n_tokens; i++) {
+		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
+		if (tokens[i] == NULL)
+			break;
+	}
+
+	if ((i == *n_tokens) &&
+		(NULL != strtok_r(string, PARSE_DELIMITER, &string)))
+		return -E2BIG;
+
+	*n_tokens = i;
+	return 0;
+}
+
+#define INADDRSZ 4
+#define IN6ADDRSZ 16
+
+/* int
+ * inet_pton4(src, dst)
+ *      like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ *      1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ *      does not touch `dst' unless it's returning 1.
+ * author:
+ *      Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+	static const char digits[] = "0123456789";
+	int saw_digit, octets, ch;
+	unsigned char tmp[INADDRSZ], *tp;
+
+	saw_digit = 0;
+	octets = 0;
+	*(tp = tmp) = 0;
+	while ((ch = *src++) != '\0') {
+		const char *pch;
+
+		pch = strchr(digits, ch);
+		if (pch != NULL) {
+			unsigned int new = *tp * 10 + (pch - digits);
+
+			if (new > 255)
+				return 0;
+			if (!saw_digit) {
+				if (++octets > 4)
+					return 0;
+				saw_digit = 1;
+			}
+			*tp = (unsigned char)new;
+		} else if (ch == '.' && saw_digit) {
+			if (octets == 4)
+				return 0;
+			*++tp = 0;
+			saw_digit = 0;
+		} else
+			return 0;
+	}
+	if (octets < 4)
+		return 0;
+
+	memcpy(dst, tmp, INADDRSZ);
+	return 1;
+}
+
+/* int
+ * inet_pton6(src, dst)
+ *      convert presentation level address to network order binary form.
+ * return:
+ *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *      (1) does not touch `dst' unless it's returning 1.
+ *      (2) :: in a full address is silently ignored.
+ * credit:
+ *      inspired by Mark Andrews.
+ * author:
+ *      Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+	static const char xdigits_l[] = "0123456789abcdef",
+		xdigits_u[] = "0123456789ABCDEF";
+	unsigned char tmp[IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0;
+	const char *xdigits = 0, *curtok = 0;
+	int ch = 0, saw_xdigit = 0, count_xdigit = 0;
+	unsigned int val = 0;
+	unsigned dbloct_count = 0;
+
+	memset((tp = tmp), '\0', IN6ADDRSZ);
+	endp = tp + IN6ADDRSZ;
+	colonp = NULL;
+	/* Leading :: requires some special handling. */
+	if (*src == ':')
+		if (*++src != ':')
+			return 0;
+	curtok = src;
+	saw_xdigit = count_xdigit = 0;
+	val = 0;
+
+	while ((ch = *src++) != '\0') {
+		const char *pch;
+
+		pch = strchr((xdigits = xdigits_l), ch);
+		if (pch == NULL)
+			pch = strchr((xdigits = xdigits_u), ch);
+		if (pch != NULL) {
+			if (count_xdigit >= 4)
+				return 0;
+			val <<= 4;
+			val |= (pch - xdigits);
+			if (val > 0xffff)
+				return 0;
+			saw_xdigit = 1;
+			count_xdigit++;
+			continue;
+		}
+		if (ch == ':') {
+			curtok = src;
+			if (!saw_xdigit) {
+				if (colonp)
+					return 0;
+				colonp = tp;
+				continue;
+			} else if (*src == '\0') {
+				return 0;
+			}
+			if (tp + sizeof(int16_t) > endp)
+				return 0;
+			*tp++ = (unsigned char) ((val >> 8) & 0xff);
+			*tp++ = (unsigned char) (val & 0xff);
+			saw_xdigit = 0;
+			count_xdigit = 0;
+			val = 0;
+			dbloct_count++;
+			continue;
+		}
+		if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+		    inet_pton4(curtok, tp) > 0) {
+			tp += INADDRSZ;
+			saw_xdigit = 0;
+			dbloct_count += 2;
+			break;  /* '\0' was seen by inet_pton4(). */
+		}
+		return 0;
+	}
+	if (saw_xdigit) {
+		if (tp + sizeof(int16_t) > endp)
+			return 0;
+		*tp++ = (unsigned char) ((val >> 8) & 0xff);
+		*tp++ = (unsigned char) (val & 0xff);
+		dbloct_count++;
+	}
+	if (colonp != NULL) {
+		/* if we already have 8 double octets, having a colon
+		 * means error */
+		if (dbloct_count == 8)
+			return 0;
+
+		/*
+		 * Since some memmove()'s erroneously fail to handle
+		 * overlapping regions, we'll do the shift by hand.
+		 */
+		const int n = tp - colonp;
+		int i;
+
+		for (i = 1; i <= n; i++) {
+			endp[-i] = colonp[n - i];
+			colonp[n - i] = 0;
+		}
+		tp = endp;
+	}
+	if (tp != endp)
+		return 0;
+	memcpy(dst, tmp, IN6ADDRSZ);
+	return 1;
+}
+
+int
+parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask)
+{
+	char ip_str[256] = {0};
+	char *pch;
+
+	pch = strchr(token, '/');
+	if (pch != NULL) {
+		strncpy(ip_str, token, pch - token);
+		pch += 1;
+		if (is_str_num(pch) != 0)
+			return -EINVAL;
+		if (mask)
+			*mask = atoi(pch);
+	} else {
+		strncpy(ip_str, token, sizeof(ip_str));
+		if (mask)
+			*mask = 0;
+	}
+
+	if (strlen(ip_str) >= INET_ADDRSTRLEN)
+		return -EINVAL;
+
+	if (inet_pton4(ip_str, (unsigned char *)ipv4) != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+int
+parse_ipv6_addr(const char *token, struct in6_addr *ipv6, uint32_t *mask)
+{
+	char ip_str[256] = {0};
+	char *pch;
+
+	pch = strchr(token, '/');
+	if (pch != NULL) {
+		strncpy(ip_str, token, pch - token);
+		pch += 1;
+		if (is_str_num(pch) != 0)
+			return -EINVAL;
+		if (mask)
+			*mask = atoi(pch);
+	} else {
+		strncpy(ip_str, token, sizeof(ip_str));
+		if (mask)
+			*mask = 0;
+	}
+
+	if (strlen(ip_str) >= INET6_ADDRSTRLEN) {
+		printf("len error, has %lu, expect %i\n", strlen(ip_str),
+			INET6_ADDRSTRLEN);
+		return -EINVAL;
+	}
+
+	if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+int
+parse_range(const char *token, uint16_t *low, uint16_t *high)
+{
+	char ch;
+	char num_str[20];
+	uint32_t pos;
+	int range_low = -1;
+	int range_high = -1;
+
+	if (!low || !high)
+		return -1;
+
+	memset(num_str, 0, 20);
+	pos = 0;
+
+	while ((ch = *token++) != '\0') {
+		if (isdigit(ch)) {
+			if (pos >= 19)
+				return -1;
+			num_str[pos++] = ch;
+		} else if (ch == ':') {
+			if (range_low != -1)
+				return -1;
+			range_low = atoi(num_str);
+			memset(num_str, 0, 20);
+			pos = 0;
+		}
+	}
+
+	if (strlen(num_str) == 0)
+		return -1;
+
+	range_high = atoi(num_str);
+
+	*low = (uint16_t)range_low;
+	*high = (uint16_t)range_high;
+
+	return 0;
+}
+
+/** sp add parse */
+struct cfg_sp_add_cfg_item {
+	cmdline_fixed_string_t sp_keyword;
+	cmdline_multi_string_t multi_string;
+};
+
+static void
+cfg_sp_add_cfg_item_parsed(void *parsed_result,
+	__rte_unused struct cmdline *cl, void *data)
+{
+	struct cfg_sp_add_cfg_item *params = parsed_result;
+	char *tokens[32];
+	uint32_t n_tokens = RTE_DIM(tokens);
+	struct parse_status *status = (struct parse_status *)data;
+
+	APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
+		&n_tokens) == 0), status, "too many arguments");
+
+	if (status->status < 0)
+		return;
+
+	if (strcmp(tokens[0], "ipv4") == 0) {
+		parse_sp4_tokens(tokens, n_tokens, status);
+		if (status->status < 0)
+			return;
+	} else if (strcmp(tokens[0], "ipv6") == 0) {
+		parse_sp6_tokens(tokens, n_tokens, status);
+		if (status->status < 0)
+			return;
+	} else {
+		APP_CHECK(0, status, "unrecognizable input %s\n",
+			tokens[0]);
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cfg_sp_add_sp_str =
+	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
+		sp_keyword, "sp");
+
+static cmdline_parse_token_string_t cfg_sp_add_multi_str =
+	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
+		TOKEN_STRING_MULTI);
+
+cmdline_parse_inst_t cfg_sp_add_rule = {
+	.f = cfg_sp_add_cfg_item_parsed,
+	.data = NULL,
+	.help_str = "",
+	.tokens = {
+		(void *) &cfg_sp_add_sp_str,
+		(void *) &cfg_sp_add_multi_str,
+		NULL,
+	},
+};
+
+/* sa add parse */
+struct cfg_sa_add_cfg_item {
+	cmdline_fixed_string_t sa_keyword;
+	cmdline_multi_string_t multi_string;
+};
+
+static void
+cfg_sa_add_cfg_item_parsed(void *parsed_result,
+	__rte_unused struct cmdline *cl, void *data)
+{
+	struct cfg_sa_add_cfg_item *params = parsed_result;
+	char *tokens[32];
+	uint32_t n_tokens = RTE_DIM(tokens);
+	struct parse_status *status = (struct parse_status *)data;
+
+	APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
+		&n_tokens) == 0, status, "too many arguments\n");
+
+	parse_sa_tokens(tokens, n_tokens, status);
+}
+
+static cmdline_parse_token_string_t cfg_sa_add_sa_str =
+	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
+		sa_keyword, "sa");
+
+static cmdline_parse_token_string_t cfg_sa_add_multi_str =
+	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
+		TOKEN_STRING_MULTI);
+
+cmdline_parse_inst_t cfg_sa_add_rule = {
+	.f = cfg_sa_add_cfg_item_parsed,
+	.data = NULL,
+	.help_str = "",
+	.tokens = {
+		(void *) &cfg_sa_add_sa_str,
+		(void *) &cfg_sa_add_multi_str,
+		NULL,
+	},
+};
+
+/* rt add parse */
+struct cfg_rt_add_cfg_item {
+	cmdline_fixed_string_t rt_keyword;
+	cmdline_multi_string_t multi_string;
+};
+
+static void
+cfg_rt_add_cfg_item_parsed(void *parsed_result,
+	__rte_unused struct cmdline *cl, void *data)
+{
+	struct cfg_rt_add_cfg_item *params = parsed_result;
+	char *tokens[32];
+	uint32_t n_tokens = RTE_DIM(tokens);
+	struct parse_status *status = (struct parse_status *)data;
+
+	APP_CHECK(parse_tokenize_string(
+		params->multi_string, tokens, &n_tokens) == 0,
+		status, "too many arguments\n");
+	if (status->status < 0)
+		return;
+
+	parse_rt_tokens(tokens, n_tokens, status);
+}
+
+static cmdline_parse_token_string_t cfg_rt_add_rt_str =
+	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
+		rt_keyword, "rt");
+
+static cmdline_parse_token_string_t cfg_rt_add_multi_str =
+	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
+		TOKEN_STRING_MULTI);
+
+cmdline_parse_inst_t cfg_rt_add_rule = {
+	.f = cfg_rt_add_cfg_item_parsed,
+	.data = NULL,
+	.help_str = "",
+	.tokens = {
+		(void *) &cfg_rt_add_rt_str,
+		(void *) &cfg_rt_add_multi_str,
+		NULL,
+	},
+};
+
+/** set of cfg items */
+cmdline_parse_ctx_t ipsec_ctx[] = {
+	(cmdline_parse_inst_t *)&cfg_sp_add_rule,
+	(cmdline_parse_inst_t *)&cfg_sa_add_rule,
+	(cmdline_parse_inst_t *)&cfg_rt_add_rule,
+	NULL,
+};
+
+int
+parse_cfg_file(const char *cfg_filename)
+{
+	struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
+	FILE *f = fopen(cfg_filename, "r");
+	char str[1024] = {0}, *get_s = NULL;
+	uint32_t line_num = 0;
+	struct parse_status status = {0};
+
+	if (f == NULL) {
+		rte_panic("Error: invalid file descriptor %s\n",
+			cfg_filename);
+		goto error_exit;
+	}
+
+	if (cl == NULL) {
+		rte_panic("Error: cannot create cmdline instance\n");
+		goto error_exit;
+	}
+
+	cfg_sp_add_rule.data = &status;
+	cfg_sa_add_rule.data = &status;
+	cfg_rt_add_rule.data = &status;
+
+	do {
+		char oneline[1024];
+
+		get_s = fgets(oneline, 1024, f);
+		if (get_s) {
+			char *pos;
+
+			line_num++;
+
+			if (strlen(oneline) > 1022) {
+				rte_panic("%s:%u: error: the line "
+					"contains more characters the "
+					"parser can handle\n",
+					cfg_filename, line_num);
+				goto error_exit;
+			}
+
+			/* process comment char '#' */
+			if (oneline[0] == '#')
+				continue;
+
+			pos = strchr(oneline, '#');
+			if (pos != NULL)
+				*pos = '\0';
+
+			/* process line concatenator '\' */
+			pos = strchr(oneline, 92);
+			if (pos != NULL) {
+				if (pos != oneline+strlen(oneline) - 2) {
+					rte_panic("%s:%u: error: no "
+						"character should exist "
+						"after '\\' symbol\n",
+						cfg_filename, line_num);
+					goto error_exit;
+				}
+
+				*pos = '\0';
+
+				if (strlen(oneline) + strlen(str) > 1022) {
+					rte_panic("%s:%u: error: the "
+						"concatenated line "
+						"contains more characters "
+						"the parser can handle\n",
+						cfg_filename, line_num);
+					goto error_exit;
+				}
+
+				strncpy(str + strlen(str), oneline,
+					strlen(oneline));
+
+				continue;
+			}
+
+			/* copy the line to str and process */
+			if (strlen(oneline) + strlen(str) > 1022) {
+				rte_panic("%s:%u: error: the line "
+					"contains more characters the "
+					"parser can handle\n",
+					cfg_filename, line_num);
+				goto error_exit;
+			}
+			strncpy(str + strlen(str), oneline,
+				strlen(oneline));
+
+			str[strlen(str)] = '\n';
+			if (cmdline_parse(cl, str) < 0) {
+				rte_panic("%s:%u: error: parsing \"%s\" "
+					"failed\n", cfg_filename,
+					line_num, str);
+				goto error_exit;
+			}
+
+			if (status.status < 0) {
+				rte_panic("%s:%u: error: %s",
+					cfg_filename, line_num,
+					status.parse_msg);
+				goto error_exit;
+			}
+
+			memset(str, 0, 1024);
+		}
+	} while (get_s != NULL);
+
+	cmdline_stdin_exit(cl);
+	fclose(f);
+
+	return 0;
+
+error_exit:
+	if (cl)
+		cmdline_stdin_exit(cl);
+	if (f)
+		fclose(f);
+
+	return -1;
+}
diff --git a/examples/ipsec-secgw/parser.h b/examples/ipsec-secgw/parser.h
new file mode 100644
index 0000000..d31ae01
--- /dev/null
+++ b/examples/ipsec-secgw/parser.h
@@ -0,0 +1,116 @@
+/*   BSD LICENSE
+ *
+ *   Copyright(c) 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 <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#ifndef __PARSER_H
+#define __PARSER_H
+
+struct parse_status {
+	int status;
+	char parse_msg[256];
+};
+
+#define	APP_CHECK(exp, status, fmt, ...)				\
+do {									\
+	if (!(exp)) {							\
+		sprintf(status->parse_msg, fmt "\n",			\
+			## __VA_ARGS__);				\
+		status->status = -1;					\
+	} else								\
+		status->status = 0;					\
+} while (0)
+
+#define APP_CHECK_PRESENCE(val, str, status)				\
+	APP_CHECK(val == 0, status,					\
+		"item \"%s\" already present", str)
+
+#define APP_CHECK_TOKEN_EQUAL(tokens, index, ref, status)		\
+	APP_CHECK(strcmp(tokens[index], ref) == 0, status,		\
+		"unrecognized input \"%s\": expect \"%s\"\n",		\
+		tokens[index], ref)
+
+static inline int
+is_str_num(const char *str)
+{
+	uint32_t i;
+
+	for (i = 0; i < strlen(str); i++)
+		if (!isdigit(str[i]))
+			return -1;
+
+	return 0;
+}
+
+#define APP_CHECK_TOKEN_IS_NUM(tokens, index, status)			\
+	APP_CHECK(is_str_num(tokens[index]) == 0, status,		\
+	"input \"%s\" is not valid number string", tokens[index])
+
+
+#define INCREMENT_TOKEN_INDEX(index, max_num, status)			\
+do {									\
+	APP_CHECK(index + 1 < max_num, status, "reaching the end of "	\
+		"the token array");					\
+	index++;							\
+} while (0)
+
+int
+parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask);
+
+int
+parse_ipv6_addr(const char *token, struct in6_addr *ipv6, uint32_t *mask);
+
+int
+parse_range(const char *token, uint16_t *low, uint16_t *high);
+
+void
+parse_sp4_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status);
+
+void
+parse_sp6_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status);
+
+void
+parse_sa_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status);
+
+void
+parse_rt_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status);
+
+int
+parse_cfg_file(const char *cfg_filename);
+
+#endif
diff --git a/examples/ipsec-secgw/rt.c b/examples/ipsec-secgw/rt.c
index fa5f042..e03c5f0 100644
--- a/examples/ipsec-secgw/rt.c
+++ b/examples/ipsec-secgw/rt.c
@@ -41,6 +41,7 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
 #define RT_IPV4_MAX_RULES	1024
 #define RT_IPV6_MAX_RULES	1024
@@ -57,135 +58,106 @@ struct ip6_route {
 	uint8_t if_out;
 };
 
-static struct ip4_route rt_ip4_ep0[] = {
-	/* Outbound */
-	/* Tunnels */
-	{ IPv4(172, 16, 2, 5), 32, 0 },
-	{ IPv4(172, 16, 2, 6), 32, 1 },
-	/* Transport */
-	{ IPv4(192, 168, 175, 0), 24, 0 },
-	{ IPv4(192, 168, 176, 0), 24, 1 },
-	/* Bypass */
-	{ IPv4(192, 168, 240, 0), 24, 0 },
-	{ IPv4(192, 168, 241, 0), 24, 1 },
+struct ip4_route rt_ip4[RT_IPV4_MAX_RULES];
+uint32_t nb_rt_ip4;
 
-	/* Inbound */
-	/* Tunnels */
-	{ IPv4(192, 168, 115, 0), 24, 2 },
-	{ IPv4(192, 168, 116, 0), 24, 3 },
-	{ IPv4(192, 168, 65, 0), 24, 2 },
-	{ IPv4(192, 168, 66, 0), 24, 3 },
-	/* Transport */
-	{ IPv4(192, 168, 185, 0), 24, 2 },
-	{ IPv4(192, 168, 186, 0), 24, 3 },
-	/* NULL */
-	{ IPv4(192, 168, 210, 0), 24, 2 },
-	{ IPv4(192, 168, 211, 0), 24, 3 },
-	/* Bypass */
-	{ IPv4(192, 168, 245, 0), 24, 2 },
-	{ IPv4(192, 168, 246, 0), 24, 3 },
-};
-
-static struct ip6_route rt_ip6_ep0[] = {
-	/* Outbound */
-	/* Tunnels */
-	{ { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		  0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 }, 116, 0 },
-	{ { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		  0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 }, 116, 1 },
-	/* Transport */
-	{ { 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00,
-		  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 116, 0 },
-	{ { 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11,
-		  0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00 }, 116, 1 },
-	/* Inbound */
-	/* Tunnels */
-	{ { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa,
-		  0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00 }, 116, 2 },
-	{ { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb,
-		  0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00 }, 116, 3 },
-	{ { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55,
-		  0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00 }, 116, 2 },
-	{ { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66,
-		  0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, 116, 3 },
-	/* Transport */
-	{ { 0xff, 0xff, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00,
-		  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 116, 2 },
-	{ { 0xff, 0xff, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11,
-		  0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00 }, 116, 3 },
-};
-
-static struct ip4_route rt_ip4_ep1[] = {
-	/* Outbound */
-	/* Tunnels */
-	{ IPv4(172, 16, 1, 5), 32, 0 },
-	{ IPv4(172, 16, 1, 6), 32, 1 },
-	/* Transport */
-	{ IPv4(192, 168, 185, 0), 24, 0 },
-	{ IPv4(192, 168, 186, 0), 24, 1 },
-	/* Bypass */
-	{ IPv4(192, 168, 245, 0), 24, 0 },
-	{ IPv4(192, 168, 246, 0), 24, 1 },
+struct ip6_route rt_ip6[RT_IPV4_MAX_RULES];
+uint32_t nb_rt_ip6;
 
-	/* Inbound */
-	/* Tunnels */
-	{ IPv4(192, 168, 105, 0), 24, 2 },
-	{ IPv4(192, 168, 106, 0), 24, 3 },
-	{ IPv4(192, 168, 55, 0), 24, 2 },
-	{ IPv4(192, 168, 56, 0), 24, 3 },
-	/* Transport */
-	{ IPv4(192, 168, 175, 0), 24, 2 },
-	{ IPv4(192, 168, 176, 0), 24, 3 },
-	/* NULL */
-	{ IPv4(192, 168, 200, 0), 24, 2 },
-	{ IPv4(192, 168, 201, 0), 24, 3 },
-	/* Bypass */
-	{ IPv4(192, 168, 240, 0), 24, 2 },
-	{ IPv4(192, 168, 241, 0), 24, 3 },
-};
+void
+parse_rt_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status)
+{
+	uint32_t ti;
+	uint32_t *n_rts = NULL;
+	struct ip4_route *route_ipv4 = NULL;
+	struct ip6_route *route_ipv6 = NULL;
+
+	if (strcmp(tokens[0], "ipv4") == 0) {
+		n_rts = &nb_rt_ip4;
+		route_ipv4 = &rt_ip4[*n_rts];
+
+		APP_CHECK(*n_rts <= RT_IPV4_MAX_RULES - 1, status,
+			"too many rt rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+	} else if (strcmp(tokens[0], "ipv6") == 0) {
+		n_rts = &nb_rt_ip6;
+		route_ipv6 = &rt_ip6[*n_rts];
+
+		APP_CHECK(*n_rts <= RT_IPV6_MAX_RULES - 1, status,
+			"too many rt rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+	} else {
+		APP_CHECK(0, status, "unrecognized input \"%s\"",
+			tokens[0]);
+		return;
+	}
 
-static struct ip6_route rt_ip6_ep1[] = {
-	/* Outbound */
-	/* Tunnels */
-	{ { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		  0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 }, 116, 0 },
-	{ { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		  0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 }, 116, 1 },
-	/* Transport */
-	{ { 0xff, 0xff, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00,
-		  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 116, 0 },
-	{ { 0xff, 0xff, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11,
-		  0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00 }, 116, 1 },
+	for (ti = 1; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "dst") == 0) {
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (route_ipv4 != NULL) {
+				struct in_addr ip;
+				uint32_t depth = 0;
+
+				APP_CHECK(parse_ipv4_addr(tokens[ti],
+					&ip, &depth) == 0, status,
+					"unrecognized input \"%s\", "
+					"expect valid ipv4 addr",
+					tokens[ti]);
+				if (status->status < 0)
+					return;
+				route_ipv4->ip = rte_bswap32(
+					(uint32_t)ip.s_addr);
+				route_ipv4->depth = (uint8_t)depth;
+			} else {
+				struct in6_addr ip;
+				uint32_t depth;
+
+				APP_CHECK(parse_ipv6_addr(tokens[ti],
+					&ip, &depth) == 0, status,
+					"unrecognized input \"%s\", "
+					"expect valid ipv6 address",
+					tokens[ti]);
+				if (status->status < 0)
+					return;
+				memcpy(route_ipv6->ip, ip.s6_addr, 16);
+				route_ipv6->depth = (uint8_t)depth;
+			}
+		}
+
+		if (strcmp(tokens[ti], "port") == 0) {
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
+			if (status->status < 0)
+				return;
+			if (route_ipv4 != NULL)
+				route_ipv4->if_out = atoi(tokens[ti]);
+			else
+				route_ipv6->if_out = atoi(tokens[ti]);
+		}
+	}
 
-	/* Inbound */
-	/* Tunnels */
-	{ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa,
-		  0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00 }, 116, 2 },
-	{ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb,
-		  0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00 }, 116, 3 },
-	{ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55,
-		  0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00 }, 116, 2 },
-	{ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66,
-		  0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, 116, 3 },
-	/* Transport */
-	{ { 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00,
-		  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 116, 2 },
-	{ { 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11,
-		  0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00 }, 116, 3 },
-};
+	*n_rts = *n_rts + 1;
+}
 
 void
-rt_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
+rt_init(struct socket_ctx *ctx, int32_t socket_id)
 {
 	char name[PATH_MAX];
 	uint32_t i;
 	int32_t ret;
 	struct rte_lpm *lpm;
 	struct rte_lpm6 *lpm6;
-	struct ip4_route *rt;
-	struct ip6_route *rt6;
 	char a, b, c, d;
-	uint32_t nb_routes, nb_routes6;
 	struct rte_lpm_config conf = { 0 };
 	struct rte_lpm6_config conf6 = { 0 };
 
@@ -200,23 +172,12 @@ rt_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
 		rte_exit(EXIT_FAILURE, "IPv6 Routing Table for socket %u "
 			"already initialized\n", socket_id);
 
+	if (nb_rt_ip4 == 0 && nb_rt_ip6 == 0)
+		RTE_LOG(WARNING, IPSEC, "No Routing rule specified\n");
+
 	printf("Creating IPv4 Routing Table (RT) context with %u max routes\n",
 			RT_IPV4_MAX_RULES);
 
-	if (ep == 0) {
-		rt = rt_ip4_ep0;
-		nb_routes = RTE_DIM(rt_ip4_ep0);
-		rt6 = rt_ip6_ep0;
-		nb_routes6 = RTE_DIM(rt_ip6_ep0);
-	} else if (ep == 1) {
-		rt = rt_ip4_ep1;
-		nb_routes = RTE_DIM(rt_ip4_ep1);
-		rt6 = rt_ip6_ep1;
-		nb_routes6 = RTE_DIM(rt_ip6_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_ip4", socket_id);
 	conf.max_rules = RT_IPV4_MAX_RULES;
@@ -227,15 +188,17 @@ rt_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
 			"on socket %d\n", name, 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);
+	for (i = 0; i < nb_rt_ip4; i++) {
+		ret = rte_lpm_add(lpm, rt_ip4[i].ip, rt_ip4[i].depth,
+			rt_ip4[i].if_out);
 		if (ret < 0)
 			rte_exit(EXIT_FAILURE, "Fail to add entry num %u to %s "
 				"LPM table on socket %d\n", i, name, socket_id);
 
-		uint32_t_to_char(rt[i].ip, &a, &b, &c, &d);
+		uint32_t_to_char(rt_ip4[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);
+				a, b, c, d, rt_ip4[i].depth,
+				rt_ip4[i].if_out);
 	}
 
 	snprintf(name, sizeof(name), "%s_%u", "rt_ip6", socket_id);
@@ -247,24 +210,24 @@ rt_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
 			"on socket %d\n", name, socket_id);
 
 	/* populate the LPM table */
-	for (i = 0; i < nb_routes6; i++) {
-		ret = rte_lpm6_add(lpm6, rt6[i].ip, rt6[i].depth,
-				rt6[i].if_out);
+	for (i = 0; i < nb_rt_ip6; i++) {
+		ret = rte_lpm6_add(lpm6, rt_ip6[i].ip, rt_ip6[i].depth,
+				rt_ip6[i].if_out);
 		if (ret < 0)
 			rte_exit(EXIT_FAILURE, "Fail to add entry num %u to %s "
 				"LPM table on socket %d\n", i, name, socket_id);
 
 		printf("LPM6: Adding route "
 			" %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%hhx (%hhx)\n",
-			(uint16_t)((rt6[i].ip[0] << 8) | rt6[i].ip[1]),
-			(uint16_t)((rt6[i].ip[2] << 8) | rt6[i].ip[3]),
-			(uint16_t)((rt6[i].ip[4] << 8) | rt6[i].ip[5]),
-			(uint16_t)((rt6[i].ip[6] << 8) | rt6[i].ip[7]),
-			(uint16_t)((rt6[i].ip[8] << 8) | rt6[i].ip[9]),
-			(uint16_t)((rt6[i].ip[10] << 8) | rt6[i].ip[11]),
-			(uint16_t)((rt6[i].ip[12] << 8) | rt6[i].ip[13]),
-			(uint16_t)((rt6[i].ip[14] << 8) | rt6[i].ip[15]),
-			rt6[i].depth, rt6[i].if_out);
+			(uint16_t)((rt_ip6[i].ip[0] << 8) | rt_ip6[i].ip[1]),
+			(uint16_t)((rt_ip6[i].ip[2] << 8) | rt_ip6[i].ip[3]),
+			(uint16_t)((rt_ip6[i].ip[4] << 8) | rt_ip6[i].ip[5]),
+			(uint16_t)((rt_ip6[i].ip[6] << 8) | rt_ip6[i].ip[7]),
+			(uint16_t)((rt_ip6[i].ip[8] << 8) | rt_ip6[i].ip[9]),
+			(uint16_t)((rt_ip6[i].ip[10] << 8) | rt_ip6[i].ip[11]),
+			(uint16_t)((rt_ip6[i].ip[12] << 8) | rt_ip6[i].ip[13]),
+			(uint16_t)((rt_ip6[i].ip[14] << 8) | rt_ip6[i].ip[15]),
+			rt_ip6[i].depth, rt_ip6[i].if_out);
 	}
 
 	ctx->rt_ip4 = (struct rt_ctx *)lpm;
diff --git a/examples/ipsec-secgw/sa.c b/examples/ipsec-secgw/sa.c
index ab18b81..66733ef 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -48,191 +48,248 @@
 
 #include "ipsec.h"
 #include "esp.h"
+#include "parser.h"
 
-/* SAs Outbound */
-const struct ipsec_sa sa_out[] = {
-	{
-	.spi = 5,
-	.src.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip4 = IPv4(172, 16, 2, 5),
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP4_TUNNEL
-	},
-	{
-	.spi = 6,
-	.src.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip4 = IPv4(172, 16, 2, 6),
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP4_TUNNEL
-	},
-	{
-	.spi = 10,
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = TRANSPORT
-	},
-	{
-	.spi = 11,
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = TRANSPORT
-	},
-	{
-	.spi = 15,
-	.src.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip4 = IPv4(172, 16, 2, 5),
-	.cipher_algo = RTE_CRYPTO_CIPHER_NULL,
-	.auth_algo = RTE_CRYPTO_AUTH_NULL,
-	.digest_len = 0,
-	.iv_len = 0,
-	.block_size = 4,
-	.flags = IP4_TUNNEL
-	},
-	{
-	.spi = 16,
-	.src.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip4 = IPv4(172, 16, 2, 6),
-	.cipher_algo = RTE_CRYPTO_CIPHER_NULL,
-	.auth_algo = RTE_CRYPTO_AUTH_NULL,
-	.digest_len = 0,
-	.iv_len = 0,
-	.block_size = 4,
-	.flags = IP4_TUNNEL
-	},
-	{
-	.spi = 25,
-	.src.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
-	.dst.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP6_TUNNEL
-	},
-	{
-	.spi = 26,
-	.src.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
-	.dst.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP6_TUNNEL
-	},
+struct supported_cipher_algo {
+	const char *keyword;
+	enum rte_crypto_cipher_algorithm algo;
+	uint16_t iv_len;
+	uint16_t block_size;
 };
 
-/* SAs Inbound */
-const struct ipsec_sa sa_in[] = {
-	{
-	.spi = 105,
-	.src.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip4 = IPv4(172, 16, 1, 5),
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP4_TUNNEL
-	},
-	{
-	.spi = 106,
-	.src.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip4 = IPv4(172, 16, 1, 6),
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP4_TUNNEL
-	},
-	{
-	.spi = 110,
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = TRANSPORT
-	},
-	{
-	.spi = 111,
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = TRANSPORT
-	},
+struct supported_auth_algo {
+	const char *keyword;
+	enum rte_crypto_auth_algorithm algo;
+	uint16_t digest_len;
+};
+
+const struct supported_cipher_algo cipher_algos[] = {
 	{
-	.spi = 115,
-	.src.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip4 = IPv4(172, 16, 1, 5),
-	.cipher_algo = RTE_CRYPTO_CIPHER_NULL,
-	.auth_algo = RTE_CRYPTO_AUTH_NULL,
-	.digest_len = 0,
-	.iv_len = 0,
-	.block_size = 4,
-	.flags = IP4_TUNNEL
+		.keyword = "null",
+		.algo = RTE_CRYPTO_CIPHER_NULL,
+		.iv_len = 0,
+		.block_size = 4
 	},
 	{
-	.spi = 116,
-	.src.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip4 = IPv4(172, 16, 1, 6),
-	.cipher_algo = RTE_CRYPTO_CIPHER_NULL,
-	.auth_algo = RTE_CRYPTO_AUTH_NULL,
-	.digest_len = 0,
-	.iv_len = 0,
-	.block_size = 4,
-	.flags = IP4_TUNNEL
-	},
+		.keyword = "aes-128-cbc",
+		.algo = RTE_CRYPTO_CIPHER_AES_CBC,
+		.iv_len = 16,
+		.block_size = 16
+	}
+};
+
+const struct supported_auth_algo auth_algos[] = {
 	{
-	.spi = 125,
-	.src.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
-	.dst.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP6_TUNNEL
+		.keyword = "null",
+		.algo = RTE_CRYPTO_AUTH_NULL,
+		.digest_len = 0,
 	},
 	{
-	.spi = 126,
-	.src.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
-	.dst.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
-	.cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
-	.auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
-	.digest_len = 12,
-	.iv_len = 16,
-	.block_size = 16,
-	.flags = IP6_TUNNEL
-	},
+		.keyword = "sha1-hmac",
+		.algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
+		.digest_len = 12
+	}
 };
 
+static int
+find_match_algo(struct ipsec_sa *rule,
+	const char *cipher_keyword, const char *auth_keyword)
+{
+	size_t i;
+	int ret = -2;
+
+	if (cipher_keyword != NULL) {
+		for (i = 0; i < RTE_DIM(cipher_algos); i++) {
+			const struct supported_cipher_algo *algo =
+				&cipher_algos[i];
+
+			if (strcmp(cipher_keyword, algo->keyword) == 0) {
+				rule->cipher_algo = algo->algo;
+				rule->block_size = algo->block_size;
+				rule->iv_len = algo->iv_len;
+				ret += 1;
+				break;
+			}
+		}
+	}
+
+	if (auth_keyword != NULL) {
+		for (i = 0; i < RTE_DIM(auth_algos); i++) {
+			const struct supported_auth_algo *algo =
+				&auth_algos[i];
+
+			if (strcmp(auth_keyword, algo->keyword) == 0) {
+				rule->auth_algo = algo->algo;
+				rule->digest_len = algo->digest_len;
+				ret += 1;
+				break;
+			}
+		}
+	}
+
+	return ret;
+}
+
+#define MAX_SA_RULE_NUM		1000
+
+struct ipsec_sa sa_out[MAX_SA_RULE_NUM];
+uint32_t nb_sa_out;
+
+struct ipsec_sa sa_in[MAX_SA_RULE_NUM];
+uint32_t nb_sa_in;
+
+void
+parse_sa_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status)
+{
+	struct ipsec_sa *rule = NULL;
+	uint32_t ti; /*token index*/
+	uint32_t *ri /*rule index*/;
+	const char *cipher_str, *auth_str;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+
+	if (strcmp(tokens[0], "in") == 0) {
+		ri = &nb_sa_in;
+
+		APP_CHECK(*ri <= MAX_SA_RULE_NUM - 1, status,
+			"too many sa rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule = &sa_in[*ri];
+	} else {
+		ri = &nb_sa_out;
+
+		APP_CHECK(*ri <= MAX_SA_RULE_NUM - 1, status,
+			"too many sa rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule = &sa_out[*ri];
+	}
+
+	/* spi number */
+	APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
+	if (status->status < 0)
+		return;
+	rule->spi = atoi(tokens[1]);
+
+	cipher_str = tokens[2];
+	auth_str = tokens[3];
+
+	APP_CHECK(find_match_algo(rule, cipher_str, auth_str) == 0,
+		status, "Unrecognized cipher or auth algorithm (%s:%s)",
+		cipher_str, auth_str);
+	if (status->status < 0)
+		return;
+
+	if (strcmp(tokens[4], "ipv4-tunnel") == 0)
+		rule->flags = IP4_TUNNEL;
+	else if (strcmp(tokens[4], "ipv6-tunnel") == 0)
+		rule->flags = IP6_TUNNEL;
+	else if (strcmp(tokens[4], "transport") == 0)
+		rule->flags = TRANSPORT;
+	else {
+		APP_CHECK(0, status, "unrecognized input \"%s\"",
+			tokens[4]);
+		return;
+	}
+
+	for (ti = 5; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "src") == 0) {
+			APP_CHECK_PRESENCE(src_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (rule->flags == IP4_TUNNEL) {
+				struct in_addr ip;
+
+				APP_CHECK(parse_ipv4_addr(tokens[ti],
+					&ip, NULL) == 0, status,
+					"unrecognized input \"%s\", "
+					"expect valid ipv4 addr",
+					tokens[ti]);
+				if (status->status < 0)
+					return;
+				rule->src.ip4 = rte_bswap32(
+					(uint32_t)ip.s_addr);
+			} else if (rule->flags == IP6_TUNNEL) {
+				struct in6_addr ip;
+
+				APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
+					NULL) == 0, status,
+					"unrecognized input \"%s\", "
+					"expect valid ipv6 addr",
+					tokens[ti]);
+				if (status->status < 0)
+					return;
+				memcpy(rule->src.ip6_b, ip.s6_addr, 16);
+			} else if (rule->flags == TRANSPORT) {
+				APP_CHECK(0, status, "unrecognized input "
+					"\"%s\"", tokens[ti]);
+				return;
+			}
+
+			src_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "dst") == 0) {
+			APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (rule->flags == IP4_TUNNEL) {
+				struct in_addr ip;
+
+				APP_CHECK(parse_ipv4_addr(tokens[ti],
+					&ip, NULL) == 0, status,
+					"unrecognized input \"%s\", "
+					"expect valid ipv4 addr",
+					tokens[ti]);
+				if (status->status < 0)
+					return;
+				rule->dst.ip4 = rte_bswap32(
+					(uint32_t)ip.s_addr);
+			} else if (rule->flags == IP6_TUNNEL) {
+				struct in6_addr ip;
+
+				APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
+					NULL) == 0, status,
+					"unrecognized input \"%s\", "
+					"expect valid ipv6 addr",
+					tokens[ti]);
+				if (status->status < 0)
+					return;
+				memcpy(rule->dst.ip6_b, ip.s6_addr, 16);
+			} else if (rule->flags == TRANSPORT) {
+				APP_CHECK(0, status, "unrecognized "
+					"input \"%s\"",	tokens[ti]);
+				return;
+			}
+
+			dst_p = 1;
+			continue;
+		}
+
+		/* unrecognizeable input */
+		APP_CHECK(0, status, "unrecognized input \"%s\"",
+			tokens[ti]);
+		return;
+	}
+
+	*ri = *ri + 1;
+}
+
 static uint8_t cipher_key[256] = "sixteenbytes key";
 
 /* AES CBC xform */
@@ -386,10 +443,8 @@ sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 }
 
 void
-sa_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
+sa_init(struct socket_ctx *ctx, int32_t socket_id)
 {
-	const struct ipsec_sa *sa_out_entries, *sa_in_entries;
-	uint32_t nb_out_entries, nb_in_entries;
 	const char *name;
 
 	if (ctx == NULL)
@@ -403,35 +458,30 @@ sa_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
 		rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
 				"initialized\n", socket_id);
 
-	if (ep == 0) {
-		sa_out_entries = sa_out;
-		nb_out_entries = RTE_DIM(sa_out);
-		sa_in_entries = sa_in;
-		nb_in_entries = RTE_DIM(sa_in);
-	} else if (ep == 1) {
-		sa_out_entries = sa_in;
-		nb_out_entries = RTE_DIM(sa_in);
-		sa_in_entries = sa_out;
-		nb_in_entries = RTE_DIM(sa_out);
-	} else
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
-
-	name = "sa_in";
-	ctx->sa_in = sa_create(name, socket_id);
-	if (ctx->sa_in == NULL)
-		rte_exit(EXIT_FAILURE, "Error [%d] creating SA context %s "
-				"in socket %d\n", rte_errno, name, socket_id);
-
-	name = "sa_out";
-	ctx->sa_out = sa_create(name, socket_id);
-	if (ctx->sa_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_in, sa_in_entries, nb_in_entries);
-
-	sa_out_add_rules(ctx->sa_out, sa_out_entries, nb_out_entries);
+	if (nb_sa_out == 0 && nb_sa_in == 0)
+		RTE_LOG(WARNING, IPSEC, "No SA rule specified\n");
+
+	if (nb_sa_in > 0) {
+		name = "sa_in";
+		ctx->sa_in = sa_create(name, socket_id);
+		if (ctx->sa_in == 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_in, sa_in, nb_sa_in);
+	}
+
+	if (nb_sa_out > 0) {
+		name = "sa_out";
+		ctx->sa_out = sa_create(name, socket_id);
+		if (ctx->sa_out == NULL)
+			rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
+				"context %s in socket %d\n", rte_errno,
+				name, socket_id);
+
+		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
+	}
 }
 
 int
diff --git a/examples/ipsec-secgw/sp4.c b/examples/ipsec-secgw/sp4.c
index 9c4b256..d2c0849 100644
--- a/examples/ipsec-secgw/sp4.c
+++ b/examples/ipsec-secgw/sp4.c
@@ -42,6 +42,7 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
 #define MAX_ACL_RULE_NUM	1000
 
@@ -113,211 +114,306 @@ struct rte_acl_field_def ip4_defs[NUM_FIELDS_IPV4] = {
 
 RTE_ACL_RULE_DEF(acl4_rules, RTE_DIM(ip4_defs));
 
-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, 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 = 1},
-	/* 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(10), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 175, 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(11), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 176, 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(15), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 200, 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(16), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 201, 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(25), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 55, 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(26), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 56, 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 = BYPASS, .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 240, 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 = BYPASS, .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 241, 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,}
+struct acl4_rules acl4_rules_out[MAX_ACL_RULE_NUM];
+uint32_t nb_acl4_rules_out;
+
+struct acl4_rules acl4_rules_in[MAX_ACL_RULE_NUM];
+uint32_t nb_acl4_rules_in;
+
+void
+parse_sp4_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status)
+{
+	struct acl4_rules *rule_ipv4 = NULL;
+
+	uint32_t *ri = NULL; /* rule index */
+	uint32_t ti = 0; /* token index */
+
+	uint32_t esp_p = 0;
+	uint32_t protect_p = 0;
+	uint32_t bypass_p = 0;
+	uint32_t discard_p = 0;
+	uint32_t pri_p = 0;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+	uint32_t proto_p = 0;
+	uint32_t sport_p = 0;
+	uint32_t dport_p = 0;
+
+	if (strcmp(tokens[1], "in") == 0) {
+		ri = &nb_acl4_rules_in;
+
+		APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
+			"too many sp rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule_ipv4 = &acl4_rules_in[*ri];
+
+	} else if (strcmp(tokens[1], "out") == 0) {
+		ri = &nb_acl4_rules_out;
+
+		APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
+			"too many sp rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule_ipv4 = &acl4_rules_out[*ri];
+	} else {
+		APP_CHECK(0, status, "unrecognized input \"%s\", expect"
+			" \"in\" or \"out\"\n", tokens[ti]);
+		return;
 	}
-};
 
-const struct acl4_rules acl4_rules_in[] = {
-	{
-	.data = {.userdata = PROTECT(105), .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(106), .category_mask = 1, .priority = 1},
-	/* 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(110), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 185, 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(111), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 186, 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(115), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 210, 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(116), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 211, 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(125), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 65, 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(126), .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 66, 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 = BYPASS, .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 245, 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 = BYPASS, .category_mask = 1, .priority = 1},
-	/* destination IPv4 */
-	.field[2] = {.value.u32 = IPv4(192, 168, 246, 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,}
+	rule_ipv4->data.category_mask = 1;
+
+	for (ti = 2; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "esp") == 0) {
+			/* currently do nothing */
+			APP_CHECK_PRESENCE(esp_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			esp_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "protect") == 0) {
+			APP_CHECK_PRESENCE(protect_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			APP_CHECK(bypass_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"bypass");
+			if (status->status < 0)
+				return;
+			APP_CHECK(discard_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"discard");
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->data.userdata =
+				PROTECT(atoi(tokens[ti]));
+
+			protect_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "bypass") == 0) {
+			APP_CHECK_PRESENCE(bypass_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			APP_CHECK(protect_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"protect");
+			if (status->status < 0)
+				return;
+			APP_CHECK(discard_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"discard");
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->data.userdata = BYPASS;
+
+			bypass_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "discard") == 0) {
+			APP_CHECK_PRESENCE(discard_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			APP_CHECK(protect_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"protect");
+			if (status->status < 0)
+				return;
+			APP_CHECK(bypass_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"discard");
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->data.userdata = DISCARD;
+
+			discard_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "pri") == 0) {
+			APP_CHECK_PRESENCE(pri_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->data.priority = atoi(tokens[ti]);
+
+			pri_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "src") == 0) {
+			struct in_addr ip;
+			uint32_t depth;
+
+			APP_CHECK_PRESENCE(src_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
+				&depth) == 0, status, "unrecognized "
+				"input \"%s\", expect valid ipv4 addr",
+				tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->field[1].value.u32 =
+				rte_bswap32(ip.s_addr);
+			rule_ipv4->field[1].mask_range.u32 =
+				depth;
+
+			src_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "dst") == 0) {
+			struct in_addr ip;
+			uint32_t depth;
+
+			APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+			APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
+				&depth) == 0, status, "unrecognized "
+				"input \"%s\", expect valid ipv4 addr",
+				tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->field[2].value.u32 =
+				rte_bswap32(ip.s_addr);
+			rule_ipv4->field[2].mask_range.u32 =
+				depth;
+
+			dst_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "proto") == 0) {
+			uint16_t low, high;
+
+			APP_CHECK_PRESENCE(proto_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_range(tokens[ti], &low, &high)
+				== 0, status, "unrecognized input \"%s\""
+				", expect \"from:to\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+			APP_CHECK(low <= 0xff, status, "proto low "
+				"over-limit");
+			if (status->status < 0)
+				return;
+			APP_CHECK(high <= 0xff, status, "proto high "
+				"over-limit");
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->field[0].value.u8 = (uint8_t)low;
+			rule_ipv4->field[0].mask_range.u8 = (uint8_t)high;
+
+			proto_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "sport") == 0) {
+			uint16_t port_low, port_high;
+
+			APP_CHECK_PRESENCE(sport_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_range(tokens[ti], &port_low,
+				&port_high) == 0, status, "unrecognized "
+				"input \"%s\", expect \"port_from:"
+				"port_to\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->field[3].value.u16 = port_low;
+			rule_ipv4->field[3].mask_range.u16 = port_high;
+
+			sport_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "dport") == 0) {
+			uint16_t port_low, port_high;
+
+			APP_CHECK_PRESENCE(dport_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_range(tokens[ti], &port_low,
+				&port_high) == 0, status, "unrecognized "
+				"input \"%s\", expect \"port_from:"
+				"port_to\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv4->field[4].value.u16 = port_low;
+			rule_ipv4->field[4].mask_range.u16 = port_high;
+
+			dport_p = 1;
+			continue;
+		}
+
+		/* unrecognizeable input */
+		APP_CHECK(0, status, "unrecognized input \"%s\"",
+			tokens[ti]);
+		return;
 	}
-};
+
+	/* check if argument(s) are missing */
+	APP_CHECK(esp_p == 1, status, "missing argument \"esp\"");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(protect_p | bypass_p | discard_p, status, "missing "
+		"argument \"protect\", \"bypass\", or \"discard\"");
+	if (status->status < 0)
+		return;
+
+	*ri = *ri + 1;
+}
 
 static void
 print_one_ip4_rule(const struct acl4_rules *rule, int32_t extra)
@@ -406,11 +502,9 @@ acl4_init(const char *name, int32_t socketid, const struct acl4_rules *rules,
 }
 
 void
-sp4_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
+sp4_init(struct socket_ctx *ctx, int32_t socket_id)
 {
 	const char *name;
-	const struct acl4_rules *rules_out, *rules_in;
-	uint32_t nb_out_rules, nb_in_rules;
 
 	if (ctx == NULL)
 		rte_exit(EXIT_FAILURE, "NULL context.\n");
@@ -423,25 +517,18 @@ sp4_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
 		rte_exit(EXIT_FAILURE, "Outbound SP DB for socket %u already "
 				"initialized\n", socket_id);
 
-	if (ep == 0) {
-		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 if (ep == 1) {
-		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
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
-
-	name = "sp_ip4_in";
-	ctx->sp_ip4_in = (struct sp_ctx *)acl4_init(name, socket_id,
-			rules_in, nb_in_rules);
-
-	name = "sp_ip4_out";
-	ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name, socket_id,
-			rules_out, nb_out_rules);
+	if (nb_acl4_rules_out == 0 && nb_acl4_rules_in == 0)
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP rule specified\n");
+
+	if (nb_acl4_rules_in > 0) {
+		name = "sp_ip4_in";
+		ctx->sp_ip4_in = (struct sp_ctx *)acl4_init(name,
+			socket_id, acl4_rules_in, nb_acl4_rules_in);
+	}
+
+	if (nb_acl4_rules_out > 0) {
+		name = "sp_ip4_out";
+		ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
+			socket_id, acl4_rules_out, nb_acl4_rules_out);
+	}
 }
diff --git a/examples/ipsec-secgw/sp6.c b/examples/ipsec-secgw/sp6.c
index 1dda11a..4360630 100644
--- a/examples/ipsec-secgw/sp6.c
+++ b/examples/ipsec-secgw/sp6.c
@@ -42,6 +42,7 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
 #define MAX_ACL_RULE_NUM	1000
 
@@ -144,155 +145,363 @@ struct rte_acl_field_def ip6_defs[IP6_NUM] = {
 
 RTE_ACL_RULE_DEF(acl6_rules, RTE_DIM(ip6_defs));
 
-const struct acl6_rules acl6_rules_out[] = {
-	{
-	.data = {.userdata = PROTECT(5), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x55555555, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(6), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x66666666, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(10), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x11111111, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x00000000, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(11), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x11111111, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x11111111, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(25), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0xaaaaaaaa, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(26), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0xbbbbbbbb, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
+struct acl6_rules acl6_rules_out[MAX_ACL_RULE_NUM];
+uint32_t nb_acl6_rules_out;
+
+struct acl6_rules acl6_rules_in[MAX_ACL_RULE_NUM];
+uint32_t nb_acl6_rules_in;
+
+void
+parse_sp6_tokens(char **tokens, uint32_t n_tokens,
+	struct parse_status *status)
+{
+	struct acl6_rules *rule_ipv6 = NULL;
+
+	uint32_t *ri = NULL; /* rule index */
+	uint32_t ti = 0; /* token index */
+
+	uint32_t esp_p = 0;
+	uint32_t protect_p = 0;
+	uint32_t bypass_p = 0;
+	uint32_t discard_p = 0;
+	uint32_t pri_p = 0;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+	uint32_t proto_p = 0;
+	uint32_t sport_p = 0;
+	uint32_t dport_p = 0;
+
+	if (strcmp(tokens[1], "in") == 0) {
+		ri = &nb_acl6_rules_in;
+
+		APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status, "too "
+			"many sp rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule_ipv6 = &acl6_rules_in[*ri];
+
+	} else if (strcmp(tokens[1], "out") == 0) {
+		ri = &nb_acl6_rules_out;
+
+		APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status, "too "
+			"many sp rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule_ipv6 = &acl6_rules_out[*ri];
+
+	} else {
+		APP_CHECK(0, status, "unrecognized input \"%s\", expect"
+			" \"in\" or \"out\"\n", tokens[ti]);
+		return;
 	}
-};
 
-const struct acl6_rules acl6_rules_in[] = {
-	{
-	.data = {.userdata = PROTECT(15), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0xffff0000, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x55555555, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(16), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0xffff0000, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x66666666, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(110), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0xffff0000, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x11111111, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x00000000, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(111), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0xffff0000, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x11111111, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0x11111111, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(125), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0xffff0000, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0xaaaaaaaa, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
-	},
-	{
-	.data = {.userdata = PROTECT(126), .category_mask = 1, .priority = 1},
-	/* destination IPv6 */
-	.field[5] = {.value.u32 = 0xffff0000, .mask_range.u32 = 32,},
-	.field[6] = {.value.u32 = 0x0, .mask_range.u32 = 32,},
-	.field[7] = {.value.u32 = 0xbbbbbbbb, .mask_range.u32 = 32,},
-	.field[8] = {.value.u32 = 0x0, .mask_range.u32 = 0,},
-	/* source port */
-	.field[9] = {.value.u16 = 0, .mask_range.u16 = 0xffff,},
-	/* destination port */
-	.field[10] = {.value.u16 = 0, .mask_range.u16 = 0xffff,}
+	rule_ipv6->data.category_mask = 1;
+
+
+	for (ti = 2; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "esp") == 0) {
+			/* currently do nothing */
+			APP_CHECK_PRESENCE(esp_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			esp_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "protect") == 0) {
+			APP_CHECK_PRESENCE(protect_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			APP_CHECK(bypass_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"bypass");
+			if (status->status < 0)
+				return;
+			APP_CHECK(discard_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"discard");
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->data.userdata =
+				PROTECT(atoi(tokens[ti]));
+
+			protect_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "bypass") == 0) {
+			APP_CHECK_PRESENCE(bypass_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			APP_CHECK(protect_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"protect");
+			if (status->status < 0)
+				return;
+			APP_CHECK(discard_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"discard");
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->data.userdata = BYPASS;
+
+			bypass_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "discard") == 0) {
+			APP_CHECK_PRESENCE(discard_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			APP_CHECK(protect_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"protect");
+			if (status->status < 0)
+				return;
+			APP_CHECK(bypass_p == 0, status, "conflict item "
+				"between \"%s\" and \"%s\"", tokens[ti],
+				"discard");
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->data.userdata = DISCARD;
+
+			discard_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "pri") == 0) {
+			APP_CHECK_PRESENCE(pri_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->data.priority = atoi(tokens[ti]);
+
+			pri_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "src") == 0) {
+			struct in6_addr ip;
+			uint32_t depth;
+
+			APP_CHECK_PRESENCE(src_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
+				&depth) == 0, status, "unrecognized "
+				"input \"%s\", expect valid ipv6 "
+				"addr", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->field[1].value.u32 =
+				(uint32_t)ip.s6_addr[0] << 24 |
+				(uint32_t)ip.s6_addr[1] << 16 |
+				(uint32_t)ip.s6_addr[2] << 8 |
+				(uint32_t)ip.s6_addr[3];
+			rule_ipv6->field[1].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+			depth = (depth > 32) ? (depth - 32) : 0;
+			rule_ipv6->field[2].value.u32 =
+				(uint32_t)ip.s6_addr[4] << 24 |
+				(uint32_t)ip.s6_addr[5] << 16 |
+				(uint32_t)ip.s6_addr[6] << 8 |
+				(uint32_t)ip.s6_addr[7];
+			rule_ipv6->field[2].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+			depth = (depth > 32) ? (depth - 32) : 0;
+			rule_ipv6->field[3].value.u32 =
+				(uint32_t)ip.s6_addr[8] << 24 |
+				(uint32_t)ip.s6_addr[9] << 16 |
+				(uint32_t)ip.s6_addr[10] << 8 |
+				(uint32_t)ip.s6_addr[11];
+			rule_ipv6->field[3].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+			depth = (depth > 32) ? (depth - 32) : 0;
+			rule_ipv6->field[4].value.u32 =
+				(uint32_t)ip.s6_addr[12] << 24 |
+				(uint32_t)ip.s6_addr[13] << 16 |
+				(uint32_t)ip.s6_addr[14] << 8 |
+				(uint32_t)ip.s6_addr[15];
+			rule_ipv6->field[4].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+
+			src_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "dst") == 0) {
+			struct in6_addr ip;
+			uint32_t depth;
+
+			APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
+				&depth) == 0, status, "unrecognized "
+				"input \"%s\", expect valid ipv6 "
+				"addr", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->field[5].value.u32 =
+				(uint32_t)ip.s6_addr[0] << 24 |
+				(uint32_t)ip.s6_addr[1] << 16 |
+				(uint32_t)ip.s6_addr[2] << 8 |
+				(uint32_t)ip.s6_addr[3];
+			rule_ipv6->field[5].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+			depth = (depth > 32) ? (depth - 32) : 0;
+			rule_ipv6->field[6].value.u32 =
+				(uint32_t)ip.s6_addr[4] << 24 |
+				(uint32_t)ip.s6_addr[5] << 16 |
+				(uint32_t)ip.s6_addr[6] << 8 |
+				(uint32_t)ip.s6_addr[7];
+			rule_ipv6->field[6].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+			depth = (depth > 32) ? (depth - 32) : 0;
+			rule_ipv6->field[7].value.u32 =
+				(uint32_t)ip.s6_addr[8] << 24 |
+				(uint32_t)ip.s6_addr[9] << 16 |
+				(uint32_t)ip.s6_addr[10] << 8 |
+				(uint32_t)ip.s6_addr[11];
+			rule_ipv6->field[7].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+			depth = (depth > 32) ? (depth - 32) : 0;
+			rule_ipv6->field[8].value.u32 =
+				(uint32_t)ip.s6_addr[12] << 24 |
+				(uint32_t)ip.s6_addr[13] << 16 |
+				(uint32_t)ip.s6_addr[14] << 8 |
+				(uint32_t)ip.s6_addr[15];
+			rule_ipv6->field[8].mask_range.u32 =
+				(depth > 32) ? 32 : depth;
+
+			dst_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "proto") == 0) {
+			uint16_t low, high;
+
+			APP_CHECK_PRESENCE(proto_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_range(tokens[ti], &low, &high)
+				== 0, status, "unrecognized input \"%s\""
+				", expect \"from:to\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+			APP_CHECK(low <= 0xff, status, "proto low "
+				"over-limit");
+			if (status->status < 0)
+				return;
+			APP_CHECK(high <= 0xff, status, "proto high "
+				"over-limit");
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->field[0].value.u8 = (uint8_t)low;
+			rule_ipv6->field[0].mask_range.u8 = (uint8_t)high;
+
+			proto_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "sport") == 0) {
+			uint16_t port_low, port_high;
+
+			APP_CHECK_PRESENCE(sport_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_range(tokens[ti], &port_low,
+				&port_high) == 0, status, "unrecognized "
+				"input \"%s\", expect \"port_from:"
+				"port_to\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->field[9].value.u16 = port_low;
+			rule_ipv6->field[9].mask_range.u16 = port_high;
+
+			sport_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "dport") == 0) {
+			uint16_t port_low, port_high;
+
+			APP_CHECK_PRESENCE(dport_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_range(tokens[ti], &port_low,
+				&port_high) == 0, status, "unrecognized "
+				"input \"%s\", expect \"port_from:"
+				"port_to\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			rule_ipv6->field[10].value.u16 = port_low;
+			rule_ipv6->field[10].mask_range.u16 = port_high;
+
+			dport_p = 1;
+			continue;
+		}
+
+		/* unrecognizeable input */
+		APP_CHECK(0, status, "unrecognized input \"%s\"",
+			tokens[ti]);
+		return;
 	}
-};
+
+	/* check if argument(s) are missing */
+	APP_CHECK(esp_p == 1, status, "missing argument \"esp\"");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(protect_p | bypass_p | discard_p, status, "missing "
+		"argument \"protect\", \"bypass\", or \"discard\"");
+	if (status->status < 0)
+		return;
+
+	*ri = *ri + 1;
+}
 
 static inline void
 print_one_ip6_rule(const struct acl6_rules *rule, int32_t extra)
@@ -407,11 +616,9 @@ acl6_init(const char *name, int32_t socketid, const struct acl6_rules *rules,
 }
 
 void
-sp6_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
+sp6_init(struct socket_ctx *ctx, int32_t socket_id)
 {
 	const char *name;
-	const struct acl6_rules *rules_out, *rules_in;
-	uint32_t nb_out_rules, nb_in_rules;
 
 	if (ctx == NULL)
 		rte_exit(EXIT_FAILURE, "NULL context.\n");
@@ -424,25 +631,18 @@ sp6_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
 		rte_exit(EXIT_FAILURE, "Outbound IPv6 SP DB for socket %u "
 				"already initialized\n", socket_id);
 
-	if (ep == 0) {
-		rules_out = acl6_rules_out;
-		nb_out_rules = RTE_DIM(acl6_rules_out);
-		rules_in = acl6_rules_in;
-		nb_in_rules = RTE_DIM(acl6_rules_in);
-	} else if (ep == 1) {
-		rules_out = acl6_rules_in;
-		nb_out_rules = RTE_DIM(acl6_rules_in);
-		rules_in = acl6_rules_out;
-		nb_in_rules = RTE_DIM(acl6_rules_out);
-	} else
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
-
-	name = "sp_ip6_in";
-	ctx->sp_ip6_in = (struct sp_ctx *)acl6_init(name, socket_id,
-			rules_in, nb_in_rules);
-
-	name = "sp_ip6_out";
-	ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name, socket_id,
-			rules_out, nb_out_rules);
+	if (nb_acl6_rules_out == 0 && nb_acl6_rules_in == 0)
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP rule specified\n");
+
+	if (nb_acl6_rules_in > 0) {
+		name = "sp_ip6_in";
+		ctx->sp_ip6_in = (struct sp_ctx *)acl6_init(name,
+			socket_id, acl6_rules_in, nb_acl6_rules_in);
+	}
+
+	if (nb_acl6_rules_out > 0) {
+		name = "sp_ip6_out";
+		ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name,
+			socket_id, acl6_rules_out, nb_acl6_rules_out);
+	}
 }
-- 
2.5.5

  reply	other threads:[~2016-07-11 14:43 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-07-07 11:31 [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-07-07 11:31 ` [PATCH 1/2] " Fan Zhang
2016-07-07 11:31 ` [PATCH 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-07-11 14:43 ` [PATCH v2 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-07-11 14:43   ` Fan Zhang [this message]
2016-07-11 15:19     ` [PATCH v2 1/2] examples/ipsec-secgw: " Thomas Monjalon
2016-07-11 14:43   ` [PATCH v2 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-07-12  9:44   ` [PATCH v3 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-07-12  9:44     ` [PATCH v3 1/2] examples/ipsec-secgw: " Fan Zhang
2016-07-19  8:19       ` Sergio Gonzalez Monroy
2016-07-12  9:44     ` [PATCH v3 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-07-21 10:51     ` [PATCH v4 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-07-21 10:51       ` [PATCH v4 1/2] examples/ipsec-secgw: " Fan Zhang
2016-07-21 10:51       ` [PATCH v4 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-08-22 10:49       ` [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-08-22 10:49         ` [PATCH 1/2] examples/ipsec-secgw: " Fan Zhang
2016-08-22 10:49         ` [PATCH 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-08-23 14:46         ` [PATCH v6 0/2] [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-08-23 14:46           ` [PATCH v6 1/2] examples/ipsec-secgw: " Fan Zhang
2016-09-07 18:13             ` De Lara Guarch, Pablo
2016-08-23 14:46           ` [PATCH v6 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-09-20 11:13         ` [PATCH v7 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-09-20 11:13           ` [PATCH v7 1/2] examples/ipsec-secgw: " Fan Zhang
2016-09-20 11:13           ` [PATCH v7 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-09-21 12:05         ` [PATCH v8 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
2016-09-21 12:05           ` [PATCH v8 1/2] examples/ipsec-secgw: " Fan Zhang
2016-09-23  7:52             ` Sergio Gonzalez Monroy
2016-09-23 22:51               ` De Lara Guarch, Pablo
2016-09-29  1:19             ` Chen, Zhaoyan
2016-09-21 12:05           ` [PATCH v8 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
2016-09-23  7:53             ` Sergio Gonzalez Monroy
2016-09-23 22:51               ` De Lara Guarch, Pablo

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=1468248213-13100-2-git-send-email-roy.fan.zhang@intel.com \
    --to=roy.fan.zhang@intel.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.