All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] examples/ipsec_secgw: add configuration file support
@ 2016-07-07 11:31 Fan Zhang
  2016-07-07 11:31 ` [PATCH 1/2] " Fan Zhang
                   ` (2 more replies)
  0 siblings, 3 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-07 11:31 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

Fan Zhang (2):
  examples/ipsec_secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/sample_app_ug/ipsec_secgw.rst | 806 ++++++++++++-------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 119 +++++
 examples/ipsec-secgw/ep1.cfg             | 119 +++++
 examples/ipsec-secgw/ipsec-secgw.c       |  48 +-
 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                | 428 ++++++++--------
 examples/ipsec-secgw/sp4.c               | 523 +++++++++++---------
 examples/ipsec-secgw/sp6.c               | 524 +++++++++++++-------
 12 files changed, 2311 insertions(+), 1238 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH 1/2] examples/ipsec_secgw: add configuration file support
  2016-07-07 11:31 [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-07-07 11:31 ` 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
  2 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-07 11:31 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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 | 806 ++++++++++++-------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ipsec-secgw.c       |  48 +-
 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                | 428 ++++++++--------
 examples/ipsec-secgw/sp4.c               | 523 +++++++++++---------
 examples/ipsec-secgw/sp6.c               | 524 +++++++++++++-------
 10 files changed, 2073 insertions(+), 1238 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..125bfa7 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,10 @@ 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.
 
-*   ``--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 +153,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 +191,10 @@ 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.
 
 Refer to the *DPDK Getting Started Guide* for general information on running
 applications and the Environment Abstraction Layer (EAL) options.
@@ -219,496 +218,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 path/to/config_file
 
 
 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..950ff5c 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
 
@@ -158,7 +159,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;
@@ -954,18 +954,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
@@ -984,10 +972,11 @@ parse_args(int32_t argc, char **argv)
 		{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 +1000,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 +1027,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 +1410,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 +1439,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..2290e52
--- /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;
+
+	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) - 3) {
+					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..2c09ac0 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,19 +458,8 @@ 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);
+	if (nb_sa_out == 0 && nb_sa_in == 0)
+		RTE_LOG(WARNING, IPSEC, "No SA rule specified\n");
 
 	name = "sa_in";
 	ctx->sa_in = sa_create(name, socket_id);
@@ -429,9 +473,9 @@ sa_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
 		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_in_add_rules(ctx->sa_in, sa_in, nb_sa_in);
 
-	sa_out_add_rules(ctx->sa_out, sa_out_entries, nb_out_entries);
+	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..aa127a5 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,14 @@ 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);
+	if (nb_acl4_rules_out == 0 && nb_acl4_rules_in == 0)
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP rule specified\n");
 
 	name = "sp_ip4_in";
 	ctx->sp_ip4_in = (struct sp_ctx *)acl4_init(name, socket_id,
-			rules_in, nb_in_rules);
+			acl4_rules_in, nb_acl4_rules_in);
 
 	name = "sp_ip4_out";
 	ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name, socket_id,
-			rules_out, nb_out_rules);
+			acl4_rules_out, nb_acl4_rules_out);
 }
diff --git a/examples/ipsec-secgw/sp6.c b/examples/ipsec-secgw/sp6.c
index 1dda11a..0c1103d 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,14 @@ 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);
+	if (nb_acl6_rules_out == 0 && nb_acl6_rules_in == 0)
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP rule specified\n");
 
 	name = "sp_ip6_in";
 	ctx->sp_ip6_in = (struct sp_ctx *)acl6_init(name, socket_id,
-			rules_in, nb_in_rules);
+			acl6_rules_in, nb_acl6_rules_in);
 
 	name = "sp_ip6_out";
 	ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name, socket_id,
-			rules_out, nb_out_rules);
+			acl6_rules_out, nb_acl6_rules_out);
 }
-- 
2.5.5

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

* [PATCH 2/2] examples/ipsec-secgw: add sample configuration files
  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 ` Fan Zhang
  2016-07-11 14:43 ` [PATCH v2 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
  2 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-07 11:31 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to set-up systems
back-to-back that would forward traffic through an IPsec tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 119 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 119 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 238 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..be4732a
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,119 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa out 6 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa out 10 aes-128-cbc sha1-hmac transport
+sa out 11 aes-128-cbc sha1-hmac transport
+sa out 15 null null ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa out 16 null null ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa out 25 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+sa out 26 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa in 106 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa in 110 aes-128-cbc sha1-hmac transport
+sa in 111 aes-128-cbc sha1-hmac transport
+sa in 115 null null ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa in 116 null null ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa in 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
+sa in 126 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3 
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..9feb0fa
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,119 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa in 6 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa in 10 aes-128-cbc sha1-hmac transport
+sa in 11 aes-128-cbc sha1-hmac transport
+sa in 15 null null ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa in 16 null null ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa in 25 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+sa in 26 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa out 106 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa out 110 aes-128-cbc sha1-hmac transport
+sa out 111 aes-128-cbc sha1-hmac transport
+sa out 115 null null ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa out 116 null null ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+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
+sa out 126 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* [PATCH v2 0/2] examples/ipsec_secgw: add configuration file support
  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 ` Fan Zhang
  2016-07-11 14:43   ` [PATCH v2 1/2] examples/ipsec-secgw: " Fan Zhang
                     ` (2 more replies)
  2 siblings, 3 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-11 14:43 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

v2 changes:
- fix configuration file parsing error.
- update doc to remove whitespace tailing errors.

Fan Zhang (2):
  examples/ipsec-secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/sample_app_ug/ipsec_secgw.rst | 809 ++++++++++++-------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 119 +++++
 examples/ipsec-secgw/ep1.cfg             | 119 +++++
 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 ++++++++++++++-------
 12 files changed, 2357 insertions(+), 1271 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH v2 1/2] examples/ipsec-secgw: add configuration file support
  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
  2016-07-11 15:19     ` 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
  2 siblings, 1 reply; 32+ messages in thread
From: Fan Zhang @ 2016-07-11 14:43 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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

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

* [PATCH v2 2/2] examples/ipsec-secgw: add sample configuration files
  2016-07-11 14:43 ` [PATCH v2 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
  2016-07-11 14:43   ` [PATCH v2 1/2] examples/ipsec-secgw: " Fan Zhang
@ 2016-07-11 14:43   ` Fan Zhang
  2016-07-12  9:44   ` [PATCH v3 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
  2 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-11 14:43 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to set-up systems
back-to-back that would forward traffic through an IPsec tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 119 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 119 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 238 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..c10e22b
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,119 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa out 6 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa out 10 aes-128-cbc sha1-hmac transport
+sa out 11 aes-128-cbc sha1-hmac transport
+sa out 15 null null ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa out 16 null null ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa out 25 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+sa out 26 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa in 106 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa in 110 aes-128-cbc sha1-hmac transport
+sa in 111 aes-128-cbc sha1-hmac transport
+sa in 115 null null ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa in 116 null null ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa in 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
+sa in 126 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..216704b
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,119 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa in 6 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa in 10 aes-128-cbc sha1-hmac transport
+sa in 11 aes-128-cbc sha1-hmac transport
+sa in 15 null null ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa in 16 null null ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa in 25 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+sa in 26 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa out 106 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa out 110 aes-128-cbc sha1-hmac transport
+sa out 111 aes-128-cbc sha1-hmac transport
+sa out 115 null null ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa out 116 null null ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+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
+sa out 126 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* Re: [PATCH v2 1/2] examples/ipsec-secgw: add configuration file support
  2016-07-11 14:43   ` [PATCH v2 1/2] examples/ipsec-secgw: " Fan Zhang
@ 2016-07-11 15:19     ` Thomas Monjalon
  0 siblings, 0 replies; 32+ messages in thread
From: Thomas Monjalon @ 2016-07-11 15:19 UTC (permalink / raw)
  To: Fan Zhang; +Cc: dev, sergio.gonzalez.monroy

2016-07-11 15:43, Fan Zhang:
> 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.

There is a 32-bit compilation error:
examples/ipsec-secgw/parser.c:286:10: error:
format ‘%lu’ expects argument of type ‘long unsigned int’,
but argument 2 has type ‘size_t {aka unsigned int}’ [-Werror=format=]
   printf("len error, has %lu, expect %i\n", strlen(ip_str),

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

* [PATCH v3 0/2] examples/ipsec_secgw: add configuration file support
  2016-07-11 14:43 ` [PATCH v2 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
  2016-07-11 14:43   ` [PATCH v2 1/2] examples/ipsec-secgw: " Fan Zhang
  2016-07-11 14:43   ` [PATCH v2 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
@ 2016-07-12  9:44   ` Fan Zhang
  2016-07-12  9:44     ` [PATCH v3 1/2] examples/ipsec-secgw: " Fan Zhang
                       ` (2 more replies)
  2 siblings, 3 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-12  9:44 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

v3 change:
- fix 32-bit compilation error

v2 changes:
- fix configuration file parsing error.
- update doc to remove whitespace tailing errors.

Fan Zhang (2):
  examples/ipsec-secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/sample_app_ug/ipsec_secgw.rst | 809 ++++++++++++-------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 119 +++++
 examples/ipsec-secgw/ep1.cfg             | 119 +++++
 examples/ipsec-secgw/ipsec-secgw.c       |  58 +--
 examples/ipsec-secgw/ipsec.h             |   8 +-
 examples/ipsec-secgw/parser.c            | 599 +++++++++++++++++++++++
 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 ++++++++++++++-------
 12 files changed, 2354 insertions(+), 1271 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH v3 1/2] examples/ipsec-secgw: add configuration file support
  2016-07-12  9:44   ` [PATCH v3 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-07-12  9:44     ` 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
  2 siblings, 1 reply; 32+ messages in thread
From: Fan Zhang @ 2016-07-12  9:44 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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            | 599 +++++++++++++++++++++++
 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, 2116 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..99bdfc5
--- /dev/null
+++ b/examples/ipsec-secgw/parser.c
@@ -0,0 +1,599 @@
+/*-
+ *   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)
+		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

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

* [PATCH v3 2/2] examples/ipsec-secgw: add sample configuration files
  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-12  9:44     ` Fan Zhang
  2016-07-21 10:51     ` [PATCH v4 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
  2 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-12  9:44 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to set-up systems
back-to-back that would forward traffic through an IPsec tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 119 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 119 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 238 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..c10e22b
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,119 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa out 6 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa out 10 aes-128-cbc sha1-hmac transport
+sa out 11 aes-128-cbc sha1-hmac transport
+sa out 15 null null ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa out 16 null null ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa out 25 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+sa out 26 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa in 106 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa in 110 aes-128-cbc sha1-hmac transport
+sa in 111 aes-128-cbc sha1-hmac transport
+sa in 115 null null ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa in 116 null null ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa in 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
+sa in 126 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..216704b
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,119 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa in 6 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa in 10 aes-128-cbc sha1-hmac transport
+sa in 11 aes-128-cbc sha1-hmac transport
+sa in 15 null null ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+sa in 16 null null ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+sa in 25 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+sa in 26 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa out 106 aes-128-cbc sha1-hmac ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+sa out 110 aes-128-cbc sha1-hmac transport
+sa out 111 aes-128-cbc sha1-hmac transport
+sa out 115 null null ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+sa out 116 null null ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+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
+sa out 126 aes-128-cbc sha1-hmac ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* Re: [PATCH v3 1/2] examples/ipsec-secgw: add configuration file support
  2016-07-12  9:44     ` [PATCH v3 1/2] examples/ipsec-secgw: " Fan Zhang
@ 2016-07-19  8:19       ` Sergio Gonzalez Monroy
  0 siblings, 0 replies; 32+ messages in thread
From: Sergio Gonzalez Monroy @ 2016-07-19  8:19 UTC (permalink / raw)
  To: Fan Zhang, dev

On 12/07/2016 10:44, Fan Zhang wrote:
> 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>

I think we should be able to set the key also on config file for both 
cipher and auth.
Then we can check that the key size is the expected by the chosen 
cipher/auth algo.

I think we should also create and set the xforms dynamically instead of 
static as
they currently are (sa_add_rules function).

Sergio

> Routing rule format:
> rt <ip_ver> <src_ip> <dst_ip> <port>
>
> Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
> ---

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

* [PATCH v4 0/2] examples/ipsec_secgw: add configuration file support
  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-12  9:44     ` [PATCH v3 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
@ 2016-07-21 10:51     ` Fan Zhang
  2016-07-21 10:51       ` [PATCH v4 1/2] examples/ipsec-secgw: " Fan Zhang
                         ` (2 more replies)
  2 siblings, 3 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-21 10:51 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

v4 change:
- rebase the patchset on top of 
  "examples/ipsec-secgw: fix GCC 4.5.x build error"
  (http://dpdk.org/dev/patchwork/patch/14895/)
- add cipher_key and auth_key configuration options to SA rules
- updated documentation for the new options

v3 change:
- fix 32-bit compilation error

v2 changes:
- fix configuration file parsing error.
- update doc to remove whitespace tailing errors.

Fan Zhang (2):
  examples/ipsec-secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 160 ++++++
 examples/ipsec-secgw/ep1.cfg             | 160 ++++++
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 737 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 12 files changed, 2703 insertions(+), 1319 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH v4 1/2] examples/ipsec-secgw: add configuration file support
  2016-07-21 10:51     ` [PATCH v4 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-07-21 10:51       ` 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
  2 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-21 10:51 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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> <cipher_key> <auth_algo> <auth_key> \
<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 | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 737 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 10 files changed, 2383 insertions(+), 1319 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..5cce2fe 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,357 @@ 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
+
+ * Syntax: *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> <cipher_key> <auth_algo> <auth_key>
+    <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
+
+ * Syntax: *cipher_algo <your algorithm>*
+
+``<cipher_key>``
+
+ * Cipher key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <cipher_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified cipher algorithm
+   key size.
+
+   For example: *cipher_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<auth_algo>``
+
+ * Authentication algorithm
+
+ * Optional: No
+
+ * Available options:
+
+    * *null*: NULL algorithm
+    * *sha1-hmac*: HMAC SHA1 algorithm
+
+``<auth_key>``
+
+ * Authentication key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <auth_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified authentication
+   algorithm key size.
+
+   For example: *auth_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<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
+
+ * Syntax: mode XXX
+
+``<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 out 5 cipher_algo null auth_algo null mode ipv4-tunnel \
+    src 172.16.1.5 dst 172.16.2.5
+
+    sa out 25 cipher_algo aes-128-cbc \
+    cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    auth_algo sha1-hmac \
+    auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    mode ipv6-tunnel \
+    src 1111:1111:1111:1111:1111:1111:1111:5555 \
+    dst 2222:2222:2222:2222:2222:2222:2222: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 1ca144b..4e84972 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;
@@ -838,7 +836,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"
@@ -846,8 +844,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
@@ -960,18 +958,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
@@ -986,14 +972,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) {
@@ -1017,6 +1002,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);
@@ -1029,6 +1029,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;
 
@@ -1407,9 +1412,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",
@@ -1439,13 +1441,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 a442a74..4cc316c 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -90,6 +90,8 @@ struct ip_addr {
 	} ip;
 };
 
+#define MAX_KEY_SIZE		20
+
 struct ipsec_sa {
 	uint32_t spi;
 	uint32_t cdev_id_qp;
@@ -106,6 +108,10 @@ struct ipsec_sa {
 #define TRANSPORT  (1 << 2)
 	struct ip_addr src;
 	struct ip_addr dst;
+	uint8_t cipher_key[MAX_KEY_SIZE];
+	uint16_t cipher_key_len;
+	uint8_t auth_key[MAX_KEY_SIZE];
+	uint16_t auth_key_len;
 	struct rte_crypto_sym_xform *xforms;
 } __rte_cache_aligned;
 
@@ -183,15 +189,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..99bdfc5
--- /dev/null
+++ b/examples/ipsec-secgw/parser.c
@@ -0,0 +1,599 @@
+/*-
+ *   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)
+		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 4439e0f..e16e58c 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -48,243 +48,451 @@
 
 #include "ipsec.h"
 #include "esp.h"
+#include "parser.h"
+
+struct supported_cipher_algo {
+	const char *keyword;
+	enum rte_crypto_cipher_algorithm algo;
+	uint16_t iv_len;
+	uint16_t block_size;
+	uint16_t key_len;
+};
 
-/* SAs Outbound */
-const struct ipsec_sa sa_out[] = {
-	{
-	.spi = 5,
-	.src.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
-	.dst.ip.ip6.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
-	.dst.ip.ip6.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_auth_algo {
+	const char *keyword;
+	enum rte_crypto_auth_algorithm algo;
+	uint16_t digest_len;
+	uint16_t key_len;
 };
 
-/* SAs Inbound */
-const struct ipsec_sa sa_in[] = {
+const struct supported_cipher_algo cipher_algos[] = {
 	{
-	.spi = 105,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
+		.keyword = "null",
+		.algo = RTE_CRYPTO_CIPHER_NULL,
+		.iv_len = 0,
+		.block_size = 4,
+		.key_len = 0
 	},
 	{
-	.spi = 106,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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
-	},
-	{
-	.spi = 115,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
-	},
-	{
-	.spi = 116,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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,
+		.key_len = 16
+	}
+};
+
+const struct supported_auth_algo auth_algos[] = {
 	{
-	.spi = 125,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
-	.dst.ip.ip6.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,
+		.key_len = 0
 	},
 	{
-	.spi = 126,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
-	.dst.ip.ip6.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,
+		.key_len = 20
+	}
 };
 
-static uint8_t cipher_key[256] = "sixteenbytes key";
+struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_out;
+
+struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_in;
+
+static const struct supported_cipher_algo *
+find_match_cipher_algo(const char *cipher_keyword)
+{
+	size_t i;
+
+	for (i = 0; i < RTE_DIM(cipher_algos); i++) {
+		const struct supported_cipher_algo *algo =
+			&cipher_algos[i];
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform aescbc_enc_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+		if (strcmp(cipher_keyword, algo->keyword) == 0)
+			return algo;
 	}
-};
 
-const struct rte_crypto_sym_xform aescbc_dec_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+	return NULL;
+}
+
+static const struct supported_auth_algo *
+find_match_auth_algo(const char *auth_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-static uint8_t auth_key[256] = "twentybytes hash key";
+	return NULL;
+}
 
-/* SHA1 HMAC xform */
-const struct rte_crypto_sym_xform sha1hmac_gen_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+/** parse_key_string
+ *  parse x:x:x:x.... hex number key string into uint8_t *key
+ *  return:
+ *  > 0: number of bytes parsed
+ *  0:   failed
+ */
+static uint32_t
+parse_key_string(const char *key_str, uint8_t *key)
+{
+	const char *pt_start = key_str, *pt_end = key_str;
+	char sub_str[3];
+	uint32_t nb_bytes = 0;
+
+	while (pt_end != NULL) {
+		pt_end = strchr(pt_start, ':');
+
+		if (pt_end == NULL)
+			strncpy(sub_str, pt_start, strlen(pt_start));
+		else {
+			if (pt_end - pt_start > 2)
+				return 0;
+
+			strncpy(sub_str, pt_start, pt_end - pt_start);
+			pt_start = pt_end + 1;
+		}
+
+		key[nb_bytes++] = strtol(sub_str, NULL, 16);
 	}
-};
 
-const struct rte_crypto_sym_xform sha1hmac_verify_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+	return nb_bytes;
+}
+
+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*/;
+	uint32_t cipher_algo_p = 0;
+	uint32_t auth_algo_p = 0;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+	uint32_t mode_p = 0;
+
+	if (strcmp(tokens[0], "in") == 0) {
+		ri = &nb_sa_in;
+
+		APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 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 <= IPSEC_SA_MAX_ENTRIES - 1, status,
+			"too many sa rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule = &sa_out[*ri];
 	}
-};
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform null_cipher_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { .algo = RTE_CRYPTO_CIPHER_NULL }
+	/* spi number */
+	APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
+	if (status->status < 0)
+		return;
+	rule->spi = atoi(tokens[1]);
+
+	for (ti = 2; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "mode") == 0) {
+			APP_CHECK_PRESENCE(mode_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (strcmp(tokens[ti], "ipv4-tunnel") == 0)
+				rule->flags = IP4_TUNNEL;
+			else if (strcmp(tokens[ti], "ipv6-tunnel") == 0)
+				rule->flags = IP6_TUNNEL;
+			else if (strcmp(tokens[ti], "transport") == 0)
+				rule->flags = TRANSPORT;
+			else {
+				APP_CHECK(0, status, "unrecognized "
+					"input \"%s\"", tokens[ti]);
+				return;
+			}
+
+			mode_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "cipher_algo") == 0) {
+			const struct supported_cipher_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_cipher_algo(tokens[ti]);
+
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->cipher_algo = algo->algo;
+			rule->block_size = algo->block_size;
+			rule->iv_len = algo->iv_len;
+			rule->cipher_key_len = algo->key_len;
+
+			/* for NULL algorithm, no cipher key should
+			 * exist */
+			if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
+				cipher_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"cipher_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->cipher_key);
+			APP_CHECK(key_len == rule->cipher_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+
+			cipher_algo_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "auth_algo") == 0) {
+			const struct supported_auth_algo *algo;
+
+			APP_CHECK_PRESENCE(auth_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_auth_algo(tokens[ti]);
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->auth_algo = algo->algo;
+			rule->auth_key_len = algo->key_len;
+			rule->digest_len = algo->digest_len;
+
+			/* for NULL algorithm, no auth key should exist */
+			if (rule->auth_algo == RTE_CRYPTO_AUTH_NULL) {
+				auth_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"auth_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(parse_key_string(tokens[ti],
+				rule->auth_key) > 0, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+
+			auth_algo_p = 1;
+			continue;
+		}
+
+		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.ip.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.ip.ip6.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.ip.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.ip.ip6.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;
 	}
-};
 
-const struct rte_crypto_sym_xform null_auth_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { .algo = RTE_CRYPTO_AUTH_NULL }
+	APP_CHECK(cipher_algo_p == 1, status, "missing cipher options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(auth_algo_p == 1, status, "missing auth options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(mode_p == 1, status, "missing mode option");
+	if (status->status < 0)
+		return;
+
+	*ri = *ri + 1;
+}
+
+static inline void
+print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
+{
+	uint32_t i;
+	uint8_t a, b, c, d;
+
+	printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
+
+	for (i = 0; i < RTE_DIM(cipher_algos); i++) {
+		if (cipher_algos[i].algo == sa->cipher_algo) {
+			printf("%s ", cipher_algos[i].keyword);
+			break;
+		}
 	}
-};
+
+	for (i = 0; i < RTE_DIM(auth_algos); i++) {
+		if (auth_algos[i].algo == sa->auth_algo) {
+			printf("%s ", auth_algos[i].keyword);
+			break;
+		}
+	}
+
+	printf("mode:");
+
+	switch (sa->flags) {
+	case IP4_TUNNEL:
+		printf("IP4Tunnel ");
+		uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
+		uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu", a, b, c, d);
+		break;
+	case IP6_TUNNEL:
+		printf("IP6Tunnel ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
+		}
+		printf(" ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
+		}
+		break;
+	case TRANSPORT:
+		printf("Transport");
+		break;
+	}
+	printf("\n");
+}
 
 struct sa_ctx {
 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
@@ -347,25 +555,49 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 		}
 
 		if (inbound) {
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_auth_xf;
-				sa_ctx->xf[idx].b = null_cipher_xf;
-			} else {
-				sa_ctx->xf[idx].a = sha1hmac_verify_xf;
-				sa_ctx->xf[idx].b = aescbc_dec_xf;
-			}
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].b.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].b.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			sa_ctx->xf[idx].b.next = NULL;
+
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].a.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].a.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].a.auth.op =
+				RTE_CRYPTO_AUTH_OP_VERIFY;
+
 		} else { /* outbound */
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_cipher_xf;
-				sa_ctx->xf[idx].b = null_auth_xf;
-			} else {
-				sa_ctx->xf[idx].a = aescbc_enc_xf;
-				sa_ctx->xf[idx].b = sha1hmac_gen_xf;
-			}
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].a.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].a.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			sa_ctx->xf[idx].a.next = NULL;
+
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].b.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].b.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].b.auth.op =
+				RTE_CRYPTO_AUTH_OP_GENERATE;
 		}
+
 		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
 		sa_ctx->xf[idx].b.next = NULL;
 		sa->xforms = &sa_ctx->xf[idx].a;
+
+		print_one_sa_rule(sa, inbound);
 	}
 
 	return 0;
@@ -386,10 +618,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 +633,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);
+	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);
 
-	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, nb_sa_in);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
 
-	sa_in_add_rules(ctx->sa_in, sa_in_entries, nb_in_entries);
+	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_entries, nb_out_entries);
+		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
+			"specified\n");
 }
 
 int
diff --git a/examples/ipsec-secgw/sp4.c b/examples/ipsec-secgw/sp4.c
index 9c4b256..38c72a9 100644
--- a/examples/ipsec-secgw/sp4.c
+++ b/examples/ipsec-secgw/sp4.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 /*
  * Rule and trace formats definitions.
@@ -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,19 @@ 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);
+	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);
 	} 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);
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip4_out";
+		ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
+			socket_id, acl4_rules_out, nb_acl4_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
+			"specified\n");
 }
diff --git a/examples/ipsec-secgw/sp6.c b/examples/ipsec-secgw/sp6.c
index 1dda11a..62fb492 100644
--- a/examples/ipsec-secgw/sp6.c
+++ b/examples/ipsec-secgw/sp6.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 enum {
 	IP6_PROTO,
@@ -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,19 @@ 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);
+	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);
 	} else
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip6_out";
+		ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name,
+			socket_id, acl6_rules_out, nb_acl6_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Outbound rule "
+			"specified\n");
 }
-- 
2.5.5

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

* [PATCH v4 2/2] examples/ipsec-secgw: add sample configuration files
  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       ` Fan Zhang
  2016-08-22 10:49       ` [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
  2 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-07-21 10:51 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to set-up systems
back-to-back that would forward traffic through an IPsec tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 320 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..299aa9e
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa out 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa out 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa out 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa out 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa out 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa in 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa in 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa in 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..3f6ff81
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa in 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa in 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa in 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa in 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa in 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa out 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa out 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa out 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* [PATCH 0/2] examples/ipsec_secgw: add configuration file support
  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       ` Fan Zhang
  2016-08-22 10:49         ` [PATCH 1/2] examples/ipsec-secgw: " Fan Zhang
                           ` (4 more replies)
  2 siblings, 5 replies; 32+ messages in thread
From: Fan Zhang @ 2016-08-22 10:49 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

v5 change:
- fix missed SA cipher key length checking parsing error
- fix SA ip address printing

v4 change:
- rebase the patchset on top of 
  "examples/ipsec-secgw: fix GCC 4.5.x build error"
  (http://dpdk.org/dev/patchwork/patch/14895/)
- add cipher_key and auth_key configuration options to SA rules
- updated documentation for the new options

v3 change:
- fix 32-bit compilation error

v2 changes:
- fix configuration file parsing error.
- update doc to remove whitespace tailing errors.

Fan Zhang (2):
  examples/ipsec-secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 160 ++++++
 examples/ipsec-secgw/ep1.cfg             | 160 ++++++
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 743 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 12 files changed, 2709 insertions(+), 1319 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH 1/2] examples/ipsec-secgw: add configuration file support
  2016-08-22 10:49       ` [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-08-22 10:49         ` Fan Zhang
  2016-08-22 10:49         ` [PATCH 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
                           ` (3 subsequent siblings)
  4 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-08-22 10:49 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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> <cipher_key> <auth_algo> <auth_key> \
<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 | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 743 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 10 files changed, 2389 insertions(+), 1319 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..5cce2fe 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,357 @@ 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
+
+ * Syntax: *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> <cipher_key> <auth_algo> <auth_key>
+    <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
+
+ * Syntax: *cipher_algo <your algorithm>*
+
+``<cipher_key>``
+
+ * Cipher key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <cipher_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified cipher algorithm
+   key size.
+
+   For example: *cipher_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<auth_algo>``
+
+ * Authentication algorithm
+
+ * Optional: No
+
+ * Available options:
+
+    * *null*: NULL algorithm
+    * *sha1-hmac*: HMAC SHA1 algorithm
+
+``<auth_key>``
+
+ * Authentication key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <auth_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified authentication
+   algorithm key size.
+
+   For example: *auth_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<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
+
+ * Syntax: mode XXX
+
+``<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 out 5 cipher_algo null auth_algo null mode ipv4-tunnel \
+    src 172.16.1.5 dst 172.16.2.5
+
+    sa out 25 cipher_algo aes-128-cbc \
+    cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    auth_algo sha1-hmac \
+    auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    mode ipv6-tunnel \
+    src 1111:1111:1111:1111:1111:1111:1111:5555 \
+    dst 2222:2222:2222:2222:2222:2222:2222: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 5d04eb3..8b55534 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;
@@ -838,7 +836,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"
@@ -846,8 +844,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
@@ -960,18 +958,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
@@ -986,14 +972,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) {
@@ -1017,6 +1002,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);
@@ -1029,6 +1029,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;
 
@@ -1411,9 +1416,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",
@@ -1443,13 +1445,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 a442a74..4cc316c 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -90,6 +90,8 @@ struct ip_addr {
 	} ip;
 };
 
+#define MAX_KEY_SIZE		20
+
 struct ipsec_sa {
 	uint32_t spi;
 	uint32_t cdev_id_qp;
@@ -106,6 +108,10 @@ struct ipsec_sa {
 #define TRANSPORT  (1 << 2)
 	struct ip_addr src;
 	struct ip_addr dst;
+	uint8_t cipher_key[MAX_KEY_SIZE];
+	uint16_t cipher_key_len;
+	uint8_t auth_key[MAX_KEY_SIZE];
+	uint16_t auth_key_len;
 	struct rte_crypto_sym_xform *xforms;
 } __rte_cache_aligned;
 
@@ -183,15 +189,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..99bdfc5
--- /dev/null
+++ b/examples/ipsec-secgw/parser.c
@@ -0,0 +1,599 @@
+/*-
+ *   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)
+		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 4439e0f..fa1dcda 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -48,243 +48,457 @@
 
 #include "ipsec.h"
 #include "esp.h"
+#include "parser.h"
+
+struct supported_cipher_algo {
+	const char *keyword;
+	enum rte_crypto_cipher_algorithm algo;
+	uint16_t iv_len;
+	uint16_t block_size;
+	uint16_t key_len;
+};
 
-/* SAs Outbound */
-const struct ipsec_sa sa_out[] = {
-	{
-	.spi = 5,
-	.src.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
-	.dst.ip.ip6.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
-	.dst.ip.ip6.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_auth_algo {
+	const char *keyword;
+	enum rte_crypto_auth_algorithm algo;
+	uint16_t digest_len;
+	uint16_t key_len;
 };
 
-/* SAs Inbound */
-const struct ipsec_sa sa_in[] = {
+const struct supported_cipher_algo cipher_algos[] = {
 	{
-	.spi = 105,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
+		.keyword = "null",
+		.algo = RTE_CRYPTO_CIPHER_NULL,
+		.iv_len = 0,
+		.block_size = 4,
+		.key_len = 0
 	},
 	{
-	.spi = 106,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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
-	},
-	{
-	.spi = 115,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
-	},
-	{
-	.spi = 116,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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,
+		.key_len = 16
+	}
+};
+
+const struct supported_auth_algo auth_algos[] = {
 	{
-	.spi = 125,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
-	.dst.ip.ip6.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,
+		.key_len = 0
 	},
 	{
-	.spi = 126,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
-	.dst.ip.ip6.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,
+		.key_len = 20
+	}
 };
 
-static uint8_t cipher_key[256] = "sixteenbytes key";
+struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_out;
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform aescbc_enc_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_in;
+
+static const struct supported_cipher_algo *
+find_match_cipher_algo(const char *cipher_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-const struct rte_crypto_sym_xform aescbc_dec_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+	return NULL;
+}
+
+static const struct supported_auth_algo *
+find_match_auth_algo(const char *auth_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-static uint8_t auth_key[256] = "twentybytes hash key";
+	return NULL;
+}
+
+/** parse_key_string
+ *  parse x:x:x:x.... hex number key string into uint8_t *key
+ *  return:
+ *  > 0: number of bytes parsed
+ *  0:   failed
+ */
+static uint32_t
+parse_key_string(const char *key_str, uint8_t *key)
+{
+	const char *pt_start = key_str, *pt_end = key_str;
+	char sub_str[3];
+	uint32_t nb_bytes = 0;
+
+	while (pt_end != NULL) {
+		pt_end = strchr(pt_start, ':');
+
+		if (pt_end == NULL)
+			strncpy(sub_str, pt_start, strlen(pt_start));
+		else {
+			if (pt_end - pt_start > 2)
+				return 0;
+
+			strncpy(sub_str, pt_start, pt_end - pt_start);
+			pt_start = pt_end + 1;
+		}
 
-/* SHA1 HMAC xform */
-const struct rte_crypto_sym_xform sha1hmac_gen_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+		key[nb_bytes++] = strtol(sub_str, NULL, 16);
 	}
-};
 
-const struct rte_crypto_sym_xform sha1hmac_verify_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+	return nb_bytes;
+}
+
+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*/;
+	uint32_t cipher_algo_p = 0;
+	uint32_t auth_algo_p = 0;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+	uint32_t mode_p = 0;
+
+	if (strcmp(tokens[0], "in") == 0) {
+		ri = &nb_sa_in;
+
+		APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 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 <= IPSEC_SA_MAX_ENTRIES - 1, status,
+			"too many sa rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule = &sa_out[*ri];
 	}
-};
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform null_cipher_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { .algo = RTE_CRYPTO_CIPHER_NULL }
+	/* spi number */
+	APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
+	if (status->status < 0)
+		return;
+	rule->spi = atoi(tokens[1]);
+
+	for (ti = 2; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "mode") == 0) {
+			APP_CHECK_PRESENCE(mode_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (strcmp(tokens[ti], "ipv4-tunnel") == 0)
+				rule->flags = IP4_TUNNEL;
+			else if (strcmp(tokens[ti], "ipv6-tunnel") == 0)
+				rule->flags = IP6_TUNNEL;
+			else if (strcmp(tokens[ti], "transport") == 0)
+				rule->flags = TRANSPORT;
+			else {
+				APP_CHECK(0, status, "unrecognized "
+					"input \"%s\"", tokens[ti]);
+				return;
+			}
+
+			mode_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "cipher_algo") == 0) {
+			const struct supported_cipher_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_cipher_algo(tokens[ti]);
+
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->cipher_algo = algo->algo;
+			rule->block_size = algo->block_size;
+			rule->iv_len = algo->iv_len;
+			rule->cipher_key_len = algo->key_len;
+
+			/* for NULL algorithm, no cipher key should
+			 * exist */
+			if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
+				cipher_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"cipher_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->cipher_key);
+			APP_CHECK(key_len == rule->cipher_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			cipher_algo_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "auth_algo") == 0) {
+			const struct supported_auth_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(auth_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_auth_algo(tokens[ti]);
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->auth_algo = algo->algo;
+			rule->auth_key_len = algo->key_len;
+			rule->digest_len = algo->digest_len;
+
+			/* for NULL algorithm, no auth key should exist */
+			if (rule->auth_algo == RTE_CRYPTO_AUTH_NULL) {
+				auth_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"auth_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->auth_key);
+			APP_CHECK(key_len == rule->auth_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			auth_algo_p = 1;
+			continue;
+		}
+
+		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.ip.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.ip.ip6.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.ip.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.ip.ip6.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;
 	}
-};
 
-const struct rte_crypto_sym_xform null_auth_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { .algo = RTE_CRYPTO_AUTH_NULL }
+	APP_CHECK(cipher_algo_p == 1, status, "missing cipher options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(auth_algo_p == 1, status, "missing auth options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(mode_p == 1, status, "missing mode option");
+	if (status->status < 0)
+		return;
+
+	*ri = *ri + 1;
+}
+
+static inline void
+print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
+{
+	uint32_t i;
+	uint8_t a, b, c, d;
+
+	printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
+
+	for (i = 0; i < RTE_DIM(cipher_algos); i++) {
+		if (cipher_algos[i].algo == sa->cipher_algo) {
+			printf("%s ", cipher_algos[i].keyword);
+			break;
+		}
 	}
-};
+
+	for (i = 0; i < RTE_DIM(auth_algos); i++) {
+		if (auth_algos[i].algo == sa->auth_algo) {
+			printf("%s ", auth_algos[i].keyword);
+			break;
+		}
+	}
+
+	printf("mode:");
+
+	switch (sa->flags) {
+	case IP4_TUNNEL:
+		printf("IP4Tunnel ");
+		uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu ", d, c, b, a);
+		uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu", d, c, b, a);
+		break;
+	case IP6_TUNNEL:
+		printf("IP6Tunnel ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
+		}
+		printf(" ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
+		}
+		break;
+	case TRANSPORT:
+		printf("Transport");
+		break;
+	}
+	printf("\n");
+}
 
 struct sa_ctx {
 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
@@ -347,25 +561,49 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 		}
 
 		if (inbound) {
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_auth_xf;
-				sa_ctx->xf[idx].b = null_cipher_xf;
-			} else {
-				sa_ctx->xf[idx].a = sha1hmac_verify_xf;
-				sa_ctx->xf[idx].b = aescbc_dec_xf;
-			}
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].b.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].b.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			sa_ctx->xf[idx].b.next = NULL;
+
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].a.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].a.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].a.auth.op =
+				RTE_CRYPTO_AUTH_OP_VERIFY;
+
 		} else { /* outbound */
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_cipher_xf;
-				sa_ctx->xf[idx].b = null_auth_xf;
-			} else {
-				sa_ctx->xf[idx].a = aescbc_enc_xf;
-				sa_ctx->xf[idx].b = sha1hmac_gen_xf;
-			}
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].a.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].a.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			sa_ctx->xf[idx].a.next = NULL;
+
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].b.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].b.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].b.auth.op =
+				RTE_CRYPTO_AUTH_OP_GENERATE;
 		}
