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

Reply via email to