+
 		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
 		sa_ctx->xf[idx].b.next = NULL;
 		sa->xforms = &sa_ctx->xf[idx].a;
+
+		print_one_sa_rule(sa, inbound);
 	}
 
 	return 0;
@@ -386,10 +624,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 +639,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);
+	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);
 
-	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, nb_sa_in);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
 
-	sa_in_add_rules(ctx->sa_in, sa_in_entries, nb_in_entries);
+	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_entries, nb_out_entries);
+		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
+			"specified\n");
 }
 
 int
diff --git a/examples/ipsec-secgw/sp4.c b/examples/ipsec-secgw/sp4.c
index 9c4b256..38c72a9 100644
--- a/examples/ipsec-secgw/sp4.c
+++ b/examples/ipsec-secgw/sp4.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 /*
  * Rule and trace formats definitions.
@@ -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,19 @@ 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);
+	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);
 	} 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);
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip4_out";
+		ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
+			socket_id, acl4_rules_out, nb_acl4_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
+			"specified\n");
 }
diff --git a/examples/ipsec-secgw/sp6.c b/examples/ipsec-secgw/sp6.c
index 1dda11a..62fb492 100644
--- a/examples/ipsec-secgw/sp6.c
+++ b/examples/ipsec-secgw/sp6.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 enum {
 	IP6_PROTO,
@@ -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,19 @@ 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);
+	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);
 	} else
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip6_out";
+		ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name,
+			socket_id, acl6_rules_out, nb_acl6_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Outbound rule "
+			"specified\n");
 }
-- 
2.5.5

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

* [PATCH 2/2] examples/ipsec-secgw: add sample configuration files
  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         ` Fan Zhang
  2016-08-23 14:46         ` [PATCH v6 0/2] [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
                           ` (2 subsequent siblings)
  4 siblings, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-08-22 10:49 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to set-up systems
back-to-back that would forward traffic through an IPsec tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 320 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..299aa9e
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa out 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa out 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa out 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa out 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa out 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa in 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa in 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa in 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..3f6ff81
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa in 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa in 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa in 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa in 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa in 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa out 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa out 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa out 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* [PATCH v6 0/2] [PATCH 0/2] examples/ipsec_secgw: add configuration file support
  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         ` Fan Zhang
  2016-08-23 14:46           ` [PATCH v6 1/2] examples/ipsec-secgw: " Fan Zhang
  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-21 12:05         ` [PATCH v8 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
  4 siblings, 2 replies; 32+ messages in thread
From: Fan Zhang @ 2016-08-23 14:46 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

v6 change:
- fix SA digest key always 0 error

v5 change:
- fix missed SA key length checking parsing error
- fix SA ip address printing

v4 change:
- rebase the patchset on top of 
  "examples/ipsec-secgw: fix GCC 4.5.x build error"
  (http://dpdk.org/dev/patchwork/patch/14895/)
- add cipher_key and auth_key configuration options to SA rules
- updated documentation for the new options

v3 change:
- fix 32-bit compilation error

v2 changes:
- fix configuration file parsing error.
- update doc to remove whitespace tailing errors.

Fan Zhang (2):
  examples/ipsec-secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 160 ++++++
 examples/ipsec-secgw/ep1.cfg             | 160 ++++++
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 12 files changed, 2713 insertions(+), 1319 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH v6 1/2] examples/ipsec-secgw: add configuration file support
  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           ` 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
  1 sibling, 1 reply; 32+ messages in thread
From: Fan Zhang @ 2016-08-23 14:46 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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> <cipher_key> <auth_algo> <auth_key> \
<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 | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 10 files changed, 2393 insertions(+), 1319 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..5cce2fe 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,357 @@ 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
+
+ * Syntax: *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> <cipher_key> <auth_algo> <auth_key>
+    <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
+
+ * Syntax: *cipher_algo <your algorithm>*
+
+``<cipher_key>``
+
+ * Cipher key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <cipher_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified cipher algorithm
+   key size.
+
+   For example: *cipher_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<auth_algo>``
+
+ * Authentication algorithm
+
+ * Optional: No
+
+ * Available options:
+
+    * *null*: NULL algorithm
+    * *sha1-hmac*: HMAC SHA1 algorithm
+
+``<auth_key>``
+
+ * Authentication key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <auth_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified authentication
+   algorithm key size.
+
+   For example: *auth_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<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
+
+ * Syntax: mode XXX
+
+``<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 out 5 cipher_algo null auth_algo null mode ipv4-tunnel \
+    src 172.16.1.5 dst 172.16.2.5
+
+    sa out 25 cipher_algo aes-128-cbc \
+    cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    auth_algo sha1-hmac \
+    auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    mode ipv6-tunnel \
+    src 1111:1111:1111:1111:1111:1111:1111:5555 \
+    dst 2222:2222:2222:2222:2222:2222:2222: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 5d04eb3..8b55534 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;
@@ -838,7 +836,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"
@@ -846,8 +844,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
@@ -960,18 +958,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
@@ -986,14 +972,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) {
@@ -1017,6 +1002,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);
@@ -1029,6 +1029,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;
 
@@ -1411,9 +1416,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",
@@ -1443,13 +1445,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 a442a74..4cc316c 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -90,6 +90,8 @@ struct ip_addr {
 	} ip;
 };
 
+#define MAX_KEY_SIZE		20
+
 struct ipsec_sa {
 	uint32_t spi;
 	uint32_t cdev_id_qp;
@@ -106,6 +108,10 @@ struct ipsec_sa {
 #define TRANSPORT  (1 << 2)
 	struct ip_addr src;
 	struct ip_addr dst;
+	uint8_t cipher_key[MAX_KEY_SIZE];
+	uint16_t cipher_key_len;
+	uint8_t auth_key[MAX_KEY_SIZE];
+	uint16_t auth_key_len;
 	struct rte_crypto_sym_xform *xforms;
 } __rte_cache_aligned;
 
@@ -183,15 +189,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..99bdfc5
--- /dev/null
+++ b/examples/ipsec-secgw/parser.c
@@ -0,0 +1,599 @@
+/*-
+ *   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)
+		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 4439e0f..54b5b19 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -48,243 +48,457 @@
 
 #include "ipsec.h"
 #include "esp.h"
+#include "parser.h"
+
+struct supported_cipher_algo {
+	const char *keyword;
+	enum rte_crypto_cipher_algorithm algo;
+	uint16_t iv_len;
+	uint16_t block_size;
+	uint16_t key_len;
+};
 
-/* SAs Outbound */
-const struct ipsec_sa sa_out[] = {
-	{
-	.spi = 5,
-	.src.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
-	.dst.ip.ip6.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
-	.dst.ip.ip6.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_auth_algo {
+	const char *keyword;
+	enum rte_crypto_auth_algorithm algo;
+	uint16_t digest_len;
+	uint16_t key_len;
 };
 
-/* SAs Inbound */
-const struct ipsec_sa sa_in[] = {
+const struct supported_cipher_algo cipher_algos[] = {
 	{
-	.spi = 105,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
+		.keyword = "null",
+		.algo = RTE_CRYPTO_CIPHER_NULL,
+		.iv_len = 0,
+		.block_size = 4,
+		.key_len = 0
 	},
 	{
-	.spi = 106,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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
-	},
-	{
-	.spi = 115,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
-	},
-	{
-	.spi = 116,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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,
+		.key_len = 16
+	}
+};
+
+const struct supported_auth_algo auth_algos[] = {
 	{
-	.spi = 125,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
-	.dst.ip.ip6.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,
+		.key_len = 0
 	},
 	{
-	.spi = 126,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
-	.dst.ip.ip6.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,
+		.key_len = 20
+	}
 };
 
-static uint8_t cipher_key[256] = "sixteenbytes key";
+struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_out;
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform aescbc_enc_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_in;
+
+static const struct supported_cipher_algo *
+find_match_cipher_algo(const char *cipher_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-const struct rte_crypto_sym_xform aescbc_dec_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+	return NULL;
+}
+
+static const struct supported_auth_algo *
+find_match_auth_algo(const char *auth_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-static uint8_t auth_key[256] = "twentybytes hash key";
+	return NULL;
+}
+
+/** parse_key_string
+ *  parse x:x:x:x.... hex number key string into uint8_t *key
+ *  return:
+ *  > 0: number of bytes parsed
+ *  0:   failed
+ */
+static uint32_t
+parse_key_string(const char *key_str, uint8_t *key)
+{
+	const char *pt_start = key_str, *pt_end = key_str;
+	char sub_str[3];
+	uint32_t nb_bytes = 0;
+
+	while (pt_end != NULL) {
+		pt_end = strchr(pt_start, ':');
+
+		if (pt_end == NULL)
+			strncpy(sub_str, pt_start, strlen(pt_start));
+		else {
+			if (pt_end - pt_start > 2)
+				return 0;
+
+			strncpy(sub_str, pt_start, pt_end - pt_start);
+			pt_start = pt_end + 1;
+		}
 
-/* SHA1 HMAC xform */
-const struct rte_crypto_sym_xform sha1hmac_gen_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+		key[nb_bytes++] = strtol(sub_str, NULL, 16);
 	}
-};
 
-const struct rte_crypto_sym_xform sha1hmac_verify_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+	return nb_bytes;
+}
+
+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*/;
+	uint32_t cipher_algo_p = 0;
+	uint32_t auth_algo_p = 0;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+	uint32_t mode_p = 0;
+
+	if (strcmp(tokens[0], "in") == 0) {
+		ri = &nb_sa_in;
+
+		APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 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 <= IPSEC_SA_MAX_ENTRIES - 1, status,
+			"too many sa rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule = &sa_out[*ri];
 	}
-};
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform null_cipher_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { .algo = RTE_CRYPTO_CIPHER_NULL }
+	/* spi number */
+	APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
+	if (status->status < 0)
+		return;
+	rule->spi = atoi(tokens[1]);
+
+	for (ti = 2; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "mode") == 0) {
+			APP_CHECK_PRESENCE(mode_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (strcmp(tokens[ti], "ipv4-tunnel") == 0)
+				rule->flags = IP4_TUNNEL;
+			else if (strcmp(tokens[ti], "ipv6-tunnel") == 0)
+				rule->flags = IP6_TUNNEL;
+			else if (strcmp(tokens[ti], "transport") == 0)
+				rule->flags = TRANSPORT;
+			else {
+				APP_CHECK(0, status, "unrecognized "
+					"input \"%s\"", tokens[ti]);
+				return;
+			}
+
+			mode_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "cipher_algo") == 0) {
+			const struct supported_cipher_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_cipher_algo(tokens[ti]);
+
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->cipher_algo = algo->algo;
+			rule->block_size = algo->block_size;
+			rule->iv_len = algo->iv_len;
+			rule->cipher_key_len = algo->key_len;
+
+			/* for NULL algorithm, no cipher key should
+			 * exist */
+			if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
+				cipher_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"cipher_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->cipher_key);
+			APP_CHECK(key_len == rule->cipher_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			cipher_algo_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "auth_algo") == 0) {
+			const struct supported_auth_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(auth_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_auth_algo(tokens[ti]);
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->auth_algo = algo->algo;
+			rule->auth_key_len = algo->key_len;
+			rule->digest_len = algo->digest_len;
+
+			/* for NULL algorithm, no auth key should exist */
+			if (rule->auth_algo == RTE_CRYPTO_AUTH_NULL) {
+				auth_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"auth_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->auth_key);
+			APP_CHECK(key_len == rule->auth_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			auth_algo_p = 1;
+			continue;
+		}
+
+		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.ip.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.ip.ip6.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.ip.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.ip.ip6.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;
 	}
-};
 
-const struct rte_crypto_sym_xform null_auth_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { .algo = RTE_CRYPTO_AUTH_NULL }
+	APP_CHECK(cipher_algo_p == 1, status, "missing cipher options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(auth_algo_p == 1, status, "missing auth options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(mode_p == 1, status, "missing mode option");
+	if (status->status < 0)
+		return;
+
+	*ri = *ri + 1;
+}
+
+static inline void
+print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
+{
+	uint32_t i;
+	uint8_t a, b, c, d;
+
+	printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
+
+	for (i = 0; i < RTE_DIM(cipher_algos); i++) {
+		if (cipher_algos[i].algo == sa->cipher_algo) {
+			printf("%s ", cipher_algos[i].keyword);
+			break;
+		}
 	}
-};
+
+	for (i = 0; i < RTE_DIM(auth_algos); i++) {
+		if (auth_algos[i].algo == sa->auth_algo) {
+			printf("%s ", auth_algos[i].keyword);
+			break;
+		}
+	}
+
+	printf("mode:");
+
+	switch (sa->flags) {
+	case IP4_TUNNEL:
+		printf("IP4Tunnel ");
+		uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu ", d, c, b, a);
+		uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu", d, c, b, a);
+		break;
+	case IP6_TUNNEL:
+		printf("IP6Tunnel ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
+		}
+		printf(" ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
+		}
+		break;
+	case TRANSPORT:
+		printf("Transport");
+		break;
+	}
+	printf("\n");
+}
 
 struct sa_ctx {
 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
@@ -347,25 +561,53 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 		}
 
 		if (inbound) {
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_auth_xf;
-				sa_ctx->xf[idx].b = null_cipher_xf;
-			} else {
-				sa_ctx->xf[idx].a = sha1hmac_verify_xf;
-				sa_ctx->xf[idx].b = aescbc_dec_xf;
-			}
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].b.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].b.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			sa_ctx->xf[idx].b.next = NULL;
+
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].a.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].a.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].b.auth.digest_length =
+				sa->digest_len;
+			sa_ctx->xf[idx].a.auth.op =
+				RTE_CRYPTO_AUTH_OP_VERIFY;
+
 		} else { /* outbound */
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_cipher_xf;
-				sa_ctx->xf[idx].b = null_auth_xf;
-			} else {
-				sa_ctx->xf[idx].a = aescbc_enc_xf;
-				sa_ctx->xf[idx].b = sha1hmac_gen_xf;
-			}
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].a.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].a.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			sa_ctx->xf[idx].a.next = NULL;
+
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].b.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].b.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].b.auth.digest_length =
+				sa->digest_len;
+			sa_ctx->xf[idx].b.auth.op =
+				RTE_CRYPTO_AUTH_OP_GENERATE;
 		}
+
 		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
 		sa_ctx->xf[idx].b.next = NULL;
 		sa->xforms = &sa_ctx->xf[idx].a;
+
+		print_one_sa_rule(sa, inbound);
 	}
 
 	return 0;
@@ -386,10 +628,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 +643,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);
+	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);
 
-	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, nb_sa_in);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
 
-	sa_in_add_rules(ctx->sa_in, sa_in_entries, nb_in_entries);
+	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_entries, nb_out_entries);
+		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
+			"specified\n");
 }
 
 int
diff --git a/examples/ipsec-secgw/sp4.c b/examples/ipsec-secgw/sp4.c
index 9c4b256..38c72a9 100644
--- a/examples/ipsec-secgw/sp4.c
+++ b/examples/ipsec-secgw/sp4.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 /*
  * Rule and trace formats definitions.
@@ -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,19 @@ 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);
+	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);
 	} 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);
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip4_out";
+		ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
+			socket_id, acl4_rules_out, nb_acl4_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
+			"specified\n");
 }
diff --git a/examples/ipsec-secgw/sp6.c b/examples/ipsec-secgw/sp6.c
index 1dda11a..62fb492 100644
--- a/examples/ipsec-secgw/sp6.c
+++ b/examples/ipsec-secgw/sp6.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 enum {
 	IP6_PROTO,
@@ -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,19 @@ 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);
+	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);
 	} else
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip6_out";
+		ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name,
+			socket_id, acl6_rules_out, nb_acl6_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Outbound rule "
+			"specified\n");
 }
-- 
2.5.5

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

* [PATCH v6 2/2] examples/ipsec-secgw: add sample configuration files
  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-08-23 14:46           ` Fan Zhang
  1 sibling, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-08-23 14:46 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to set-up systems
back-to-back that would forward traffic through an IPsec tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 320 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..299aa9e
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa out 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa out 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa out 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa out 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa out 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa in 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa in 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa in 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..3f6ff81
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa in 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa in 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa in 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa in 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa in 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa out 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa out 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa out 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* Re: [PATCH v6 1/2] examples/ipsec-secgw: add configuration file support
  2016-08-23 14:46           ` [PATCH v6 1/2] examples/ipsec-secgw: " Fan Zhang
@ 2016-09-07 18:13             ` De Lara Guarch, Pablo
  0 siblings, 0 replies; 32+ messages in thread
From: De Lara Guarch, Pablo @ 2016-09-07 18:13 UTC (permalink / raw)
  To: Zhang, Roy Fan, dev; +Cc: Gonzalez Monroy, Sergio

Hi Fan,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Fan Zhang
> Sent: Tuesday, August 23, 2016 7:47 AM
> To: dev@dpdk.org
> Cc: Gonzalez Monroy, Sergio
> Subject: [dpdk-dev] [PATCH v6 1/2] examples/ipsec-secgw: add configuration
> file support
> 
> 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> <cipher_key> <auth_algo> <auth_key> \
> <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>

This probably deserves an update in release notes doc.

Thanks,
Pablo

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

* [PATCH v7 0/2] examples/ipsec_secgw: add configuration file support
  2016-08-22 10:49       ` [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
                           ` (2 preceding siblings ...)
  2016-08-23 14:46         ` [PATCH v6 0/2] [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-09-20 11:13         ` 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
  4 siblings, 2 replies; 32+ messages in thread
From: Fan Zhang @ 2016-09-20 11:13 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

v7 change:
- updated release note

v6 change:
- fix SA digest key always 0 error

v5 change:
- fix missed SA key length checking parsing error
- fix SA ip address printing

v4 change:
- rebase the patchset on top of 
  "examples/ipsec-secgw: fix GCC 4.5.x build error"
  (http://dpdk.org/dev/patchwork/patch/14895/)
- add cipher_key and auth_key configuration options to SA rules
- updated documentation for the new options

v3 change:
- fix 32-bit compilation error

v2 changes:
- fix configuration file parsing error.
- update doc to remove whitespace tailing errors.

*** BLURB HERE ***

Fan Zhang (2):
  examples/ipsec-secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/rel_notes/release_16_11.rst   |   4 +
 doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 160 ++++++
 examples/ipsec-secgw/ep1.cfg             | 160 ++++++
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 13 files changed, 2717 insertions(+), 1319 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH v7 1/2] examples/ipsec-secgw: add configuration file support
  2016-09-20 11:13         ` [PATCH v7 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-09-20 11:13           ` Fan Zhang
  2016-09-20 11:13           ` [PATCH v7 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
  1 sibling, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-09-20 11:13 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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> <cipher_key> <auth_algo> <auth_key> \
<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/rel_notes/release_16_11.rst   |   4 +
 doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 11 files changed, 2397 insertions(+), 1319 deletions(-)
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

diff --git a/doc/guides/rel_notes/release_16_11.rst b/doc/guides/rel_notes/release_16_11.rst
index 451872e..da61798 100644
--- a/doc/guides/rel_notes/release_16_11.rst
+++ b/doc/guides/rel_notes/release_16_11.rst
@@ -82,6 +82,10 @@ Libraries
 Examples
 ~~~~~~~~
 
+* **ipsec-secgw: add configuration file support**
+
+  ipsec-secgw sample application now supports configuration file to specify
+  SP, SA, and routing rules.
 
 Other
 ~~~~~
diff --git a/doc/guides/sample_app_ug/ipsec_secgw.rst b/doc/guides/sample_app_ug/ipsec_secgw.rst
index fcb33c2..5cce2fe 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,357 @@ 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
+
+ * Syntax: *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> <cipher_key> <auth_algo> <auth_key>
+    <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
+
+ * Syntax: *cipher_algo <your algorithm>*
+
+``<cipher_key>``
+
+ * Cipher key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <cipher_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified cipher algorithm
+   key size.
+
+   For example: *cipher_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<auth_algo>``
+
+ * Authentication algorithm
+
+ * Optional: No
+
+ * Available options:
+
+    * *null*: NULL algorithm
+    * *sha1-hmac*: HMAC SHA1 algorithm
+
+``<auth_key>``
+
+ * Authentication key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <auth_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified authentication
+   algorithm key size.
+
+   For example: *auth_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<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
+
+ * Syntax: mode XXX
+
+``<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 out 5 cipher_algo null auth_algo null mode ipv4-tunnel \
+    src 172.16.1.5 dst 172.16.2.5
+
+    sa out 25 cipher_algo aes-128-cbc \
+    cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    auth_algo sha1-hmac \
+    auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    mode ipv6-tunnel \
+    src 1111:1111:1111:1111:1111:1111:1111:5555 \
+    dst 2222:2222:2222:2222:2222:2222:2222: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 5d04eb3..8b55534 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;
@@ -838,7 +836,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"
@@ -846,8 +844,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
@@ -960,18 +958,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
@@ -986,14 +972,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) {
@@ -1017,6 +1002,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);
@@ -1029,6 +1029,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;
 
@@ -1411,9 +1416,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",
@@ -1443,13 +1445,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 a442a74..4cc316c 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -90,6 +90,8 @@ struct ip_addr {
 	} ip;
 };
 
+#define MAX_KEY_SIZE		20
+
 struct ipsec_sa {
 	uint32_t spi;
 	uint32_t cdev_id_qp;
@@ -106,6 +108,10 @@ struct ipsec_sa {
 #define TRANSPORT  (1 << 2)
 	struct ip_addr src;
 	struct ip_addr dst;
+	uint8_t cipher_key[MAX_KEY_SIZE];
+	uint16_t cipher_key_len;
+	uint8_t auth_key[MAX_KEY_SIZE];
+	uint16_t auth_key_len;
 	struct rte_crypto_sym_xform *xforms;
 } __rte_cache_aligned;
 
@@ -183,15 +189,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..99bdfc5
--- /dev/null
+++ b/examples/ipsec-secgw/parser.c
@@ -0,0 +1,599 @@
+/*-
+ *   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)
+		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 4439e0f..54b5b19 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -48,243 +48,457 @@
 
 #include "ipsec.h"
 #include "esp.h"
+#include "parser.h"
+
+struct supported_cipher_algo {
+	const char *keyword;
+	enum rte_crypto_cipher_algorithm algo;
+	uint16_t iv_len;
+	uint16_t block_size;
+	uint16_t key_len;
+};
 
-/* SAs Outbound */
-const struct ipsec_sa sa_out[] = {
-	{
-	.spi = 5,
-	.src.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
-	.dst.ip.ip6.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
-	.dst.ip.ip6.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_auth_algo {
+	const char *keyword;
+	enum rte_crypto_auth_algorithm algo;
+	uint16_t digest_len;
+	uint16_t key_len;
 };
 
-/* SAs Inbound */
-const struct ipsec_sa sa_in[] = {
+const struct supported_cipher_algo cipher_algos[] = {
 	{
-	.spi = 105,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
+		.keyword = "null",
+		.algo = RTE_CRYPTO_CIPHER_NULL,
+		.iv_len = 0,
+		.block_size = 4,
+		.key_len = 0
 	},
 	{
-	.spi = 106,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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
-	},
-	{
-	.spi = 115,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
-	},
-	{
-	.spi = 116,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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,
+		.key_len = 16
+	}
+};
+
+const struct supported_auth_algo auth_algos[] = {
 	{
-	.spi = 125,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
-	.dst.ip.ip6.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,
+		.key_len = 0
 	},
 	{
-	.spi = 126,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
-	.dst.ip.ip6.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,
+		.key_len = 20
+	}
 };
 
-static uint8_t cipher_key[256] = "sixteenbytes key";
+struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_out;
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform aescbc_enc_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_in;
+
+static const struct supported_cipher_algo *
+find_match_cipher_algo(const char *cipher_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-const struct rte_crypto_sym_xform aescbc_dec_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+	return NULL;
+}
+
+static const struct supported_auth_algo *
+find_match_auth_algo(const char *auth_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-static uint8_t auth_key[256] = "twentybytes hash key";
+	return NULL;
+}
+
+/** parse_key_string
+ *  parse x:x:x:x.... hex number key string into uint8_t *key
+ *  return:
+ *  > 0: number of bytes parsed
+ *  0:   failed
+ */
+static uint32_t
+parse_key_string(const char *key_str, uint8_t *key)
+{
+	const char *pt_start = key_str, *pt_end = key_str;
+	char sub_str[3];
+	uint32_t nb_bytes = 0;
+
+	while (pt_end != NULL) {
+		pt_end = strchr(pt_start, ':');
+
+		if (pt_end == NULL)
+			strncpy(sub_str, pt_start, strlen(pt_start));
+		else {
+			if (pt_end - pt_start > 2)
+				return 0;
+
+			strncpy(sub_str, pt_start, pt_end - pt_start);
+			pt_start = pt_end + 1;
+		}
 
-/* SHA1 HMAC xform */
-const struct rte_crypto_sym_xform sha1hmac_gen_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+		key[nb_bytes++] = strtol(sub_str, NULL, 16);
 	}
-};
 
-const struct rte_crypto_sym_xform sha1hmac_verify_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+	return nb_bytes;
+}
+
+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*/;
+	uint32_t cipher_algo_p = 0;
+	uint32_t auth_algo_p = 0;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+	uint32_t mode_p = 0;
+
+	if (strcmp(tokens[0], "in") == 0) {
+		ri = &nb_sa_in;
+
+		APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 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 <= IPSEC_SA_MAX_ENTRIES - 1, status,
+			"too many sa rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule = &sa_out[*ri];
 	}
-};
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform null_cipher_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { .algo = RTE_CRYPTO_CIPHER_NULL }
+	/* spi number */
+	APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
+	if (status->status < 0)
+		return;
+	rule->spi = atoi(tokens[1]);
+
+	for (ti = 2; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "mode") == 0) {
+			APP_CHECK_PRESENCE(mode_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (strcmp(tokens[ti], "ipv4-tunnel") == 0)
+				rule->flags = IP4_TUNNEL;
+			else if (strcmp(tokens[ti], "ipv6-tunnel") == 0)
+				rule->flags = IP6_TUNNEL;
+			else if (strcmp(tokens[ti], "transport") == 0)
+				rule->flags = TRANSPORT;
+			else {
+				APP_CHECK(0, status, "unrecognized "
+					"input \"%s\"", tokens[ti]);
+				return;
+			}
+
+			mode_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "cipher_algo") == 0) {
+			const struct supported_cipher_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_cipher_algo(tokens[ti]);
+
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->cipher_algo = algo->algo;
+			rule->block_size = algo->block_size;
+			rule->iv_len = algo->iv_len;
+			rule->cipher_key_len = algo->key_len;
+
+			/* for NULL algorithm, no cipher key should
+			 * exist */
+			if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
+				cipher_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"cipher_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->cipher_key);
+			APP_CHECK(key_len == rule->cipher_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			cipher_algo_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "auth_algo") == 0) {
+			const struct supported_auth_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(auth_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_auth_algo(tokens[ti]);
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->auth_algo = algo->algo;
+			rule->auth_key_len = algo->key_len;
+			rule->digest_len = algo->digest_len;
+
+			/* for NULL algorithm, no auth key should exist */
+			if (rule->auth_algo == RTE_CRYPTO_AUTH_NULL) {
+				auth_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"auth_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->auth_key);
+			APP_CHECK(key_len == rule->auth_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			auth_algo_p = 1;
+			continue;
+		}
+
+		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.ip.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.ip.ip6.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.ip.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.ip.ip6.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;
 	}
-};
 
-const struct rte_crypto_sym_xform null_auth_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { .algo = RTE_CRYPTO_AUTH_NULL }
+	APP_CHECK(cipher_algo_p == 1, status, "missing cipher options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(auth_algo_p == 1, status, "missing auth options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(mode_p == 1, status, "missing mode option");
+	if (status->status < 0)
+		return;
+
+	*ri = *ri + 1;
+}
+
+static inline void
+print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
+{
+	uint32_t i;
+	uint8_t a, b, c, d;
+
+	printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
+
+	for (i = 0; i < RTE_DIM(cipher_algos); i++) {
+		if (cipher_algos[i].algo == sa->cipher_algo) {
+			printf("%s ", cipher_algos[i].keyword);
+			break;
+		}
 	}
-};
+
+	for (i = 0; i < RTE_DIM(auth_algos); i++) {
+		if (auth_algos[i].algo == sa->auth_algo) {
+			printf("%s ", auth_algos[i].keyword);
+			break;
+		}
+	}
+
+	printf("mode:");
+
+	switch (sa->flags) {
+	case IP4_TUNNEL:
+		printf("IP4Tunnel ");
+		uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu ", d, c, b, a);
+		uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu", d, c, b, a);
+		break;
+	case IP6_TUNNEL:
+		printf("IP6Tunnel ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
+		}
+		printf(" ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
+		}
+		break;
+	case TRANSPORT:
+		printf("Transport");
+		break;
+	}
+	printf("\n");
+}
 
 struct sa_ctx {
 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
@@ -347,25 +561,53 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 		}
 
 		if (inbound) {
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_auth_xf;
-				sa_ctx->xf[idx].b = null_cipher_xf;
-			} else {
-				sa_ctx->xf[idx].a = sha1hmac_verify_xf;
-				sa_ctx->xf[idx].b = aescbc_dec_xf;
-			}
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].b.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].b.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			sa_ctx->xf[idx].b.next = NULL;
+
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].a.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].a.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].b.auth.digest_length =
+				sa->digest_len;
+			sa_ctx->xf[idx].a.auth.op =
+				RTE_CRYPTO_AUTH_OP_VERIFY;
+
 		} else { /* outbound */
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_cipher_xf;
-				sa_ctx->xf[idx].b = null_auth_xf;
-			} else {
-				sa_ctx->xf[idx].a = aescbc_enc_xf;
-				sa_ctx->xf[idx].b = sha1hmac_gen_xf;
-			}
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].a.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].a.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			sa_ctx->xf[idx].a.next = NULL;
+
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].b.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].b.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].b.auth.digest_length =
+				sa->digest_len;
+			sa_ctx->xf[idx].b.auth.op =
+				RTE_CRYPTO_AUTH_OP_GENERATE;
 		}
+
 		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
 		sa_ctx->xf[idx].b.next = NULL;
 		sa->xforms = &sa_ctx->xf[idx].a;
+
+		print_one_sa_rule(sa, inbound);
 	}
 
 	return 0;
@@ -386,10 +628,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 +643,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);
+	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);
 
-	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, nb_sa_in);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
 
-	sa_in_add_rules(ctx->sa_in, sa_in_entries, nb_in_entries);
+	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_entries, nb_out_entries);
+		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
+			"specified\n");
 }
 
 int
diff --git a/examples/ipsec-secgw/sp4.c b/examples/ipsec-secgw/sp4.c
index 9c4b256..38c72a9 100644
--- a/examples/ipsec-secgw/sp4.c
+++ b/examples/ipsec-secgw/sp4.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 /*
  * Rule and trace formats definitions.
@@ -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,19 @@ 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);
+	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);
 	} 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);
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip4_out";
+		ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
+			socket_id, acl4_rules_out, nb_acl4_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
+			"specified\n");
 }
diff --git a/examples/ipsec-secgw/sp6.c b/examples/ipsec-secgw/sp6.c
index 1dda11a..62fb492 100644
--- a/examples/ipsec-secgw/sp6.c
+++ b/examples/ipsec-secgw/sp6.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 enum {
 	IP6_PROTO,
@@ -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,19 @@ 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);
+	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);
 	} else
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip6_out";
+		ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name,
+			socket_id, acl6_rules_out, nb_acl6_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Outbound rule "
+			"specified\n");
 }
-- 
2.5.5

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

* [PATCH v7 2/2] examples/ipsec-secgw: add sample configuration files
  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           ` Fan Zhang
  1 sibling, 0 replies; 32+ messages in thread
From: Fan Zhang @ 2016-09-20 11:13 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to set-up systems
back-to-back that would forward traffic through an IPsec tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 320 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..299aa9e
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa out 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa out 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa out 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa out 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa out 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa in 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa in 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa in 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..3f6ff81
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa in 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa in 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa in 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa in 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa in 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa out 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa out 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa out 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* [PATCH v8 0/2] examples/ipsec_secgw: add configuration file support
  2016-08-22 10:49       ` [PATCH 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
                           ` (3 preceding siblings ...)
  2016-09-20 11:13         ` [PATCH v7 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-09-21 12:05         ` Fan Zhang
  2016-09-21 12:05           ` [PATCH v8 1/2] examples/ipsec-secgw: " Fan Zhang
  2016-09-21 12:05           ` [PATCH v8 2/2] examples/ipsec-secgw: add sample configuration files Fan Zhang
  4 siblings, 2 replies; 32+ messages in thread
From: Fan Zhang @ 2016-09-21 12:05 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patchset adds the configuration file supported to ipsec_secgw
sample application. Two sample configuration files, ep0.cfg and ep1.cfg
are also added to show how to configure two systems back-to-back that 
would forward traffic through an IPsec tunnel

v8 change:
- fix wrong inbound SA rule parsing

v7 change:
- updated release note

v6 change:
- fix SA digest length always 0 error

v5 change:
- fix missed SA key length checking parsing error
- fix SA ip address printing

v4 change:
- rebase the patchset on top of 
  "examples/ipsec-secgw: fix GCC 4.5.x build error"
  (http://dpdk.org/dev/patchwork/patch/14895/)
- add cipher_key and auth_key configuration options to SA rules
- updated documentation for the new options

v3 change:
- fix 32-bit compilation error

v2 changes:
- fix configuration file parsing error.
- update doc to remove whitespace tailing errors.

Fan Zhang (2):
  examples/ipsec-secgw: add configuration file support
  examples/ipsec-secgw: add sample configuration files

 doc/guides/rel_notes/release_16_11.rst   |   4 +
 doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ep0.cfg             | 160 ++++++
 examples/ipsec-secgw/ep1.cfg             | 160 ++++++
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 13 files changed, 2717 insertions(+), 1319 deletions(-)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

-- 
2.5.5

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

* [PATCH v8 1/2] examples/ipsec-secgw: add configuration file support
  2016-09-21 12:05         ` [PATCH v8 0/2] examples/ipsec_secgw: add configuration file support Fan Zhang
@ 2016-09-21 12:05           ` Fan Zhang
  2016-09-23  7:52             ` Sergio Gonzalez Monroy
  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
  1 sibling, 2 replies; 32+ messages in thread
From: Fan Zhang @ 2016-09-21 12:05 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

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> <cipher_key> <auth_algo> <auth_key> \
<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/rel_notes/release_16_11.rst   |   4 +
 doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
 examples/ipsec-secgw/Makefile            |   1 +
 examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
 examples/ipsec-secgw/ipsec.h             |  14 +-
 examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
 examples/ipsec-secgw/parser.h            | 116 +++++
 examples/ipsec-secgw/rt.c                | 255 ++++------
 examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
 examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
 examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
 11 files changed, 2397 insertions(+), 1319 deletions(-)
 create mode 100644 examples/ipsec-secgw/parser.c
 create mode 100644 examples/ipsec-secgw/parser.h

diff --git a/doc/guides/rel_notes/release_16_11.rst b/doc/guides/rel_notes/release_16_11.rst
index 451872e..da61798 100644
--- a/doc/guides/rel_notes/release_16_11.rst
+++ b/doc/guides/rel_notes/release_16_11.rst
@@ -82,6 +82,10 @@ Libraries
 Examples
 ~~~~~~~~
 
+* **ipsec-secgw: add configuration file support**
+
+  ipsec-secgw sample application now supports configuration file to specify
+  SP, SA, and routing rules.
 
 Other
 ~~~~~
diff --git a/doc/guides/sample_app_ug/ipsec_secgw.rst b/doc/guides/sample_app_ug/ipsec_secgw.rst
index fcb33c2..5cce2fe 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,357 @@ 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
+
+ * Syntax: *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> <cipher_key> <auth_algo> <auth_key>
+    <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
+
+ * Syntax: *cipher_algo <your algorithm>*
+
+``<cipher_key>``
+
+ * Cipher key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <cipher_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified cipher algorithm
+   key size.
+
+   For example: *cipher_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<auth_algo>``
+
+ * Authentication algorithm
+
+ * Optional: No
+
+ * Available options:
+
+    * *null*: NULL algorithm
+    * *sha1-hmac*: HMAC SHA1 algorithm
+
+``<auth_key>``
+
+ * Authentication key, NOT available when 'null' algorithm is used
+
+ * Optional: No, must followed by <auth_algo> option
+
+ * Syntax: Hexadecimal bytes (0x0-0xFF) concatenate by colon symbol ':'.
+   The number of bytes should be as same as the specified authentication
+   algorithm key size.
+
+   For example: *auth_key A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:A1:B2:C3:D4:
+   A1:B2:C3:D4*
+
+``<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
+
+ * Syntax: mode XXX
+
+``<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 out 5 cipher_algo null auth_algo null mode ipv4-tunnel \
+    src 172.16.1.5 dst 172.16.2.5
+
+    sa out 25 cipher_algo aes-128-cbc \
+    cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    auth_algo sha1-hmac \
+    auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3 \
+    mode ipv6-tunnel \
+    src 1111:1111:1111:1111:1111:1111:1111:5555 \
+    dst 2222:2222:2222:2222:2222:2222:2222: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 5d04eb3..8b55534 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;
@@ -838,7 +836,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"
@@ -846,8 +844,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
@@ -960,18 +958,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
@@ -986,14 +972,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) {
@@ -1017,6 +1002,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);
@@ -1029,6 +1029,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;
 
@@ -1411,9 +1416,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",
@@ -1443,13 +1445,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 a442a74..4cc316c 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -90,6 +90,8 @@ struct ip_addr {
 	} ip;
 };
 
+#define MAX_KEY_SIZE		20
+
 struct ipsec_sa {
 	uint32_t spi;
 	uint32_t cdev_id_qp;
@@ -106,6 +108,10 @@ struct ipsec_sa {
 #define TRANSPORT  (1 << 2)
 	struct ip_addr src;
 	struct ip_addr dst;
+	uint8_t cipher_key[MAX_KEY_SIZE];
+	uint16_t cipher_key_len;
+	uint8_t auth_key[MAX_KEY_SIZE];
+	uint16_t auth_key_len;
 	struct rte_crypto_sym_xform *xforms;
 } __rte_cache_aligned;
 
@@ -183,15 +189,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..99bdfc5
--- /dev/null
+++ b/examples/ipsec-secgw/parser.c
@@ -0,0 +1,599 @@
+/*-
+ *   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)
+		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 4439e0f..ee88802 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -48,243 +48,457 @@
 
 #include "ipsec.h"
 #include "esp.h"
+#include "parser.h"
+
+struct supported_cipher_algo {
+	const char *keyword;
+	enum rte_crypto_cipher_algorithm algo;
+	uint16_t iv_len;
+	uint16_t block_size;
+	uint16_t key_len;
+};
 
-/* SAs Outbound */
-const struct ipsec_sa sa_out[] = {
-	{
-	.spi = 5,
-	.src.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 5),
-	.dst.ip.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.ip.ip4 = IPv4(172, 16, 1, 6),
-	.dst.ip.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
-	.dst.ip.ip6.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.ip.ip6.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
-	.dst.ip.ip6.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_auth_algo {
+	const char *keyword;
+	enum rte_crypto_auth_algorithm algo;
+	uint16_t digest_len;
+	uint16_t key_len;
 };
 
-/* SAs Inbound */
-const struct ipsec_sa sa_in[] = {
+const struct supported_cipher_algo cipher_algos[] = {
 	{
-	.spi = 105,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
+		.keyword = "null",
+		.algo = RTE_CRYPTO_CIPHER_NULL,
+		.iv_len = 0,
+		.block_size = 4,
+		.key_len = 0
 	},
 	{
-	.spi = 106,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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
-	},
-	{
-	.spi = 115,
-	.src.ip.ip4 = IPv4(172, 16, 2, 5),
-	.dst.ip.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
-	},
-	{
-	.spi = 116,
-	.src.ip.ip4 = IPv4(172, 16, 2, 6),
-	.dst.ip.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,
+		.key_len = 16
+	}
+};
+
+const struct supported_auth_algo auth_algos[] = {
 	{
-	.spi = 125,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
-	.dst.ip.ip6.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,
+		.key_len = 0
 	},
 	{
-	.spi = 126,
-	.src.ip.ip6.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
-		0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
-	.dst.ip.ip6.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,
+		.key_len = 20
+	}
 };
 
-static uint8_t cipher_key[256] = "sixteenbytes key";
+struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_out;
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform aescbc_enc_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES];
+uint32_t nb_sa_in;
+
+static const struct supported_cipher_algo *
+find_match_cipher_algo(const char *cipher_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-const struct rte_crypto_sym_xform aescbc_dec_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
-		.key = { cipher_key, 16 } }
+	return NULL;
+}
+
+static const struct supported_auth_algo *
+find_match_auth_algo(const char *auth_keyword)
+{
+	size_t i;
+
+	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)
+			return algo;
 	}
-};
 
-static uint8_t auth_key[256] = "twentybytes hash key";
+	return NULL;
+}
+
+/** parse_key_string
+ *  parse x:x:x:x.... hex number key string into uint8_t *key
+ *  return:
+ *  > 0: number of bytes parsed
+ *  0:   failed
+ */
+static uint32_t
+parse_key_string(const char *key_str, uint8_t *key)
+{
+	const char *pt_start = key_str, *pt_end = key_str;
+	char sub_str[3];
+	uint32_t nb_bytes = 0;
+
+	while (pt_end != NULL) {
+		pt_end = strchr(pt_start, ':');
+
+		if (pt_end == NULL)
+			strncpy(sub_str, pt_start, strlen(pt_start));
+		else {
+			if (pt_end - pt_start > 2)
+				return 0;
+
+			strncpy(sub_str, pt_start, pt_end - pt_start);
+			pt_start = pt_end + 1;
+		}
 
-/* SHA1 HMAC xform */
-const struct rte_crypto_sym_xform sha1hmac_gen_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+		key[nb_bytes++] = strtol(sub_str, NULL, 16);
 	}
-};
 
-const struct rte_crypto_sym_xform sha1hmac_verify_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
-		.key = { auth_key, 20 }, 12, 0 }
+	return nb_bytes;
+}
+
+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*/;
+	uint32_t cipher_algo_p = 0;
+	uint32_t auth_algo_p = 0;
+	uint32_t src_p = 0;
+	uint32_t dst_p = 0;
+	uint32_t mode_p = 0;
+
+	if (strcmp(tokens[0], "in") == 0) {
+		ri = &nb_sa_in;
+
+		APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 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 <= IPSEC_SA_MAX_ENTRIES - 1, status,
+			"too many sa rules, abort insertion\n");
+		if (status->status < 0)
+			return;
+
+		rule = &sa_out[*ri];
 	}
-};
 
-/* AES CBC xform */
-const struct rte_crypto_sym_xform null_cipher_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_CIPHER,
-	{.cipher = { .algo = RTE_CRYPTO_CIPHER_NULL }
+	/* spi number */
+	APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
+	if (status->status < 0)
+		return;
+	rule->spi = atoi(tokens[1]);
+
+	for (ti = 2; ti < n_tokens; ti++) {
+		if (strcmp(tokens[ti], "mode") == 0) {
+			APP_CHECK_PRESENCE(mode_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			if (strcmp(tokens[ti], "ipv4-tunnel") == 0)
+				rule->flags = IP4_TUNNEL;
+			else if (strcmp(tokens[ti], "ipv6-tunnel") == 0)
+				rule->flags = IP6_TUNNEL;
+			else if (strcmp(tokens[ti], "transport") == 0)
+				rule->flags = TRANSPORT;
+			else {
+				APP_CHECK(0, status, "unrecognized "
+					"input \"%s\"", tokens[ti]);
+				return;
+			}
+
+			mode_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "cipher_algo") == 0) {
+			const struct supported_cipher_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_cipher_algo(tokens[ti]);
+
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->cipher_algo = algo->algo;
+			rule->block_size = algo->block_size;
+			rule->iv_len = algo->iv_len;
+			rule->cipher_key_len = algo->key_len;
+
+			/* for NULL algorithm, no cipher key should
+			 * exist */
+			if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
+				cipher_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"cipher_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->cipher_key);
+			APP_CHECK(key_len == rule->cipher_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			cipher_algo_p = 1;
+			continue;
+		}
+
+		if (strcmp(tokens[ti], "auth_algo") == 0) {
+			const struct supported_auth_algo *algo;
+			uint32_t key_len;
+
+			APP_CHECK_PRESENCE(auth_algo_p, tokens[ti],
+				status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			algo = find_match_auth_algo(tokens[ti]);
+			APP_CHECK(algo != NULL, status, "unrecognized "
+				"input \"%s\"", tokens[ti]);
+
+			rule->auth_algo = algo->algo;
+			rule->auth_key_len = algo->key_len;
+			rule->digest_len = algo->digest_len;
+
+			/* for NULL algorithm, no auth key should exist */
+			if (rule->auth_algo == RTE_CRYPTO_AUTH_NULL) {
+				auth_algo_p = 1;
+				continue;
+			}
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
+				status, "unrecognized input \"%s\", "
+				"expect \"auth_key\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			key_len = parse_key_string(tokens[ti],
+				rule->auth_key);
+			APP_CHECK(key_len == rule->auth_key_len, status,
+				"unrecognized input \"%s\"", tokens[ti]);
+			if (status->status < 0)
+				return;
+
+			auth_algo_p = 1;
+			continue;
+		}
+
+		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.ip.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.ip.ip6.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.ip.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.ip.ip6.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;
 	}
-};
 
-const struct rte_crypto_sym_xform null_auth_xf = {
-	NULL,
-	RTE_CRYPTO_SYM_XFORM_AUTH,
-	{.auth = { .algo = RTE_CRYPTO_AUTH_NULL }
+	APP_CHECK(cipher_algo_p == 1, status, "missing cipher options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(auth_algo_p == 1, status, "missing auth options");
+	if (status->status < 0)
+		return;
+
+	APP_CHECK(mode_p == 1, status, "missing mode option");
+	if (status->status < 0)
+		return;
+
+	*ri = *ri + 1;
+}
+
+static inline void
+print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
+{
+	uint32_t i;
+	uint8_t a, b, c, d;
+
+	printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
+
+	for (i = 0; i < RTE_DIM(cipher_algos); i++) {
+		if (cipher_algos[i].algo == sa->cipher_algo) {
+			printf("%s ", cipher_algos[i].keyword);
+			break;
+		}
 	}
-};
+
+	for (i = 0; i < RTE_DIM(auth_algos); i++) {
+		if (auth_algos[i].algo == sa->auth_algo) {
+			printf("%s ", auth_algos[i].keyword);
+			break;
+		}
+	}
+
+	printf("mode:");
+
+	switch (sa->flags) {
+	case IP4_TUNNEL:
+		printf("IP4Tunnel ");
+		uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu ", d, c, b, a);
+		uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
+		printf("%hhu.%hhu.%hhu.%hhu", d, c, b, a);
+		break;
+	case IP6_TUNNEL:
+		printf("IP6Tunnel ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
+		}
+		printf(" ");
+		for (i = 0; i < 16; i++) {
+			if (i % 2 && i != 15)
+				printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
+			else
+				printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
+		}
+		break;
+	case TRANSPORT:
+		printf("Transport");
+		break;
+	}
+	printf("\n");
+}
 
 struct sa_ctx {
 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
@@ -347,25 +561,53 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 		}
 
 		if (inbound) {
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_auth_xf;
-				sa_ctx->xf[idx].b = null_cipher_xf;
-			} else {
-				sa_ctx->xf[idx].a = sha1hmac_verify_xf;
-				sa_ctx->xf[idx].b = aescbc_dec_xf;
-			}
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].b.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].b.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			sa_ctx->xf[idx].b.next = NULL;
+
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].a.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].a.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].a.auth.digest_length =
+				sa->digest_len;
+			sa_ctx->xf[idx].a.auth.op =
+				RTE_CRYPTO_AUTH_OP_VERIFY;
+
 		} else { /* outbound */
-			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
-				sa_ctx->xf[idx].a = null_cipher_xf;
-				sa_ctx->xf[idx].b = null_auth_xf;
-			} else {
-				sa_ctx->xf[idx].a = aescbc_enc_xf;
-				sa_ctx->xf[idx].b = sha1hmac_gen_xf;
-			}
+			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+			sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
+			sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
+			sa_ctx->xf[idx].a.cipher.key.length =
+				sa->cipher_key_len;
+			sa_ctx->xf[idx].a.cipher.op =
+				RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			sa_ctx->xf[idx].a.next = NULL;
+
+			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+			sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
+			sa_ctx->xf[idx].b.auth.add_auth_data_length = 0;
+			sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
+			sa_ctx->xf[idx].b.auth.key.length =
+				sa->auth_key_len;
+			sa_ctx->xf[idx].b.auth.digest_length =
+				sa->digest_len;
+			sa_ctx->xf[idx].b.auth.op =
+				RTE_CRYPTO_AUTH_OP_GENERATE;
 		}
+
 		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
 		sa_ctx->xf[idx].b.next = NULL;
 		sa->xforms = &sa_ctx->xf[idx].a;
+
+		print_one_sa_rule(sa, inbound);
 	}
 
 	return 0;
@@ -386,10 +628,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 +643,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);
+	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);
 
-	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, nb_sa_in);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
 
-	sa_in_add_rules(ctx->sa_in, sa_in_entries, nb_in_entries);
+	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_entries, nb_out_entries);
+		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
+			"specified\n");
 }
 
 int
diff --git a/examples/ipsec-secgw/sp4.c b/examples/ipsec-secgw/sp4.c
index 9c4b256..38c72a9 100644
--- a/examples/ipsec-secgw/sp4.c
+++ b/examples/ipsec-secgw/sp4.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 /*
  * Rule and trace formats definitions.
@@ -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,19 @@ 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);
+	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);
 	} 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);
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip4_out";
+		ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
+			socket_id, acl4_rules_out, nb_acl4_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
+			"specified\n");
 }
diff --git a/examples/ipsec-secgw/sp6.c b/examples/ipsec-secgw/sp6.c
index 1dda11a..62fb492 100644
--- a/examples/ipsec-secgw/sp6.c
+++ b/examples/ipsec-secgw/sp6.c
@@ -42,8 +42,9 @@
 #include <rte_ip.h>
 
 #include "ipsec.h"
+#include "parser.h"
 
-#define MAX_ACL_RULE_NUM	1000
+#define MAX_ACL_RULE_NUM	1024
 
 enum {
 	IP6_PROTO,
@@ -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,19 @@ 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);
+	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);
 	} else
-		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
-				"Only 0 or 1 supported.\n", ep);
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Inbound rule "
+			"specified\n");
 
-	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) {
+		name = "sp_ip6_out";
+		ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name,
+			socket_id, acl6_rules_out, nb_acl6_rules_out);
+	} else
+		RTE_LOG(WARNING, IPSEC, "No IPv6 SP Outbound rule "
+			"specified\n");
 }
-- 
2.5.5

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

* [PATCH v8 2/2] examples/ipsec-secgw: add sample configuration files
  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-21 12:05           ` Fan Zhang
  2016-09-23  7:53             ` Sergio Gonzalez Monroy
  1 sibling, 1 reply; 32+ messages in thread
From: Fan Zhang @ 2016-09-21 12:05 UTC (permalink / raw)
  To: dev; +Cc: sergio.gonzalez.monroy

This patch adds two sample configuration files to ipsec-secgw sample
application. The sample configuration files shows how to setup
back-to-back systems that would forward traffic through an IPsec
tunnel.

Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
---
 examples/ipsec-secgw/ep0.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 examples/ipsec-secgw/ep1.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 320 insertions(+)
 create mode 100644 examples/ipsec-secgw/ep0.cfg
 create mode 100644 examples/ipsec-secgw/ep1.cfg

diff --git a/examples/ipsec-secgw/ep0.cfg b/examples/ipsec-secgw/ep0.cfg
new file mode 100644
index 0000000..299aa9e
--- /dev/null
+++ b/examples/ipsec-secgw/ep0.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint 0 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep0.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 out esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 in esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 out esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 in esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa out 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa out 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa out 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa out 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa out 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa out 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa in 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa in 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa in 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa in 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa in 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.2.5/32 port 0
+rt ipv4 dst 172.16.2.6/32 port 1
+rt ipv4 dst 192.168.175.0/24 port 0
+rt ipv4 dst 192.168.176.0/24 port 1
+rt ipv4 dst 192.168.240.0/24 port 0
+rt ipv4 dst 192.168.241.0/24 port 1
+rt ipv4 dst 192.168.115.0/24 port 2
+rt ipv4 dst 192.168.116.0/24 port 3
+rt ipv4 dst 192.168.65.0/24 port 2
+rt ipv4 dst 192.168.66.0/24 port 3
+rt ipv4 dst 192.168.185.0/24 port 2
+rt ipv4 dst 192.168.186.0/24 port 3
+rt ipv4 dst 192.168.210.0/24 port 2
+rt ipv4 dst 192.168.211.0/24 port 3
+rt ipv4 dst 192.168.245.0/24 port 2
+rt ipv4 dst 192.168.246.0/24 port 3
+
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:5555/116 port 0
+rt ipv6 dst 2222:2222:2222:2222:2222:2222:2222:6666/116 port 1
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 3
diff --git a/examples/ipsec-secgw/ep1.cfg b/examples/ipsec-secgw/ep1.cfg
new file mode 100644
index 0000000..3f6ff81
--- /dev/null
+++ b/examples/ipsec-secgw/ep1.cfg
@@ -0,0 +1,160 @@
+###########################################################################
+#   IPSEC-SECGW Endpoint1 sample configuration
+#
+#   The main purpose of this file is to show how to configure two systems
+#   back-to-back that would forward traffic through an IPsec tunnel. This
+#   file is the Endpoint1 configuration. To use this configuration file,
+#   add the following command-line option:
+#
+#       -f ./ep1.cfg
+#
+###########################################################################
+
+#SP IPv4 rules
+sp ipv4 in esp protect 5 pri 1 dst 192.168.105.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 6 pri 1 dst 192.168.106.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 10 pri 1 dst 192.168.175.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 11 pri 1 dst 192.168.176.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 15 pri 1 dst 192.168.200.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 16 pri 1 dst 192.168.201.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 25 pri 1 dst 192.168.55.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp protect 26 pri 1 dst 192.168.56.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.240.0/24 sport 0:65535 dport 0:65535
+sp ipv4 in esp bypass dst 192.168.241.0/24 sport 0:65535 dport 0:65535
+
+sp ipv4 out esp protect 105 pri 1 dst 192.168.115.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 106 pri 1 dst 192.168.116.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 110 pri 1 dst 192.168.185.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 111 pri 1 dst 192.168.186.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 116 pri 1 dst 192.168.211.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 115 pri 1 dst 192.168.210.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 125 pri 1 dst 192.168.65.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp protect 126 pri 1 dst 192.168.66.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.245.0/24 sport 0:65535 dport 0:65535
+sp ipv4 out esp bypass pri 1 dst 192.168.246.0/24 sport 0:65535 dport 0:65535
+
+#SP IPv6 rules
+sp ipv6 in esp protect 5 pri 1 dst 0000:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 6 pri 1 dst 0000:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 10 pri 1 dst 0000:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 11 pri 1 dst 0000:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 25 pri 1 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 in esp protect 26 pri 1 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+sp ipv6 out esp protect 15 pri 1 dst ffff:0000:0000:0000:5555:5555:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 16 pri 1 dst ffff:0000:0000:0000:6666:6666:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 110 pri 1 dst ffff:0000:1111:1111:0000:0000:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 111 pri 1 dst ffff:0000:1111:1111:1111:1111:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 125 pri 1 dst ffff:0000:0000:0000:aaaa:aaaa:0000:0000/96 \
+sport 0:65535 dport 0:65535
+sp ipv6 out esp protect 126 pri 1 dst ffff:0000:0000:0000:bbbb:bbbb:0000:0000/96 \
+sport 0:65535 dport 0:65535
+
+#SA rules
+sa in 5 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.1.5 dst 172.16.2.5
+
+sa in 6 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.1.6 dst 172.16.2.6
+
+sa in 10 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa in 11 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa in 15 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.5 \
+dst 172.16.2.5
+
+sa in 16 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.1.6 \
+dst 172.16.2.6
+
+sa in 25 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:5555 \
+dst 2222:2222:2222:2222:2222:2222:2222:5555
+
+sa in 26 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 1111:1111:1111:1111:1111:1111:1111:6666 \
+dst 2222:2222:2222:2222:2222:2222:2222:6666
+
+sa out 105 cipher_algo aes-128-cbc cipher_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+auth_algo sha1-hmac auth_key 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 \
+mode ipv4-tunnel src 172.16.2.5 dst 172.16.1.5
+
+sa out 106 cipher_algo aes-128-cbc cipher_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0 auth_algo sha1-hmac auth_key a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:a0:\
+a0:a0:a0:a0:a0:a0:a0:a0:a0 mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 110 cipher_algo aes-128-cbc cipher_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1 auth_algo sha1-hmac auth_key a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:\
+a1:a1:a1:a1:a1:a1:a1:a1:a1 mode transport
+
+sa out 111 cipher_algo aes-128-cbc cipher_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2 auth_algo sha1-hmac auth_key b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:b2:\
+b2:b2:b2:b2:b2:b2:b2:b2:b2 mode transport
+
+sa out 115 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.5 \
+dst 172.16.1.5
+
+sa out 116 cipher_algo null auth_algo null mode ipv4-tunnel src 172.16.2.6 dst 172.16.1.6
+
+sa out 125 cipher_algo aes-128-cbc cipher_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3 auth_algo sha1-hmac auth_key c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:c3:\
+c3:c3:c3:c3:c3:c3:c3:c3:c3 mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:5555 \
+dst 1111:1111:1111:1111:1111:1111:1111:5555
+
+sa out 126 cipher_algo aes-128-cbc cipher_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d auth_algo sha1-hmac auth_key 4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:4d:\
+4d:4d:4d:4d:4d:4d:4d:4d:4d mode ipv6-tunnel \
+src 2222:2222:2222:2222:2222:2222:2222:6666 \
+dst 1111:1111:1111:1111:1111:1111:1111:6666
+
+#Routing rules
+rt ipv4 dst 172.16.1.5/32 port 0
+rt ipv4 dst 172.16.1.6/32 port 1
+rt ipv4 dst 192.168.185.0/24 port 0
+rt ipv4 dst 192.168.186.0/24 port 1
+rt ipv4 dst 192.168.245.0/24 port 0
+rt ipv4 dst 192.168.246.0/24 port 1
+rt ipv4 dst 192.168.105.0/24 port 2
+rt ipv4 dst 192.168.106.0/24 port 3
+rt ipv4 dst 192.168.55.0/24 port 2
+rt ipv4 dst 192.168.56.0/24 port 3
+rt ipv4 dst 192.168.175.0/24 port 2
+rt ipv4 dst 192.168.176.0/24 port 3
+rt ipv4 dst 192.168.200.0/24 port 2
+rt ipv4 dst 192.168.201.0/24 port 3
+rt ipv4 dst 192.168.240.0/24 port 2
+rt ipv4 dst 192.168.241.0/24 port 3
+
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0
+rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:6666/116 port 1
+rt ipv6 dst ffff:0000:1111:1111:0000:0000:0000:0000/116 port 0
+rt ipv6 dst ffff:0000:1111:1111:1111:1111:0000:0000/116 port 1
+rt ipv6 dst 0000:0000:0000:0000:aaaa:aaaa:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:bbbb:bbbb:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:0000:0000:5555:5555:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:0000:0000:6666:6666:0000:0000/116 port 3
+rt ipv6 dst 0000:0000:1111:1111:0000:0000:0000:0000/116 port 2
+rt ipv6 dst 0000:0000:1111:1111:1111:1111:0000:0000/116 port 3
-- 
2.5.5

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

* Re: [PATCH v8 1/2] examples/ipsec-secgw: add configuration file support
  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
  1 sibling, 1 reply; 32+ messages in thread
From: Sergio Gonzalez Monroy @ 2016-09-23  7:52 UTC (permalink / raw)
  To: Fan Zhang, dev

On 21/09/2016 13:05, Fan Zhang wrote:
> 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> <cipher_key> <auth_algo> <auth_key> \
> <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/rel_notes/release_16_11.rst   |   4 +
>   doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++------------------
>   examples/ipsec-secgw/Makefile            |   1 +
>   examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
>   examples/ipsec-secgw/ipsec.h             |  14 +-
>   examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
>   examples/ipsec-secgw/parser.h            | 116 +++++
>   examples/ipsec-secgw/rt.c                | 255 ++++------
>   examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
>   examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
>   examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
>   11 files changed, 2397 insertions(+), 1319 deletions(-)
>   create mode 100644 examples/ipsec-secgw/parser.c
>   create mode 100644 examples/ipsec-secgw/parser.h

Acked-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>

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

* Re: [PATCH v8 2/2] examples/ipsec-secgw: add sample configuration files
  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
  0 siblings, 1 reply; 32+ messages in thread
From: Sergio Gonzalez Monroy @ 2016-09-23  7:53 UTC (permalink / raw)
  To: Fan Zhang, dev

On 21/09/2016 13:05, Fan Zhang wrote:
> This patch adds two sample configuration files to ipsec-secgw sample
> application. The sample configuration files shows how to setup
> back-to-back systems that would forward traffic through an IPsec
> tunnel.
>
> Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
> ---
>   examples/ipsec-secgw/ep0.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
>   examples/ipsec-secgw/ep1.cfg | 160 +++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 320 insertions(+)
>   create mode 100644 examples/ipsec-secgw/ep0.cfg
>   create mode 100644 examples/ipsec-secgw/ep1.cfg
>

Acked-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>

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

* Re: [PATCH v8 1/2] examples/ipsec-secgw: add configuration file support
  2016-09-23  7:52             ` Sergio Gonzalez Monroy
@ 2016-09-23 22:51               ` De Lara Guarch, Pablo
  0 siblings, 0 replies; 32+ messages in thread
From: De Lara Guarch, Pablo @ 2016-09-23 22:51 UTC (permalink / raw)
  To: Gonzalez Monroy, Sergio, Zhang, Roy Fan, dev



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Sergio Gonzalez
> Monroy
> Sent: Friday, September 23, 2016 12:53 AM
> To: Zhang, Roy Fan; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v8 1/2] examples/ipsec-secgw: add
> configuration file support
> 
> On 21/09/2016 13:05, Fan Zhang wrote:
> > 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> <cipher_key> <auth_algo> <auth_key> \
> > <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/rel_notes/release_16_11.rst   |   4 +
> >   doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++-----------
> -------
> >   examples/ipsec-secgw/Makefile            |   1 +
> >   examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
> >   examples/ipsec-secgw/ipsec.h             |  14 +-
> >   examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
> >   examples/ipsec-secgw/parser.h            | 116 +++++
> >   examples/ipsec-secgw/rt.c                | 255 ++++------
> >   examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
> >   examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
> >   examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
> >   11 files changed, 2397 insertions(+), 1319 deletions(-)
> >   create mode 100644 examples/ipsec-secgw/parser.c
> >   create mode 100644 examples/ipsec-secgw/parser.h
> 
> Acked-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>

Applied to dpdk-next-crypto, moving the update in release_16_11.rst  
from "Resolved issues" to "New Features" section.

Thanks,
Pablo

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

* Re: [PATCH v8 2/2] examples/ipsec-secgw: add sample configuration files
  2016-09-23  7:53             ` Sergio Gonzalez Monroy
@ 2016-09-23 22:51               ` De Lara Guarch, Pablo
  0 siblings, 0 replies; 32+ messages in thread
From: De Lara Guarch, Pablo @ 2016-09-23 22:51 UTC (permalink / raw)
  To: Gonzalez Monroy, Sergio, Zhang, Roy Fan, dev



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Sergio Gonzalez
> Monroy
> Sent: Friday, September 23, 2016 12:53 AM
> To: Zhang, Roy Fan; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v8 2/2] examples/ipsec-secgw: add sample
> configuration files
> 
> On 21/09/2016 13:05, Fan Zhang wrote:
> > This patch adds two sample configuration files to ipsec-secgw sample
> > application. The sample configuration files shows how to setup
> > back-to-back systems that would forward traffic through an IPsec
> > tunnel.
> >
> > Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
> > ---
> >   examples/ipsec-secgw/ep0.cfg | 160
> +++++++++++++++++++++++++++++++++++++++++++
> >   examples/ipsec-secgw/ep1.cfg | 160
> +++++++++++++++++++++++++++++++++++++++++++
> >   2 files changed, 320 insertions(+)
> >   create mode 100644 examples/ipsec-secgw/ep0.cfg
> >   create mode 100644 examples/ipsec-secgw/ep1.cfg
> >
> 
> Acked-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>

Applied to dpdk-next-crypto.
Thanks,

Pablo

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

* Re: [PATCH v8 1/2] examples/ipsec-secgw: add configuration file support
  2016-09-21 12:05           ` [PATCH v8 1/2] examples/ipsec-secgw: " Fan Zhang
  2016-09-23  7:52             ` Sergio Gonzalez Monroy
@ 2016-09-29  1:19             ` Chen, Zhaoyan
  1 sibling, 0 replies; 32+ messages in thread
From: Chen, Zhaoyan @ 2016-09-29  1:19 UTC (permalink / raw)
  To: dev

Tested-by: Zhaoyan Chen <zhaoyan.chen@intel.com>
- Apply patch: Pass
- Compile: Pass
- OS: 3.17.4-301.fc21.x86_64
- GCC: 4.9.2

Test Case - Pass
- Launch ipsec-gateway with "-f" parameter and ep0.cfg, check the default configuration file work.
- Modified Algorithm in config file and launch ipsec-gateway and check the new algorithm effected
- Modified the key and check the key effected.
- Modified the iv and check the iv effected
- Modified the router rule and check the the rule effected

But for negative test, 
If input incorrect key and iv, the application will PANIC in parse_cfg_file()
For instance, change the line 70 in ep0.cfg, add 1 more <space> at the end of line, like "a0:a0:a0: \" 
The application will Abort (core dump). Expected it should report error and exit safe. 



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Fan Zhang
> Sent: Wednesday, September 21, 2016 8:05 PM
> To: dev@dpdk.org
> Cc: Gonzalez Monroy, Sergio <sergio.gonzalez.monroy@intel.com>
> Subject: [dpdk-dev] [PATCH v8 1/2] examples/ipsec-secgw: add
> configuration file support
> 
> 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> <cipher_key> <auth_algo> <auth_key> \
> <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/rel_notes/release_16_11.rst   |   4 +
>  doc/guides/sample_app_ug/ipsec_secgw.rst | 845 +++++++++++++----------
> --------
>  examples/ipsec-secgw/Makefile            |   1 +
>  examples/ipsec-secgw/ipsec-secgw.c       |  58 ++-
>  examples/ipsec-secgw/ipsec.h             |  14 +-
>  examples/ipsec-secgw/parser.c            | 599 ++++++++++++++++++++++
>  examples/ipsec-secgw/parser.h            | 116 +++++
>  examples/ipsec-secgw/rt.c                | 255 ++++------
>  examples/ipsec-secgw/sa.c                | 747 +++++++++++++++++----------
>  examples/ipsec-secgw/sp4.c               | 538 ++++++++++++--------
>  examples/ipsec-secgw/sp6.c               | 539 +++++++++++++-------
>  11 files changed, 2397 insertions(+), 1319 deletions(-)
>  create mode 100644 examples/ipsec-secgw/parser.c
>  create mode 100644 examples/ipsec-secgw/parser.h
> 
> diff --git a/doc/guides/rel_notes/release_16_11.rst
> b/doc/guides/rel_notes/release_16_11.rst
> index 451872e..da61798 100644
> --- a/doc/guides/rel_notes/release_16_11.rst
> +++ b/doc/guides/rel_notes/release_16_11.rst
> @@ -82,6 +82,10 @@ Libraries
>  Examples
>  ~~~~~~~~
> 
> +* **ipsec-secgw: add configuration file support**
> +
> +  ipsec-secgw sample application now supports configuration file to specify
> +  SP, SA, and routing rules.
> 
>  Other

[...] 

> --
> 2.5.5

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

end of thread, other threads:[~2016-09-29  1:19 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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   ` [PATCH v2 1/2] examples/ipsec-secgw: " Fan Zhang
2016-07-11 15:19     ` 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

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.