DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements
@ 2015-07-07  8:09 Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 01/11] ip_pipeline: add parsing for config files with new syntax Maciej Gajdzica
                   ` (12 more replies)
  0 siblings, 13 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

This patchset enhances functionality of ip_pipeline application. New config
file syntax is introduced, so parser is changed. Changed structure of the
application. Now every global variable is stored in app_struct in app.h.
Syntax of pipeline cli commands was changed. Implementation of cli commands
for every pipeline is moved to the separate file.

Changes in v2:
- renamed some files
- added more config files
- reworked flow classification pipeline implementation
- fixed some bugs

Changes in v3:
- fixed checkpatch errors
- fixed bug with message queues
- fixed bug with application log

Changes in v4:
- fixed build issue with gcc 5
- fixed bugs in flow classification and firewall pipelines

Changes in v5:
- fixed build issue with clang 3.6

Changes in v6:
- fixed build issues with icc 15
- fixed build issues with gcc 32bit

Daniel Mrzyglod (1):
  ip_pipeline: added new implementation of firewall pipeline

Jasvinder Singh (3):
  ip_pipeline: added config checks
  ip_pipeline: added master pipeline
  ip_pipeline: added new implementation of passthrough pipeline

Maciej Gajdzica (6):
  ip_pipeline: modified init to match new params      struct
  ip_pipeline: moved pipelines to separate folder
  ip_pipeline: added application thread
  ip_pipeline: moved config files to separate folder
  ip_pipeline: added new implementation of routing pipeline
  ip_pipeline: added new implementation of flow classification pipeline

Pawel Wodkowski (1):
  ip_pipeline: add parsing for config files with new syntax

 examples/ip_pipeline/Makefile                      |   36 +-
 examples/ip_pipeline/app.h                         |  909 ++++++++
 examples/ip_pipeline/cmdline.c                     | 1976 ----------------
 examples/ip_pipeline/config.c                      |  419 ----
 examples/ip_pipeline/config/ip_pipeline.cfg        |    9 +
 examples/ip_pipeline/config/ip_pipeline.sh         |    5 +
 examples/ip_pipeline/config/tm_profile.cfg         |  105 +
 examples/ip_pipeline/config_check.c                |  399 ++++
 examples/ip_pipeline/config_parse.c                | 2457 ++++++++++++++++++++
 examples/ip_pipeline/config_parse_tm.c             |  448 ++++
 examples/ip_pipeline/cpu_core_map.c                |  493 ++++
 examples/ip_pipeline/cpu_core_map.h                |   69 +
 examples/ip_pipeline/init.c                        | 1656 +++++++++----
 examples/ip_pipeline/ip_pipeline.cfg               |   56 -
 examples/ip_pipeline/ip_pipeline.sh                |   18 -
 examples/ip_pipeline/main.c                        |  137 +-
 examples/ip_pipeline/main.h                        |  298 ---
 examples/ip_pipeline/pipeline.h                    |   87 +
 examples/ip_pipeline/pipeline/hash_func.h          |  351 +++
 .../ip_pipeline/pipeline/pipeline_actions_common.h |  119 +
 examples/ip_pipeline/pipeline/pipeline_common_be.c |  206 ++
 examples/ip_pipeline/pipeline/pipeline_common_be.h |  163 ++
 examples/ip_pipeline/pipeline/pipeline_common_fe.c | 1333 +++++++++++
 examples/ip_pipeline/pipeline/pipeline_common_fe.h |  228 ++
 examples/ip_pipeline/pipeline/pipeline_firewall.c  | 1003 ++++++++
 examples/ip_pipeline/pipeline/pipeline_firewall.h  |   63 +
 .../ip_pipeline/pipeline/pipeline_firewall_be.c    |  740 ++++++
 .../ip_pipeline/pipeline/pipeline_firewall_be.h    |  138 ++
 .../pipeline/pipeline_flow_classification.c        | 2061 ++++++++++++++++
 .../pipeline/pipeline_flow_classification.h        |  105 +
 .../pipeline/pipeline_flow_classification_be.c     |  589 +++++
 .../pipeline/pipeline_flow_classification_be.h     |  140 ++
 examples/ip_pipeline/pipeline/pipeline_master.c    |   47 +
 examples/ip_pipeline/pipeline/pipeline_master.h    |   41 +
 examples/ip_pipeline/pipeline/pipeline_master_be.c |  150 ++
 examples/ip_pipeline/pipeline/pipeline_master_be.h |   41 +
 .../ip_pipeline/pipeline/pipeline_passthrough.c    |   47 +
 .../ip_pipeline/pipeline/pipeline_passthrough.h    |   41 +
 .../ip_pipeline/pipeline/pipeline_passthrough_be.c |  772 ++++++
 .../ip_pipeline/pipeline/pipeline_passthrough_be.h |   41 +
 examples/ip_pipeline/pipeline/pipeline_routing.c   | 1545 ++++++++++++
 examples/ip_pipeline/pipeline/pipeline_routing.h   |   99 +
 .../ip_pipeline/pipeline/pipeline_routing_be.c     |  869 +++++++
 .../ip_pipeline/pipeline/pipeline_routing_be.h     |  230 ++
 examples/ip_pipeline/pipeline_be.h                 |  256 ++
 examples/ip_pipeline/pipeline_firewall.c           |  313 ---
 .../ip_pipeline/pipeline_flow_classification.c     |  306 ---
 examples/ip_pipeline/pipeline_ipv4_frag.c          |  184 --
 examples/ip_pipeline/pipeline_ipv4_ras.c           |  181 --
 examples/ip_pipeline/pipeline_passthrough.c        |  213 --
 examples/ip_pipeline/pipeline_routing.c            |  474 ----
 examples/ip_pipeline/pipeline_rx.c                 |  385 ---
 examples/ip_pipeline/pipeline_tx.c                 |  283 ---
 examples/ip_pipeline/thread.c                      |  110 +
 54 files changed, 17775 insertions(+), 5669 deletions(-)
 create mode 100644 examples/ip_pipeline/app.h
 delete mode 100644 examples/ip_pipeline/cmdline.c
 delete mode 100644 examples/ip_pipeline/config.c
 create mode 100644 examples/ip_pipeline/config/ip_pipeline.cfg
 create mode 100644 examples/ip_pipeline/config/ip_pipeline.sh
 create mode 100644 examples/ip_pipeline/config/tm_profile.cfg
 create mode 100644 examples/ip_pipeline/config_check.c
 create mode 100644 examples/ip_pipeline/config_parse.c
 create mode 100644 examples/ip_pipeline/config_parse_tm.c
 create mode 100644 examples/ip_pipeline/cpu_core_map.c
 create mode 100644 examples/ip_pipeline/cpu_core_map.h
 delete mode 100644 examples/ip_pipeline/ip_pipeline.cfg
 delete mode 100644 examples/ip_pipeline/ip_pipeline.sh
 delete mode 100644 examples/ip_pipeline/main.h
 create mode 100644 examples/ip_pipeline/pipeline.h
 create mode 100644 examples/ip_pipeline/pipeline/hash_func.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_actions_common.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_be.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_fe.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_fe.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall_be.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification_be.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master_be.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough_be.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing_be.h
 create mode 100644 examples/ip_pipeline/pipeline_be.h
 delete mode 100644 examples/ip_pipeline/pipeline_firewall.c
 delete mode 100644 examples/ip_pipeline/pipeline_flow_classification.c
 delete mode 100644 examples/ip_pipeline/pipeline_ipv4_frag.c
 delete mode 100644 examples/ip_pipeline/pipeline_ipv4_ras.c
 delete mode 100644 examples/ip_pipeline/pipeline_passthrough.c
 delete mode 100644 examples/ip_pipeline/pipeline_routing.c
 delete mode 100644 examples/ip_pipeline/pipeline_rx.c
 delete mode 100644 examples/ip_pipeline/pipeline_tx.c
 create mode 100644 examples/ip_pipeline/thread.c

-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 01/11] ip_pipeline: add parsing for config files with new syntax
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 02/11] ip_pipeline: added config checks Maciej Gajdzica
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

From: Pawel Wodkowski <pawelx.wodkowski@intel.com>

New syntax of config files is needed for ip_pipeline example
enhancements. Some old files are temporarily disabled in the Makefile.
It is part of a bigger change.

Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
---
 examples/ip_pipeline/Makefile          |   17 +-
 examples/ip_pipeline/app.h             |  909 ++++++++++++
 examples/ip_pipeline/config.c          |  419 ------
 examples/ip_pipeline/config_parse.c    | 2457 ++++++++++++++++++++++++++++++++
 examples/ip_pipeline/config_parse_tm.c |  448 ++++++
 examples/ip_pipeline/cpu_core_map.c    |  493 +++++++
 examples/ip_pipeline/cpu_core_map.h    |   69 +
 examples/ip_pipeline/main.c            |  130 +-
 examples/ip_pipeline/main.h            |  298 ----
 examples/ip_pipeline/pipeline.h        |   87 ++
 examples/ip_pipeline/pipeline_be.h     |  256 ++++
 11 files changed, 4730 insertions(+), 853 deletions(-)
 create mode 100644 examples/ip_pipeline/app.h
 delete mode 100644 examples/ip_pipeline/config.c
 create mode 100644 examples/ip_pipeline/config_parse.c
 create mode 100644 examples/ip_pipeline/config_parse_tm.c
 create mode 100644 examples/ip_pipeline/cpu_core_map.c
 create mode 100644 examples/ip_pipeline/cpu_core_map.h
 delete mode 100644 examples/ip_pipeline/main.h
 create mode 100644 examples/ip_pipeline/pipeline.h
 create mode 100644 examples/ip_pipeline/pipeline_be.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index e70fdc7..b0feb4f 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -43,20 +43,9 @@ APP = ip_pipeline
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cmdline.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_rx.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_tx.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_classification.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_ipv4_frag.c
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_ipv4_ras.c
-
-ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
-SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall.c
-endif
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse_tm.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
 
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
new file mode 100644
index 0000000..521e3a0
--- /dev/null
+++ b/examples/ip_pipeline/app.h
@@ -0,0 +1,909 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_APP_H__
+#define __INCLUDE_APP_H__
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_mempool.h>
+#include <rte_ring.h>
+#include <rte_sched.h>
+#include <cmdline_parse.h>
+
+#include <rte_ethdev.h>
+
+#include "cpu_core_map.h"
+#include "pipeline.h"
+
+#define APP_PARAM_NAME_SIZE                      PIPELINE_NAME_SIZE
+
+struct app_mempool_params {
+	char *name;
+	uint32_t parsed;
+	uint32_t buffer_size;
+	uint32_t pool_size;
+	uint32_t cache_size;
+	uint32_t cpu_socket_id;
+};
+
+struct app_link_params {
+	char *name;
+	uint32_t parsed;
+	uint32_t pmd_id; /* Generated based on port mask */
+	uint32_t arp_q; /* 0 = Disabled (packets go to default queue 0) */
+	uint32_t tcp_syn_local_q; /* 0 = Disabled (pkts go to default queue) */
+	uint32_t ip_local_q; /* 0 = Disabled (pkts go to default queue 0) */
+	uint32_t tcp_local_q; /* 0 = Disabled (pkts go to default queue 0) */
+	uint32_t udp_local_q; /* 0 = Disabled (pkts go to default queue 0) */
+	uint32_t sctp_local_q; /* 0 = Disabled (pkts go to default queue 0) */
+	uint32_t state; /* DOWN = 0, UP = 1 */
+	uint32_t ip; /* 0 = Invalid */
+	uint32_t depth; /* Valid only when IP is valid */
+	uint64_t mac_addr; /* Read from HW */
+
+	struct rte_eth_conf conf;
+	uint8_t promisc;
+};
+
+struct app_pktq_hwq_in_params {
+	char *name;
+	uint32_t parsed;
+	uint32_t mempool_id; /* Position in the app->mempool_params */
+	uint32_t size;
+	uint32_t burst;
+
+	struct rte_eth_rxconf conf;
+};
+
+struct app_pktq_hwq_out_params {
+	char *name;
+	uint32_t parsed;
+	uint32_t size;
+	uint32_t burst;
+	uint32_t dropless;
+	uint64_t n_retries;
+	struct rte_eth_txconf conf;
+};
+
+struct app_pktq_swq_params {
+	char *name;
+	uint32_t parsed;
+	uint32_t size;
+	uint32_t burst_read;
+	uint32_t burst_write;
+	uint32_t dropless;
+	uint64_t n_retries;
+	uint32_t cpu_socket_id;
+};
+
+#ifndef APP_FILE_NAME_SIZE
+#define APP_FILE_NAME_SIZE                       256
+#endif
+
+#ifndef APP_MAX_SCHED_SUBPORTS
+#define APP_MAX_SCHED_SUBPORTS                   8
+#endif
+
+#ifndef APP_MAX_SCHED_PIPES
+#define APP_MAX_SCHED_PIPES                      4096
+#endif
+
+struct app_pktq_tm_params {
+	char *name;
+	uint32_t parsed;
+	const char *file_name;
+	struct rte_sched_port_params sched_port_params;
+	struct rte_sched_subport_params
+		sched_subport_params[APP_MAX_SCHED_SUBPORTS];
+	struct rte_sched_pipe_params
+		sched_pipe_profiles[RTE_SCHED_PIPE_PROFILES_PER_PORT];
+	int sched_pipe_to_profile[APP_MAX_SCHED_SUBPORTS * APP_MAX_SCHED_PIPES];
+	uint32_t burst_read;
+	uint32_t burst_write;
+};
+
+struct app_pktq_source_params {
+	char *name;
+	uint32_t parsed;
+	uint32_t mempool_id; /* Position in the app->mempool_params array */
+	uint32_t burst;
+};
+
+struct app_pktq_sink_params {
+	char *name;
+	uint8_t parsed;
+};
+
+struct app_msgq_params {
+	char *name;
+	uint32_t parsed;
+	uint32_t size;
+	uint32_t cpu_socket_id;
+};
+
+enum app_pktq_in_type {
+	APP_PKTQ_IN_HWQ,
+	APP_PKTQ_IN_SWQ,
+	APP_PKTQ_IN_TM,
+	APP_PKTQ_IN_SOURCE,
+};
+
+struct app_pktq_in_params {
+	enum app_pktq_in_type type;
+	uint32_t id; /* Position in the appropriate app array */
+};
+
+enum app_pktq_out_type {
+	APP_PKTQ_OUT_HWQ,
+	APP_PKTQ_OUT_SWQ,
+	APP_PKTQ_OUT_TM,
+	APP_PKTQ_OUT_SINK,
+};
+
+struct app_pktq_out_params {
+	enum app_pktq_out_type type;
+	uint32_t id; /* Position in the appropriate app array */
+};
+
+#ifndef APP_PIPELINE_TYPE_SIZE
+#define APP_PIPELINE_TYPE_SIZE                   64
+#endif
+
+#define APP_MAX_PIPELINE_PKTQ_IN                 PIPELINE_MAX_PORT_IN
+#define APP_MAX_PIPELINE_PKTQ_OUT                PIPELINE_MAX_PORT_OUT
+#define APP_MAX_PIPELINE_MSGQ_IN                 PIPELINE_MAX_MSGQ_IN
+#define APP_MAX_PIPELINE_MSGQ_OUT                PIPELINE_MAX_MSGQ_OUT
+
+#define APP_MAX_PIPELINE_ARGS                    PIPELINE_MAX_ARGS
+
+struct app_pipeline_params {
+	char *name;
+	uint8_t parsed;
+
+	char type[APP_PIPELINE_TYPE_SIZE];
+
+	uint32_t socket_id;
+	uint32_t core_id;
+	uint32_t hyper_th_id;
+
+	struct app_pktq_in_params pktq_in[APP_MAX_PIPELINE_PKTQ_IN];
+	struct app_pktq_out_params pktq_out[APP_MAX_PIPELINE_PKTQ_OUT];
+	uint32_t msgq_in[APP_MAX_PIPELINE_MSGQ_IN];
+	uint32_t msgq_out[APP_MAX_PIPELINE_MSGQ_OUT];
+
+	uint32_t n_pktq_in;
+	uint32_t n_pktq_out;
+	uint32_t n_msgq_in;
+	uint32_t n_msgq_out;
+
+	uint32_t timer_period;
+
+	char *args_name[APP_MAX_PIPELINE_ARGS];
+	char *args_value[APP_MAX_PIPELINE_ARGS];
+	uint32_t n_args;
+};
+
+struct app_pipeline_data {
+	void *be;
+	void *fe;
+	uint64_t timer_period;
+};
+
+struct app_thread_pipeline_data {
+	void *be;
+	pipeline_be_op_run f_run;
+	pipeline_be_op_timer f_timer;
+	uint64_t timer_period;
+	uint64_t deadline;
+};
+
+#ifndef APP_MAX_THREAD_PIPELINES
+#define APP_MAX_THREAD_PIPELINES                 16
+#endif
+
+struct app_thread_data {
+	struct app_thread_pipeline_data regular[APP_MAX_THREAD_PIPELINES];
+	struct app_thread_pipeline_data custom[APP_MAX_THREAD_PIPELINES];
+
+	uint32_t n_regular;
+	uint32_t n_custom;
+
+	uint64_t deadline;
+};
+
+struct app_eal_params {
+	/* Map lcore set to physical cpu set */
+	char *coremap;
+
+	/* Core ID that is used as master */
+	uint32_t master_lcore_present;
+	uint32_t master_lcore;
+
+	/* Number of memory channels */
+	uint32_t channels_present;
+	uint32_t channels;
+
+	/* Memory to allocate (see also --socket-mem) */
+	uint32_t memory_present;
+	uint32_t memory;
+
+	/* Force number of memory ranks (don't detect) */
+	uint32_t ranks_present;
+	uint32_t ranks;
+
+	/* Add a PCI device in black list. */
+	char *pci_blacklist;
+
+	/* Add a PCI device in white list. */
+	char *pci_whitelist;
+
+	/* Add a virtual device. */
+	char *vdev;
+
+	 /* Use VMware TSC map instead of native RDTSC */
+	uint32_t vmware_tsc_map_present;
+	int vmware_tsc_map;
+
+	 /* Type of this process (primary|secondary|auto) */
+	char *proc_type;
+
+	 /* Set syslog facility */
+	char *syslog;
+
+	/* Set default log level */
+	uint32_t log_level_present;
+	uint32_t log_level;
+
+	/* Display version information on startup */
+	uint32_t version_present;
+	int version;
+
+	/* This help */
+	uint32_t help_present;
+	int help;
+
+	 /* Use malloc instead of hugetlbfs */
+	uint32_t no_huge_present;
+	int no_huge;
+
+	/* Disable PCI */
+	uint32_t no_pci_present;
+	int no_pci;
+
+	/* Disable HPET */
+	uint32_t no_hpet_present;
+	int no_hpet;
+
+	/* No shared config (mmap'd files) */
+	uint32_t no_shconf_present;
+	int no_shconf;
+
+	/* Add driver */
+	char *add_driver;
+
+	/*  Memory to allocate on sockets (comma separated values)*/
+	char *socket_mem;
+
+	/* Directory where hugetlbfs is mounted */
+	char *huge_dir;
+
+	/* Prefix for hugepage filenames */
+	char *file_prefix;
+
+	/* Base virtual address */
+	char *base_virtaddr;
+
+	/* Create /dev/uioX (usually done by hotplug) */
+	uint32_t create_uio_dev_present;
+	int create_uio_dev;
+
+	/* Interrupt mode for VFIO (legacy|msi|msix) */
+	char *vfio_intr;
+
+	/* Support running on Xen dom0 without hugetlbfs */
+	uint32_t xen_dom0_present;
+	int xen_dom0;
+};
+
+#ifndef APP_APPNAME_SIZE
+#define APP_APPNAME_SIZE                         256
+#endif
+
+#ifndef APP_MAX_MEMPOOLS
+#define APP_MAX_MEMPOOLS                         8
+#endif
+
+#ifndef APP_MAX_LINKS
+#define APP_MAX_LINKS                            16
+#endif
+
+#ifndef APP_LINK_MAX_HWQ_IN
+#define APP_LINK_MAX_HWQ_IN                      64
+#endif
+
+#ifndef APP_LINK_MAX_HWQ_OUT
+#define APP_LINK_MAX_HWQ_OUT                     64
+#endif
+
+#define APP_MAX_HWQ_IN                     (APP_MAX_LINKS * APP_LINK_MAX_HWQ_IN)
+
+#define APP_MAX_HWQ_OUT                   (APP_MAX_LINKS * APP_LINK_MAX_HWQ_OUT)
+
+#ifndef APP_MAX_PKTQ_SWQ
+#define APP_MAX_PKTQ_SWQ                         256
+#endif
+
+#define APP_MAX_PKTQ_TM                          APP_MAX_LINKS
+
+#ifndef APP_MAX_PKTQ_SOURCE
+#define APP_MAX_PKTQ_SOURCE                      16
+#endif
+
+#ifndef APP_MAX_PKTQ_SINK
+#define APP_MAX_PKTQ_SINK                        16
+#endif
+
+#ifndef APP_MAX_MSGQ
+#define APP_MAX_MSGQ                             64
+#endif
+
+#ifndef APP_MAX_PIPELINES
+#define APP_MAX_PIPELINES                        64
+#endif
+
+#ifndef APP_EAL_ARGC
+#define APP_EAL_ARGC                             32
+#endif
+
+#ifndef APP_MAX_PIPELINE_TYPES
+#define APP_MAX_PIPELINE_TYPES                   64
+#endif
+
+#ifndef APP_MAX_THREADS
+#define APP_MAX_THREADS                          RTE_MAX_LCORE
+#endif
+
+#ifndef APP_MAX_CMDS
+#define APP_MAX_CMDS                             64
+#endif
+
+struct app_params {
+	/* Config */
+	char app_name[APP_APPNAME_SIZE];
+	const char *config_file;
+	const char *script_file;
+	uint64_t port_mask;
+	uint32_t log_level;
+
+	struct app_eal_params eal_params;
+	struct app_mempool_params mempool_params[APP_MAX_MEMPOOLS];
+	struct app_link_params link_params[APP_MAX_LINKS];
+	struct app_pktq_hwq_in_params hwq_in_params[APP_MAX_HWQ_IN];
+	struct app_pktq_hwq_out_params hwq_out_params[APP_MAX_HWQ_OUT];
+	struct app_pktq_swq_params swq_params[APP_MAX_PKTQ_SWQ];
+	struct app_pktq_tm_params tm_params[APP_MAX_PKTQ_TM];
+	struct app_pktq_source_params source_params[APP_MAX_PKTQ_SOURCE];
+	struct app_pktq_sink_params sink_params[APP_MAX_PKTQ_SINK];
+	struct app_msgq_params msgq_params[APP_MAX_MSGQ];
+	struct app_pipeline_params pipeline_params[APP_MAX_PIPELINES];
+
+	uint32_t n_mempools;
+	uint32_t n_links;
+	uint32_t n_pktq_hwq_in;
+	uint32_t n_pktq_hwq_out;
+	uint32_t n_pktq_swq;
+	uint32_t n_pktq_tm;
+	uint32_t n_pktq_source;
+	uint32_t n_pktq_sink;
+	uint32_t n_msgq;
+	uint32_t n_pipelines;
+
+	/* Init */
+	char *eal_argv[1 + APP_EAL_ARGC];
+	struct cpu_core_map *core_map;
+	uint64_t core_mask;
+	struct rte_mempool *mempool[APP_MAX_MEMPOOLS];
+	struct rte_ring *swq[APP_MAX_PKTQ_SWQ];
+	struct rte_sched_port *tm[APP_MAX_PKTQ_TM];
+	struct rte_ring *msgq[APP_MAX_MSGQ];
+	struct pipeline_type pipeline_type[APP_MAX_PIPELINE_TYPES];
+	struct app_pipeline_data pipeline_data[APP_MAX_PIPELINES];
+	struct app_thread_data thread_data[APP_MAX_THREADS];
+	cmdline_parse_ctx_t cmds[APP_MAX_CMDS + 1];
+
+	int eal_argc;
+	uint32_t n_pipeline_types;
+	uint32_t n_cmds;
+};
+
+#define APP_PARAM_VALID(obj) ((obj)->name != NULL)
+
+#define APP_PARAM_COUNT(obj_array, n_objs)				\
+{									\
+	size_t i;							\
+									\
+	n_objs = 0;							\
+	for (i = 0; i < RTE_DIM(obj_array); i++)			\
+		if (APP_PARAM_VALID(&((obj_array)[i])))			\
+			n_objs++;					\
+}
+
+#define APP_PARAM_FIND(obj_array, key)					\
+({									\
+	ssize_t obj_idx;						\
+	const ssize_t obj_count = RTE_DIM(obj_array);			\
+									\
+	for (obj_idx = 0; obj_idx < obj_count; obj_idx++) {		\
+		if (!APP_PARAM_VALID(&((obj_array)[obj_idx])))		\
+			continue;					\
+									\
+		if (strcmp(key, (obj_array)[obj_idx].name) == 0)	\
+			break;						\
+	}								\
+	obj_idx < obj_count ? obj_idx : -ENOENT;			\
+})
+
+#define APP_PARAM_FIND_BY_ID(obj_array, prefix, id, obj)		\
+do {									\
+	char name[APP_PARAM_NAME_SIZE];					\
+	ssize_t pos;							\
+									\
+	sprintf(name, prefix "%" PRIu32, id);				\
+	pos = APP_PARAM_FIND(obj_array, name);				\
+	obj = (pos < 0) ? NULL : &((obj_array)[pos]);			\
+} while (0)
+
+#define APP_PARAM_GET_ID(obj, prefix, id)				\
+do									\
+	sscanf(obj->name, prefix "%" SCNu32, &id);				\
+while (0)								\
+
+#define APP_PARAM_ADD(obj_array, obj_name)				\
+({									\
+	ssize_t obj_idx;						\
+	const ssize_t obj_count = RTE_DIM(obj_array);			\
+									\
+	obj_idx = APP_PARAM_FIND(obj_array, obj_name);			\
+	if (obj_idx < 0) {						\
+		for (obj_idx = 0; obj_idx < obj_count; obj_idx++) {	\
+			if (!APP_PARAM_VALID(&((obj_array)[obj_idx])))	\
+				break;					\
+		}							\
+									\
+		if (obj_idx < obj_count) {				\
+			(obj_array)[obj_idx].name = strdup(obj_name);   \
+			if ((obj_array)[obj_idx].name == NULL)          \
+				obj_idx = -EINVAL;			\
+		} else							\
+			obj_idx = -ENOMEM;				\
+	}								\
+	obj_idx;							\
+})
+
+#define	APP_CHECK(exp, fmt, ...)					\
+do {									\
+	if (!(exp)) {							\
+		fprintf(stderr, fmt "\n", ## __VA_ARGS__);		\
+		abort();						\
+	}								\
+} while (0)
+
+enum app_log_level {
+	APP_LOG_LEVEL_HIGH = 1,
+	APP_LOG_LEVEL_LOW,
+	APP_LOG_LEVELS
+};
+
+#define APP_LOG(app, level, fmt, ...)					\
+do {									\
+	if (app->log_level >= APP_LOG_LEVEL_ ## level)			\
+		fprintf(stdout, "[APP] " fmt "\n", ## __VA_ARGS__);	\
+} while (0)
+
+static inline uint32_t
+app_link_get_n_rxq(struct app_params *app, struct app_link_params *link)
+{
+	uint32_t n_rxq = 0, link_id, i;
+	uint32_t n_pktq_hwq_in = RTE_MIN(app->n_pktq_hwq_in,
+		RTE_DIM(app->hwq_in_params));
+
+	APP_PARAM_GET_ID(link, "LINK", link_id);
+
+	for (i = 0; i < n_pktq_hwq_in; i++) {
+		struct app_pktq_hwq_in_params *p = &app->hwq_in_params[i];
+		uint32_t rxq_link_id, rxq_queue_id;
+
+		sscanf(p->name, "RXQ%" SCNu32 ".%" SCNu32,
+			&rxq_link_id, &rxq_queue_id);
+		if (rxq_link_id == link_id)
+			n_rxq++;
+	}
+
+	return n_rxq;
+}
+
+static inline uint32_t
+app_link_get_n_txq(struct app_params *app, struct app_link_params *link)
+{
+	uint32_t n_txq = 0, link_id, i;
+	uint32_t n_pktq_hwq_out = RTE_MIN(app->n_pktq_hwq_out,
+		RTE_DIM(app->hwq_out_params));
+
+	APP_PARAM_GET_ID(link, "LINK", link_id);
+
+	for (i = 0; i < n_pktq_hwq_out; i++) {
+		struct app_pktq_hwq_out_params *p = &app->hwq_out_params[i];
+		uint32_t txq_link_id, txq_queue_id;
+
+		sscanf(p->name, "TXQ%" SCNu32 ".%" SCNu32,
+			&txq_link_id, &txq_queue_id);
+		if (txq_link_id == link_id)
+			n_txq++;
+	}
+
+	return n_txq;
+}
+
+static inline uint32_t
+app_rxq_get_readers(struct app_params *app, struct app_pktq_hwq_in_params *rxq)
+{
+	uint32_t pos = rxq - app->hwq_in_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_HWQ) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline uint32_t
+app_swq_get_readers(struct app_params *app, struct app_pktq_swq_params *swq)
+{
+	uint32_t pos = swq - app->swq_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_SWQ) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline uint32_t
+app_tm_get_readers(struct app_params *app, struct app_pktq_tm_params *tm)
+{
+	uint32_t pos = tm - app->tm_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_TM) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline uint32_t
+app_source_get_readers(struct app_params *app,
+struct app_pktq_source_params *source)
+{
+	uint32_t pos = source - app->source_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_in = RTE_MIN(p->n_pktq_in, RTE_DIM(p->pktq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_in; j++) {
+			struct app_pktq_in_params *pktq = &p->pktq_in[j];
+
+			if ((pktq->type == APP_PKTQ_IN_SOURCE) &&
+				(pktq->id == pos))
+				n_readers++;
+		}
+	}
+
+	return n_readers;
+}
+
+static inline uint32_t
+app_msgq_get_readers(struct app_params *app, struct app_msgq_params *msgq)
+{
+	uint32_t pos = msgq - app->msgq_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_readers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_msgq_in = RTE_MIN(p->n_msgq_in, RTE_DIM(p->msgq_in));
+		uint32_t j;
+
+		for (j = 0; j < n_msgq_in; j++)
+			if (p->msgq_in[j] == pos)
+				n_readers++;
+	}
+
+	return n_readers;
+}
+
+static inline uint32_t
+app_txq_get_writers(struct app_params *app, struct app_pktq_hwq_out_params *txq)
+{
+	uint32_t pos = txq - app->hwq_out_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_HWQ) &&
+				(pktq->id == pos))
+				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
+static inline uint32_t
+app_swq_get_writers(struct app_params *app, struct app_pktq_swq_params *swq)
+{
+	uint32_t pos = swq - app->swq_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_SWQ) &&
+				(pktq->id == pos))
+				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
+static inline uint32_t
+app_tm_get_writers(struct app_params *app, struct app_pktq_tm_params *tm)
+{
+	uint32_t pos = tm - app->tm_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_TM) &&
+				(pktq->id == pos))
+				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
+static inline uint32_t
+app_sink_get_writers(struct app_params *app, struct app_pktq_sink_params *sink)
+{
+	uint32_t pos = sink - app->sink_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_pktq_out = RTE_MIN(p->n_pktq_out,
+			RTE_DIM(p->pktq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_pktq_out; j++) {
+			struct app_pktq_out_params *pktq = &p->pktq_out[j];
+
+			if ((pktq->type == APP_PKTQ_OUT_SINK) &&
+				(pktq->id == pos))
+				n_writers++;
+		}
+	}
+
+	return n_writers;
+}
+
+static inline uint32_t
+app_msgq_get_writers(struct app_params *app, struct app_msgq_params *msgq)
+{
+	uint32_t pos = msgq - app->msgq_params;
+	uint32_t n_pipelines = RTE_MIN(app->n_pipelines,
+		RTE_DIM(app->pipeline_params));
+	uint32_t n_writers = 0, i;
+
+	for (i = 0; i < n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		uint32_t n_msgq_out = RTE_MIN(p->n_msgq_out,
+			RTE_DIM(p->msgq_out));
+		uint32_t j;
+
+		for (j = 0; j < n_msgq_out; j++)
+			if (p->msgq_out[j] == pos)
+				n_writers++;
+	}
+
+	return n_writers;
+}
+
+static inline struct app_link_params *
+app_get_link_for_rxq(struct app_params *app, struct app_pktq_hwq_in_params *p)
+{
+	char link_name[APP_PARAM_NAME_SIZE];
+	ssize_t link_param_idx;
+	uint32_t rxq_link_id, rxq_queue_id;
+
+	sscanf(p->name, "RXQ%" SCNu32 ".%" SCNu32,
+		&rxq_link_id, &rxq_queue_id);
+	sprintf(link_name, "LINK%" PRIu32, rxq_link_id);
+	link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
+	APP_CHECK((link_param_idx >= 0),
+		"Cannot find %s for %s", link_name, p->name);
+
+	return &app->link_params[link_param_idx];
+}
+
+static inline struct app_link_params *
+app_get_link_for_txq(struct app_params *app, struct app_pktq_hwq_out_params *p)
+{
+	char link_name[APP_PARAM_NAME_SIZE];
+	ssize_t link_param_idx;
+	uint32_t txq_link_id, txq_queue_id;
+
+	sscanf(p->name, "TXQ%" SCNu32 ".%" SCNu32,
+		&txq_link_id, &txq_queue_id);
+	sprintf(link_name, "LINK%" PRIu32, txq_link_id);
+	link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
+	APP_CHECK((link_param_idx >= 0),
+		"Cannot find %s for %s", link_name, p->name);
+
+	return &app->link_params[link_param_idx];
+}
+
+static inline struct app_link_params *
+app_get_link_for_tm(struct app_params *app, struct app_pktq_tm_params *p_tm)
+{
+	char link_name[APP_PARAM_NAME_SIZE];
+	uint32_t link_id;
+	ssize_t link_param_idx;
+
+	sscanf(p_tm->name, "TM%" PRIu32, &link_id);
+	sprintf(link_name, "LINK%" PRIu32, link_id);
+	link_param_idx = APP_PARAM_FIND(app->link_params, link_name);
+	APP_CHECK((link_param_idx >= 0),
+		"Cannot find %s for %s", link_name, p_tm->name);
+
+	return &app->link_params[link_param_idx];
+}
+
+int app_config_init(struct app_params *app);
+
+int app_config_args(struct app_params *app,
+	int argc, char **argv);
+
+int app_config_parse(struct app_params *app,
+	const char *file_name);
+
+int app_config_parse_tm(struct app_params *app);
+
+void app_config_save(struct app_params *app,
+	const char *file_name);
+
+int app_config_check(struct app_params *app);
+
+int app_init(struct app_params *app);
+
+int app_thread(void *arg);
+
+int app_pipeline_type_register(struct app_params *app,
+	struct pipeline_type *ptype);
+
+struct pipeline_type *app_pipeline_type_find(struct app_params *app,
+	char *name);
+
+void app_link_up_internal(struct app_params *app,
+	struct app_link_params *cp);
+
+void app_link_down_internal(struct app_params *app,
+	struct app_link_params *cp);
+
+#endif
diff --git a/examples/ip_pipeline/config.c b/examples/ip_pipeline/config.c
deleted file mode 100644
index 9414a7b..0000000
--- a/examples/ip_pipeline/config.c
+++ /dev/null
@@ -1,419 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memcpy.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_per_lcore.h>
-#include <rte_launch.h>
-#include <rte_atomic.h>
-#include <rte_cycles.h>
-#include <rte_prefetch.h>
-#include <rte_lcore.h>
-#include <rte_per_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_random.h>
-#include <rte_debug.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_ring.h>
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_ip.h>
-#include <rte_tcp.h>
-#include <rte_lpm.h>
-#include <rte_lpm6.h>
-#include <rte_string_fns.h>
-#include <rte_cfgfile.h>
-
-#include "main.h"
-
-struct app_params app;
-
-static const char usage[] =
-	"Usage: %s EAL_OPTIONS-- -p PORT_MASK [-f CONFIG_FILE]\n";
-
-void
-app_print_usage(char *prgname)
-{
-	printf(usage, prgname);
-}
-
-const char *
-app_core_type_id_to_string(enum app_core_type id)
-{
-	switch (id) {
-	case APP_CORE_NONE: return "NONE";
-	case APP_CORE_MASTER: return "MASTER";
-	case APP_CORE_RX: return "RX";
-	case APP_CORE_TX: return "TX";
-	case APP_CORE_PT: return "PT";
-	case APP_CORE_FC: return "FC";
-	case APP_CORE_FW: return "FW";
-	case APP_CORE_RT: return "RT";
-	case APP_CORE_TM: return "TM";
-	case APP_CORE_IPV4_FRAG: return "IPV4_FRAG";
-	case APP_CORE_IPV4_RAS: return "IPV4_RAS";
-	default: return NULL;
-	}
-}
-
-int
-app_core_type_string_to_id(const char *string, enum app_core_type *id)
-{
-	if (strcmp(string, "NONE") == 0) {
-		*id = APP_CORE_NONE;
-		return 0;
-	}
-	if (strcmp(string, "MASTER") == 0) {
-		*id = APP_CORE_MASTER;
-		return 0;
-	}
-	if (strcmp(string, "RX") == 0) {
-		*id = APP_CORE_RX;
-		return 0;
-	}
-	if (strcmp(string, "TX") == 0) {
-		*id = APP_CORE_TX;
-		return 0;
-	}
-	if (strcmp(string, "PT") == 0) {
-		*id = APP_CORE_PT;
-		return 0;
-	}
-	if (strcmp(string, "FC") == 0) {
-		*id = APP_CORE_FC;
-		return 0;
-	}
-	if (strcmp(string, "FW") == 0) {
-		*id = APP_CORE_FW;
-		return 0;
-	}
-	if (strcmp(string, "RT") == 0) {
-		*id = APP_CORE_RT;
-		return 0;
-	}
-	if (strcmp(string, "TM") == 0) {
-		*id = APP_CORE_TM;
-		return 0;
-	}
-	if (strcmp(string, "IPV4_FRAG") == 0) {
-		*id = APP_CORE_IPV4_FRAG;
-		return 0;
-	}
-	if (strcmp(string, "IPV4_RAS") == 0) {
-		*id = APP_CORE_IPV4_RAS;
-		return 0;
-	}
-
-	return -1;
-}
-
-static uint64_t
-app_get_core_mask(void)
-{
-	uint64_t core_mask = 0;
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		if (rte_lcore_is_enabled(i) == 0)
-			continue;
-
-		core_mask |= 1LLU << i;
-	}
-
-	return core_mask;
-}
-
-static int
-app_install_coremask(uint64_t core_mask)
-{
-	uint32_t n_cores, i;
-
-	for (n_cores = 0, i = 0; i < RTE_MAX_LCORE; i++)
-		if (app.cores[i].core_type != APP_CORE_NONE)
-			n_cores++;
-
-	if (n_cores != app.n_cores) {
-		rte_panic("Number of cores in COREMASK should be %u instead "
-			"of %u\n", n_cores, app.n_cores);
-		return -1;
-	}
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		uint32_t core_id;
-
-		if (app.cores[i].core_type == APP_CORE_NONE)
-			continue;
-
-		core_id = __builtin_ctzll(core_mask);
-		core_mask &= ~(1LLU << core_id);
-
-		app.cores[i].core_id = core_id;
-	}
-
-	return 0;
-}
-static int
-app_install_cfgfile(const char *file_name)
-{
-	struct rte_cfgfile *file;
-	uint32_t n_cores, i;
-
-	memset(app.cores, 0, sizeof(app.cores));
-
-	if (file_name[0] == '\0')
-		return -1;
-
-	file = rte_cfgfile_load(file_name, 0);
-	if (file == NULL) {
-		rte_panic("Config file %s not found\n", file_name);
-		return -1;
-	}
-
-	n_cores = (uint32_t) rte_cfgfile_num_sections(file, "core",
-		strnlen("core", 5));
-	if (n_cores < app.n_cores) {
-		rte_panic("Config file parse error: not enough cores specified "
-			"(%u cores missing)\n", app.n_cores - n_cores);
-		return -1;
-	}
-	if (n_cores > app.n_cores) {
-		rte_panic("Config file parse error: too many cores specified "
-			"(%u cores too many)\n", n_cores - app.n_cores);
-		return -1;
-	}
-
-	for (i = 0; i < n_cores; i++) {
-		struct app_core_params *p = &app.cores[i];
-		char section_name[16];
-		const char *entry;
-		uint32_t j;
-
-		/* [core X] */
-		snprintf(section_name, sizeof(section_name), "core %u", i);
-		if (!rte_cfgfile_has_section(file, section_name)) {
-			rte_panic("Config file parse error: core IDs are not "
-				"sequential (core %u missing)\n", i);
-			return -1;
-		}
-
-		/* type */
-		entry = rte_cfgfile_get_entry(file, section_name, "type");
-		if (!entry) {
-			rte_panic("Config file parse error: core %u type not "
-				"defined\n", i);
-			return -1;
-		}
-		if ((app_core_type_string_to_id(entry, &p->core_type) != 0) ||
-		    (p->core_type == APP_CORE_NONE)) {
-			rte_panic("Config file parse error: core %u type "
-				"error\n", i);
-			return -1;
-		}
-
-		/* queues in */
-		entry = rte_cfgfile_get_entry(file, section_name, "queues in");
-		if (!entry) {
-			rte_panic("Config file parse error: core %u queues in "
-				"not defined\n", i);
-			return -1;
-		}
-
-		for (j = 0; (j < APP_MAX_SWQ_PER_CORE) && (entry != NULL);
-			j++) {
-			char *next;
-
-			p->swq_in[j] =  (uint32_t) strtol(entry, &next, 10);
-			if (next == entry)
-				break;
-			entry = next;
-		}
-
-		if ((j != APP_MAX_SWQ_PER_CORE) || (*entry != '\0')) {
-			rte_panic("Config file parse error: core %u queues in "
-				"error\n", i);
-			return -1;
-		}
-
-		/* queues out */
-		entry = rte_cfgfile_get_entry(file, section_name, "queues out");
-		if (!entry) {
-			rte_panic("Config file parse error: core %u queues out "
-				"not defined\n", i);
-			return -1;
-		}
-
-		for (j = 0; (j < APP_MAX_SWQ_PER_CORE) && (entry != NULL);
-			j++) {
-			char *next;
-
-			p->swq_out[j] =  (uint32_t) strtol(entry, &next, 10);
-			if (next == entry)
-				break;
-			entry = next;
-		}
-		if ((j != APP_MAX_SWQ_PER_CORE) || (*entry != '\0')) {
-			rte_panic("Config file parse error: core %u queues out "
-				"error\n", i);
-			return -1;
-		}
-	}
-
-	rte_cfgfile_close(file);
-
-	return 0;
-}
-
-void app_cores_config_print(void)
-{
-	uint32_t i;
-
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
-		uint32_t j;
-
-		if (app.cores[i].core_type == APP_CORE_NONE)
-			continue;
-
-		printf("---> core %u: id = %u type = %6s [", i, p->core_id,
-			app_core_type_id_to_string(p->core_type));
-		for (j = 0; j < APP_MAX_SWQ_PER_CORE; j++)
-			printf("%2d ", (int) p->swq_in[j]);
-
-		printf("] [");
-		for (j = 0; j < APP_MAX_SWQ_PER_CORE; j++)
-			printf("%2d ", (int) p->swq_out[j]);
-
-		printf("]\n");
-	}
-}
-
-static int
-app_install_port_mask(const char *arg)
-{
-	char *end = NULL;
-	uint64_t port_mask;
-	uint32_t i;
-
-	if (arg[0] == '\0')
-		return -1;
-
-	port_mask = strtoul(arg, &end, 16);
-	if ((end == NULL) || (*end != '\0'))
-		return -2;
-
-	if (port_mask == 0)
-		return -3;
-
-	app.n_ports = 0;
-	for (i = 0; i < 64; i++) {
-		if ((port_mask & (1LLU << i)) == 0)
-			continue;
-
-		if (app.n_ports >= APP_MAX_PORTS)
-			return -4;
-
-		app.ports[app.n_ports] = i;
-		app.n_ports++;
-	}
-
-	if (!rte_is_power_of_2(app.n_ports))
-		return -5;
-
-	return 0;
-}
-
-int
-app_parse_args(int argc, char **argv)
-{
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	static struct option lgopts[] = {
-		{NULL, 0, 0, 0}
-	};
-	uint64_t core_mask = app_get_core_mask();
-
-	app.n_cores = __builtin_popcountll(core_mask);
-
-	argvopt = argv;
-	while ((opt = getopt_long(argc, argvopt, "p:f:", lgopts,
-			&option_index)) != EOF) {
-		switch (opt) {
-		case 'p':
-			if (app_install_port_mask(optarg) != 0)
-				rte_panic("PORT_MASK should specify a number "
-					"of ports that is power of 2 less or "
-					"equal to %u\n", APP_MAX_PORTS);
-			break;
-
-		case 'f':
-			app_install_cfgfile(optarg);
-			break;
-
-		default:
-			return -1;
-		}
-	}
-
-	app_install_coremask(core_mask);
-
-	app_cores_config_print();
-
-	if (optind >= 0)
-		argv[optind - 1] = prgname;
-
-	ret = optind - 1;
-	optind = 0; /* reset getopt lib */
-
-	return ret;
-}
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
new file mode 100644
index 0000000..361bf8a
--- /dev/null
+++ b/examples/ip_pipeline/config_parse.c
@@ -0,0 +1,2457 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_cfgfile.h>
+#include <rte_string_fns.h>
+
+#include "app.h"
+
+/**
+ * Default config values
+ **/
+
+static struct app_params app_params_default = {
+	.config_file = "./config/ip_pipeline.cfg",
+	.log_level = APP_LOG_LEVEL_HIGH,
+
+	.eal_params = {
+		.channels = 4,
+	},
+};
+
+static const struct app_mempool_params mempool_params_default = {
+	.parsed = 0,
+	.buffer_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM,
+	.pool_size = 32 * 1024,
+	.cache_size = 256,
+	.cpu_socket_id = 0,
+};
+
+static const struct app_link_params link_params_default = {
+	.parsed = 0,
+	.pmd_id = 0,
+	.arp_q = 0,
+	.tcp_syn_local_q = 0,
+	.ip_local_q = 0,
+	.tcp_local_q = 0,
+	.udp_local_q = 0,
+	.sctp_local_q = 0,
+	.state = 0,
+	.ip = 0,
+	.depth = 0,
+	.mac_addr = 0,
+
+	.conf = {
+		.link_speed = 0,
+		.link_duplex = 0,
+		.rxmode = {
+			.mq_mode = ETH_MQ_RX_NONE,
+
+			.header_split   = 0, /* Header split */
+			.hw_ip_checksum = 0, /* IP checksum offload */
+			.hw_vlan_filter = 0, /* VLAN filtering */
+			.hw_vlan_strip  = 0, /* VLAN strip */
+			.hw_vlan_extend = 0, /* Extended VLAN */
+			.jumbo_frame    = 0, /* Jumbo frame support */
+			.hw_strip_crc   = 0, /* CRC strip by HW */
+			.enable_scatter = 0, /* Scattered packets RX handler */
+
+			.max_rx_pkt_len = 9000, /* Jumbo frame max packet len */
+			.split_hdr_size = 0, /* Header split buffer size */
+		},
+		.txmode = {
+			.mq_mode = ETH_MQ_TX_NONE,
+		},
+		.lpbk_mode = 0,
+	},
+
+	.promisc = 1,
+};
+
+static const struct app_pktq_hwq_in_params default_hwq_in_params = {
+	.parsed = 0,
+	.mempool_id = 0,
+	.size = 128,
+	.burst = 32,
+
+	.conf = {
+		.rx_thresh = {
+				.pthresh = 8,
+				.hthresh = 8,
+				.wthresh = 4,
+		},
+		.rx_free_thresh = 64,
+		.rx_drop_en = 0,
+		.rx_deferred_start = 0,
+	}
+};
+
+static const struct app_pktq_hwq_out_params default_hwq_out_params = {
+	.parsed = 0,
+	.size = 512,
+	.burst = 32,
+	.dropless = 0,
+	.n_retries = 0,
+
+	.conf = {
+		.tx_thresh = {
+			.pthresh = 36,
+			.hthresh = 0,
+			.wthresh = 0,
+		},
+		.tx_rs_thresh = 0,
+		.tx_free_thresh = 0,
+		.txq_flags = ETH_TXQ_FLAGS_NOMULTSEGS |
+			ETH_TXQ_FLAGS_NOOFFLOADS,
+		.tx_deferred_start = 0,
+	}
+};
+
+static const struct app_pktq_swq_params default_swq_params = {
+	.parsed = 0,
+	.size = 256,
+	.burst_read = 32,
+	.burst_write = 32,
+	.dropless = 0,
+	.n_retries = 0,
+	.cpu_socket_id = 0,
+};
+
+struct app_pktq_tm_params default_tm_params = {
+	.parsed = 0,
+	.file_name = "./config/tm_profile.cfg",
+	.burst_read = 64,
+	.burst_write = 32,
+};
+
+struct app_pktq_source_params default_source_params = {
+	.parsed = 0,
+	.mempool_id = 0,
+	.burst = 32,
+};
+
+struct app_pktq_sink_params default_sink_params = {
+	.parsed = 0,
+};
+
+struct app_msgq_params default_msgq_params = {
+	.parsed = 0,
+	.size = 64,
+	.cpu_socket_id = 0,
+};
+
+struct app_pipeline_params default_pipeline_params = {
+	.parsed = 0,
+	.socket_id = 0,
+	.core_id = 0,
+	.hyper_th_id = 0,
+	.n_pktq_in = 0,
+	.n_pktq_out = 0,
+	.n_msgq_in = 0,
+	.n_msgq_out = 0,
+	.timer_period = 1,
+	.n_args = 0,
+};
+
+static const char app_usage[] =
+	"Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] -p PORT_MASK "
+	"[-l LOG_LEVEL]\n"
+	"\n"
+	"Arguments:\n"
+	"\t-f CONFIG_FILE: Default config file is %s\n"
+	"\t-p PORT_MASK: Mask of NIC port IDs in hexadecimal format\n"
+	"\t-s SCRIPT_FILE: No CLI script file is run when not specified\n"
+	"\t-l LOG_LEVEL: 0 = NONE, 1 = HIGH PRIO (default), 2 = LOW PRIO\n"
+	"\n";
+
+static void
+app_print_usage(char *prgname)
+{
+	rte_exit(0, app_usage, prgname, app_params_default.config_file);
+}
+
+#define skip_white_spaces(pos)			\
+({						\
+	__typeof__(pos) _p = (pos);		\
+	for ( ; isspace(*_p); _p++);		\
+	_p;					\
+})
+
+#define PARSER_IMPLICIT_PARAM_ADD_CHECK(result, section_name)		\
+do {									\
+	APP_CHECK((result != -EINVAL),					\
+		"CFG: [%s] name too long", section_name);		\
+	APP_CHECK(result != -ENOMEM,					\
+		"CFG: [%s] too much sections", section_name);		\
+	APP_CHECK(result >= 0,						\
+		"CFG: [%s] Unknown error while adding '%s'",		\
+		section_name, section_name);				\
+} while (0)
+
+#define PARSER_PARAM_ADD_CHECK(result, params_array, section_name)	\
+do {									\
+	APP_CHECK((result != -EINVAL),					\
+		"CFG: [%s] name too long", section_name);		\
+	APP_CHECK((result != -ENOMEM),					\
+		"CFG: [%s] too much sections", section_name);		\
+	APP_CHECK(((result >= 0) && (params_array)[result].parsed == 0),\
+		"CFG: [%s] duplicate section", section_name);		\
+	APP_CHECK((result >= 0),					\
+		"CFG: [%s] Unknown error while adding '%s'",		\
+		section_name, section_name);				\
+} while (0)
+
+static int
+parser_read_arg_bool(const char *p)
+{
+	p = skip_white_spaces(p);
+	int result = -EINVAL;
+
+	if (((p[0] == 'y') && (p[1] == 'e') && (p[2] == 's')) ||
+		((p[0] == 'Y') && (p[1] == 'E') && (p[2] == 'S'))) {
+		p += 3;
+		result = 1;
+	}
+
+	if (((p[0] == 'o') && (p[1] == 'n')) ||
+		((p[0] == 'O') && (p[1] == 'N'))) {
+		p += 2;
+		result = 1;
+	}
+
+	if (((p[0] == 'n') && (p[1] == 'o')) ||
+		((p[0] == 'N') && (p[1] == 'O'))) {
+		p += 2;
+		result = 0;
+	}
+
+	if (((p[0] == 'o') && (p[1] == 'f') && (p[2] == 'f')) ||
+		((p[0] == 'O') && (p[1] == 'F') && (p[2] == 'F'))) {
+		p += 3;
+		result = 0;
+	}
+
+	p = skip_white_spaces(p);
+
+	if (p[0] != '\0')
+		return -EINVAL;
+
+	return result;
+}
+
+#define PARSE_ERROR(exp, section, entry)				\
+APP_CHECK(exp, "Parse error in section \"%s\": entry \"%s\"\n", section, entry)
+
+#define PARSE_ERROR_MALLOC(exp)						\
+APP_CHECK(exp, "Parse error: no free memory\n")
+
+#define PARSE_ERROR_SECTION(exp, section)				\
+APP_CHECK(exp, "Parse error in section \"%s\"", section)
+
+#define PARSE_ERROR_SECTION_NO_ENTRIES(exp, section)			\
+APP_CHECK(exp, "Parse error in section \"%s\": no entries\n", section)
+
+#define PARSE_WARNING_IGNORED(exp, section, entry)			\
+do									\
+if (!(exp))								\
+	fprintf(stderr, "Parse warning in section \"%s\": "		\
+		"entry \"%s\" is ignored\n", section, entry);		\
+while (0)
+
+#define PARSE_ERROR_INVALID(exp, section, entry)			\
+APP_CHECK(exp, "Parse error in section \"%s\": unrecognized entry \"%s\"\n",\
+	section, entry)
+
+#define PARSE_ERROR_DUPLICATE(exp, section, entry)			\
+APP_CHECK(exp, "Parse error in section \"%s\": duplicate entry \"%s\"\n",\
+	section, entry)
+
+static int
+parser_read_uint64(uint64_t *value, const char *p)
+{
+	char *next;
+	uint64_t val;
+
+	p = skip_white_spaces(p);
+	if (!isdigit(*p))
+		return -EINVAL;
+
+	val = strtoul(p, &next, 10);
+	if (p == next)
+		return -EINVAL;
+
+	p = next;
+	switch (*p) {
+	case 'T':
+		val *= 1024ULL;
+		/* fall trought */
+	case 'G':
+		val *= 1024ULL;
+		/* fall trought */
+	case 'M':
+		val *= 1024ULL;
+		/* fall trought */
+	case 'k':
+	case 'K':
+		val *= 1024ULL;
+		p++;
+		break;
+	}
+
+	p = skip_white_spaces(p);
+	if (*p != '\0')
+		return -EINVAL;
+
+	*value = val;
+	return 0;
+}
+
+static int
+parser_read_uint32(uint32_t *value, const char *p)
+{
+	uint64_t val = 0;
+	int ret = parser_read_uint64(&val, p);
+
+	if (ret < 0)
+		return ret;
+	else if (val > UINT32_MAX)
+		return -ERANGE;
+
+	*value = val;
+	return 0;
+}
+
+static int
+parse_pipeline_core(uint32_t *socket,
+	uint32_t *core,
+	uint32_t *ht,
+	const char *entry)
+{
+	size_t num_len;
+	char num[8];
+
+	uint32_t s = 0, c = 0, h = 0, val;
+	uint8_t s_parsed = 0, c_parsed = 0, h_parsed = 0;
+	const char *next = skip_white_spaces(entry);
+	char type;
+
+	/* Expect <CORE> or [sX][cY][h]. At least one parameter is required. */
+	while (*next != '\0') {
+		/* If everything parsed nothing should left */
+		if (s_parsed && c_parsed && h_parsed)
+			return -EINVAL;
+
+		type = *next;
+		switch (type) {
+		case 's':
+		case 'S':
+			if (s_parsed)
+				return -EINVAL;
+			s_parsed = 1;
+			next++;
+			break;
+		case 'c':
+		case 'C':
+			if (c_parsed)
+				return -EINVAL;
+			c_parsed = 1;
+			next++;
+			break;
+		case 'h':
+		case 'H':
+			if (h_parsed)
+				return -EINVAL;
+			h_parsed = 1;
+			next++;
+			break;
+		default:
+			/* If it start from digit it must be only core id. */
+			if (!isdigit(*next) || s_parsed || c_parsed || h_parsed)
+				return -EINVAL;
+
+			type = 'C';
+		}
+
+		for (num_len = 0; *next != '\0'; next++, num_len++) {
+			if (num_len == RTE_DIM(num))
+				return -EINVAL;
+
+			if (!isdigit(*next))
+				break;
+
+			num[num_len] = *next;
+		}
+
+		if (num_len == 0 && type != 'h')
+			return -EINVAL;
+
+		num[num_len] = '\0';
+		val = strtol(num, NULL, 10);
+
+		h = 0;
+		switch (type) {
+		case 's':
+		case 'S':
+			s = val;
+			break;
+		case 'c':
+		case 'C':
+			c = val;
+			if (type == 'C' && *next != '\0')
+				return -EINVAL;
+
+			break;
+		case 'h':
+		case 'H':
+			h = 1;
+			break;
+		}
+	}
+
+	*socket = s;
+	*core = c;
+	*ht = h;
+	return 0;
+}
+
+static size_t
+skip_digits(const char *src)
+{
+	size_t i;
+
+	for (i = 0; isdigit(src[i]); i++);
+
+	return i;
+}
+
+static int
+validate_name(const char *name, const char *prefix, int num)
+{
+	size_t i, j;
+
+	for (i = 0; (name[i] != '\0') && (prefix[i] != '\0'); i++) {
+		if (name[i] != prefix[i])
+			return -1;
+	}
+
+	if (prefix[i] != '\0')
+		return -1;
+
+	if (!num) {
+		if (name[i] != '\0')
+			return -1;
+		else
+			return 0;
+	}
+
+	if (num == 2) {
+		j = skip_digits(&name[i]);
+		i += j;
+		if ((j == 0) || (name[i] != '.'))
+			return -1;
+		i++;
+	}
+
+	if (num == 1) {
+		j = skip_digits(&name[i]);
+		i += j;
+		if ((j == 0) || (name[i] != '\0'))
+			return -1;
+	}
+
+	return 0;
+}
+
+static void
+parse_eal(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_eal_params *p = &app->eal_params;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, i;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *entry = &entries[i];
+
+		/* coremask */
+		if (strcmp(entry->name, "c") == 0) {
+			PARSE_WARNING_IGNORED(0, section_name, entry->name);
+			continue;
+		}
+
+		/* corelist */
+		if (strcmp(entry->name, "l") == 0) {
+			PARSE_WARNING_IGNORED(0, section_name, entry->name);
+			continue;
+		}
+
+		/* coremap */
+		if (strcmp(entry->name, "lcores") == 0) {
+			PARSE_ERROR_DUPLICATE((p->coremap == NULL),
+				section_name,
+				entry->name);
+			p->coremap = strdup(entry->value);
+			continue;
+		}
+
+		/* master_lcore */
+		if (strcmp(entry->name, "master_lcore") == 0) {
+			int status;
+
+			PARSE_ERROR_DUPLICATE((p->master_lcore_present == 0),
+				section_name,
+				entry->name);
+			p->master_lcore_present = 1;
+
+			status = parser_read_uint32(&p->master_lcore,
+				entry->value);
+			PARSE_ERROR((status == 0), section_name, entry->name);
+			continue;
+		}
+
+		/* channels */
+		if (strcmp(entry->name, "n") == 0) {
+			int status;
+
+			PARSE_ERROR_DUPLICATE((p->channels_present == 0),
+				section_name,
+				entry->name);
+			p->channels_present = 1;
+
+			status = parser_read_uint32(&p->channels, entry->value);
+			PARSE_ERROR((status == 0), section_name, entry->name);
+			continue;
+		}
+
+		/* memory */
+		if (strcmp(entry->name, "m") == 0) {
+			int status;
+
+			PARSE_ERROR_DUPLICATE((p->memory_present == 0),
+				section_name,
+				entry->name);
+			p->memory_present = 1;
+
+			status = parser_read_uint32(&p->memory, entry->value);
+			PARSE_ERROR((status == 0), section_name, entry->name);
+			continue;
+		}
+
+		/* ranks */
+		if (strcmp(entry->name, "r") == 0) {
+			int status;
+
+			PARSE_ERROR_DUPLICATE((p->ranks_present == 0),
+				section_name,
+				entry->name);
+			p->ranks_present = 1;
+
+			status = parser_read_uint32(&p->ranks, entry->value);
+			PARSE_ERROR((status == 0), section_name, entry->name);
+			continue;
+		}
+
+		/* pci_blacklist */
+		if ((strcmp(entry->name, "pci_blacklist") == 0) ||
+			(strcmp(entry->name, "b") == 0)) {
+			PARSE_ERROR_DUPLICATE((p->pci_blacklist == NULL),
+				section_name,
+				entry->name);
+			p->pci_blacklist = strdup(entry->value);
+			continue;
+		}
+
+		/* pci_whitelist */
+		if ((strcmp(entry->name, "pci_whitelist") == 0) ||
+			(strcmp(entry->name, "w") == 0)) {
+			PARSE_ERROR_DUPLICATE((p->pci_whitelist == NULL),
+				section_name,
+				entry->name);
+			p->pci_whitelist = strdup(entry->value);
+			continue;
+		}
+
+		/* vdev */
+		if (strcmp(entry->name, "vdev") == 0) {
+			PARSE_ERROR_DUPLICATE((p->vdev == NULL),
+				section_name,
+				entry->name);
+			p->vdev = strdup(entry->value);
+			continue;
+		}
+
+		/* vmware_tsc_map */
+		if (strcmp(entry->name, "vmware_tsc_map") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->vmware_tsc_map_present == 0),
+				section_name,
+				entry->name);
+			p->vmware_tsc_map_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->vmware_tsc_map = val;
+			continue;
+		}
+
+		/* proc_type */
+		if (strcmp(entry->name, "proc_type") == 0) {
+			PARSE_ERROR_DUPLICATE((p->proc_type == NULL),
+				section_name,
+				entry->name);
+			p->proc_type = strdup(entry->value);
+			continue;
+		}
+
+		/* syslog */
+		if (strcmp(entry->name, "syslog") == 0) {
+			PARSE_ERROR_DUPLICATE((p->syslog == NULL),
+				section_name,
+				entry->name);
+			p->syslog = strdup(entry->value);
+			continue;
+		}
+
+		/* log_level */
+		if (strcmp(entry->name, "log_level") == 0) {
+			int status;
+
+			PARSE_ERROR_DUPLICATE((p->log_level_present == 0),
+				section_name,
+				entry->name);
+			p->log_level_present = 1;
+
+			status = parser_read_uint32(&p->log_level,
+				entry->value);
+			PARSE_ERROR((status == 0), section_name, entry->name);
+			continue;
+		}
+
+		/* version */
+		if (strcmp(entry->name, "v") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->version_present == 0),
+				section_name,
+				entry->name);
+			p->version_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->version = val;
+			continue;
+		}
+
+		/* help */
+		if ((strcmp(entry->name, "help") == 0) ||
+			(strcmp(entry->name, "h") == 0)) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->help_present == 0),
+				section_name,
+				entry->name);
+			p->help_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->help = val;
+			continue;
+		}
+
+		/* no_huge */
+		if (strcmp(entry->name, "no_huge") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->no_huge_present == 0),
+				section_name,
+				entry->name);
+			p->no_huge_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->no_huge = val;
+			continue;
+		}
+
+		/* no_pci */
+		if (strcmp(entry->name, "no_pci") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->no_pci_present == 0),
+				section_name,
+				entry->name);
+			p->no_pci_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->no_pci = val;
+			continue;
+		}
+
+		/* no_hpet */
+		if (strcmp(entry->name, "no_hpet") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->no_hpet_present == 0),
+				section_name,
+				entry->name);
+			p->no_hpet_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->no_hpet = val;
+			continue;
+		}
+
+		/* no_shconf */
+		if (strcmp(entry->name, "no_shconf") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->no_shconf_present == 0),
+				section_name,
+				entry->name);
+			p->no_shconf_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->no_shconf = val;
+			continue;
+		}
+
+		/* add_driver */
+		if (strcmp(entry->name, "d") == 0) {
+			PARSE_ERROR_DUPLICATE((p->add_driver == NULL),
+				section_name,
+				entry->name);
+			p->add_driver = strdup(entry->value);
+			continue;
+		}
+
+		/* socket_mem */
+		if (strcmp(entry->name, "socket_mem") == 0) {
+			PARSE_ERROR_DUPLICATE((p->socket_mem == NULL),
+				section_name,
+				entry->name);
+			p->socket_mem = strdup(entry->value);
+			continue;
+		}
+
+		/* huge_dir */
+		if (strcmp(entry->name, "huge_dir") == 0) {
+			PARSE_ERROR_DUPLICATE((p->huge_dir == NULL),
+				section_name,
+				entry->name);
+			p->huge_dir = strdup(entry->value);
+			continue;
+		}
+
+		/* file_prefix */
+		if (strcmp(entry->name, "file_prefix") == 0) {
+			PARSE_ERROR_DUPLICATE((p->file_prefix == NULL),
+				section_name,
+				entry->name);
+			p->file_prefix = strdup(entry->value);
+			continue;
+		}
+
+		/* base_virtaddr */
+		if (strcmp(entry->name, "base_virtaddr") == 0) {
+			PARSE_ERROR_DUPLICATE((p->base_virtaddr == NULL),
+				section_name,
+				entry->name);
+			p->base_virtaddr = strdup(entry->value);
+			continue;
+		}
+
+		/* create_uio_dev */
+		if (strcmp(entry->name, "create_uio_dev") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->create_uio_dev_present == 0),
+				section_name,
+				entry->name);
+			p->create_uio_dev_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->create_uio_dev = val;
+			continue;
+		}
+
+		/* vfio_intr */
+		if (strcmp(entry->name, "vfio_intr") == 0) {
+			PARSE_ERROR_DUPLICATE((p->vfio_intr == NULL),
+				section_name,
+				entry->name);
+			p->vfio_intr = strdup(entry->value);
+			continue;
+		}
+
+		/* xen_dom0 */
+		if (strcmp(entry->name, "xen_dom0") == 0) {
+			int val;
+
+			PARSE_ERROR_DUPLICATE((p->xen_dom0_present == 0),
+				section_name,
+				entry->name);
+			p->xen_dom0_present = 1;
+
+			val = parser_read_arg_bool(entry->value);
+			PARSE_ERROR((val >= 0), section_name, entry->name);
+			p->xen_dom0 = val;
+			continue;
+		}
+
+		/* unrecognized */
+		PARSE_ERROR_INVALID(0, section_name, entry->name);
+	}
+
+	free(entries);
+}
+
+static int
+parse_pipeline_pktq_in(struct app_params *app,
+	struct app_pipeline_params *p,
+	const char *value)
+{
+	const char *next = value;
+	char *end;
+	char name[APP_PARAM_NAME_SIZE];
+	size_t name_len;
+
+	while (*next != '\0') {
+		enum app_pktq_in_type type;
+		int id;
+
+		end = strchr(next, ' ');
+		if (!end)
+			name_len = strlen(next);
+		else
+			name_len = end - next;
+
+		if (name_len == 0 || name_len == sizeof(name))
+			return -EINVAL;
+
+		strncpy(name, next, name_len);
+		name[name_len] = '\0';
+		next += name_len;
+		if (*next != '\0')
+			next++;
+
+		if (validate_name(name, "RXQ", 2) == 0) {
+			type = APP_PKTQ_IN_HWQ;
+			id = APP_PARAM_ADD(app->hwq_in_params, name);
+		} else if (validate_name(name, "SWQ", 1) == 0) {
+			type = APP_PKTQ_IN_SWQ;
+			id = APP_PARAM_ADD(app->swq_params, name);
+		} else if (validate_name(name, "TM", 1) == 0) {
+			type = APP_PKTQ_IN_TM;
+			id = APP_PARAM_ADD(app->tm_params, name);
+		} else if (validate_name(name, "SOURCE", 1) == 0) {
+			type = APP_PKTQ_IN_SOURCE;
+			id = APP_PARAM_ADD(app->source_params, name);
+		} else
+			return -EINVAL;
+
+		if (id < 0)
+			return id;
+
+		p->pktq_in[p->n_pktq_in].type = type;
+		p->pktq_in[p->n_pktq_in].id = (uint32_t) id;
+		p->n_pktq_in++;
+	}
+
+	return 0;
+}
+
+static int
+parse_pipeline_pktq_out(struct app_params *app,
+	struct app_pipeline_params *p,
+	const char *value)
+{
+	const char *next = value;
+	char *end;
+	char name[APP_PARAM_NAME_SIZE];
+	size_t name_len;
+
+	while (*next != '\0') {
+		enum app_pktq_out_type type;
+		int id;
+
+		end = strchr(next, ' ');
+		if (!end)
+			name_len = strlen(next);
+		else
+			name_len = end - next;
+
+		if (name_len == 0 || name_len == sizeof(name))
+			return -EINVAL;
+
+		strncpy(name, next, name_len);
+		name[name_len] = '\0';
+		next += name_len;
+		if (*next != '\0')
+			next++;
+
+		if (validate_name(name, "TXQ", 2) == 0) {
+			type = APP_PKTQ_OUT_HWQ;
+			id = APP_PARAM_ADD(app->hwq_out_params, name);
+		} else if (validate_name(name, "SWQ", 1) == 0) {
+			type = APP_PKTQ_OUT_SWQ;
+			id = APP_PARAM_ADD(app->swq_params, name);
+		} else if (validate_name(name, "TM", 1) == 0) {
+			type = APP_PKTQ_OUT_TM;
+			id = APP_PARAM_ADD(app->tm_params, name);
+		} else if (validate_name(name, "SINK", 1) == 0) {
+			type = APP_PKTQ_OUT_SINK;
+			id = APP_PARAM_ADD(app->sink_params, name);
+		} else
+			return -EINVAL;
+
+		if (id < 0)
+			return id;
+
+		p->pktq_out[p->n_pktq_out].type = type;
+		p->pktq_out[p->n_pktq_out].id = id;
+		p->n_pktq_out++;
+	}
+
+	return 0;
+}
+
+static int
+parse_pipeline_msgq_in(struct app_params *app,
+	struct app_pipeline_params *p,
+	const char *value)
+{
+	const char *next = value;
+	char *end;
+	char name[APP_PARAM_NAME_SIZE];
+	size_t name_len;
+	ssize_t idx;
+
+	while (*next != '\0') {
+		end = strchr(next, ' ');
+		if (!end)
+			name_len = strlen(next);
+		else
+			name_len = end - next;
+
+		if (name_len == 0 || name_len == sizeof(name))
+			return -EINVAL;
+
+		strncpy(name, next, name_len);
+		name[name_len] = '\0';
+		next += name_len;
+		if (*next != '\0')
+			next++;
+
+		if (validate_name(name, "MSGQ", 1) != 0)
+			return -EINVAL;
+
+		idx = APP_PARAM_ADD(app->msgq_params, name);
+		if (idx < 0)
+			return idx;
+
+		p->msgq_in[p->n_msgq_in] = idx;
+		p->n_msgq_in++;
+	}
+
+	return 0;
+}
+
+static int
+parse_pipeline_msgq_out(struct app_params *app,
+	struct app_pipeline_params *p,
+	const char *value)
+{
+	const char *next = value;
+	char *end;
+	char name[APP_PARAM_NAME_SIZE];
+	size_t name_len;
+	ssize_t idx;
+
+	while (*next != '\0') {
+		end = strchr(next, ' ');
+		if (!end)
+			name_len = strlen(next);
+		else
+			name_len = end - next;
+
+		if (name_len == 0 || name_len == sizeof(name))
+			return -EINVAL;
+
+		strncpy(name, next, name_len);
+		name[name_len] = '\0';
+		next += name_len;
+		if (*next != '\0')
+			next++;
+
+		if (validate_name(name, "MSGQ", 1) != 0)
+			return -EINVAL;
+
+		idx = APP_PARAM_ADD(app->msgq_params, name);
+		if (idx < 0)
+			return idx;
+
+		p->msgq_out[p->n_msgq_out] = idx;
+		p->n_msgq_out++;
+	}
+
+	return 0;
+}
+
+
+static void
+parse_pipeline(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	char name[CFG_NAME_LEN];
+	struct app_pipeline_params *param;
+	struct rte_cfgfile_entry *entries;
+	ssize_t param_idx;
+	int n_entries, ret, i;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->pipeline_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->pipeline_params, section_name);
+
+	param = &app->pipeline_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		if (strcmp(ent->name, "type") == 0) {
+			ret = snprintf(param->type,
+				RTE_DIM(param->type),
+				"%s",
+				ent->value);
+			if ((ret > 0) && (ret < (int)RTE_DIM(param->type)))
+				ret = 0;
+			else
+				ret = -EINVAL;
+		} else if (strcmp(ent->name, "core") == 0)
+			ret = parse_pipeline_core(&param->socket_id,
+				&param->core_id,
+				&param->hyper_th_id,
+				ent->value);
+		else if (strcmp(ent->name, "pktq_in") == 0)
+			ret = parse_pipeline_pktq_in(app, param, ent->value);
+		else if (strcmp(ent->name, "pktq_out") == 0)
+			ret = parse_pipeline_pktq_out(app, param, ent->value);
+		else if (strcmp(ent->name, "msgq_in") == 0)
+			ret = parse_pipeline_msgq_in(app, param, ent->value);
+		else if (strcmp(ent->name, "msgq_out") == 0)
+			ret = parse_pipeline_msgq_out(app, param, ent->value);
+		else if (strcmp(ent->name, "timer_period") == 0)
+			ret = parser_read_uint32(&param->timer_period,
+				ent->value);
+		else {
+			param->args_name[param->n_args] = strdup(ent->name);
+			param->args_value[param->n_args] = strdup(ent->value);
+
+			APP_CHECK((param->args_name[param->n_args] != NULL) &&
+				(param->args_value[param->n_args] != NULL),
+				"CFG: [%s] out of memory",
+				section_name);
+
+			param->n_args++;
+			ret = 0;
+		}
+
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	snprintf(name, sizeof(name), "MSGQ-REQ-%s", section_name);
+	param_idx = APP_PARAM_ADD(app->msgq_params, name);
+	PARSER_IMPLICIT_PARAM_ADD_CHECK(param_idx, name);
+	app->msgq_params[param_idx].cpu_socket_id = param->socket_id;
+	param->msgq_in[param->n_msgq_in++] = param_idx;
+
+	snprintf(name, sizeof(name), "MSGQ-RSP-%s", section_name);
+	param_idx = APP_PARAM_ADD(app->msgq_params, name);
+	PARSER_IMPLICIT_PARAM_ADD_CHECK(param_idx, name);
+	app->msgq_params[param_idx].cpu_socket_id = param->socket_id;
+	param->msgq_out[param->n_msgq_out++] = param_idx;
+
+	snprintf(name, sizeof(name), "MSGQ-REQ-CORE-s%" PRIu32 "c%" PRIu32 "%s",
+		param->socket_id,
+		param->core_id,
+		(param->hyper_th_id) ? "h" : "");
+	param_idx = APP_PARAM_ADD(app->msgq_params, name);
+	PARSER_IMPLICIT_PARAM_ADD_CHECK(param_idx, name);
+	app->msgq_params[param_idx].cpu_socket_id = param->socket_id;
+
+	snprintf(name, sizeof(name), "MSGQ-RSP-CORE-s%" PRIu32 "c%" PRIu32 "%s",
+		param->socket_id,
+		param->core_id,
+		(param->hyper_th_id) ? "h" : "");
+	param_idx = APP_PARAM_ADD(app->msgq_params, name);
+	PARSER_IMPLICIT_PARAM_ADD_CHECK(param_idx, name);
+	app->msgq_params[param_idx].cpu_socket_id = param->socket_id;
+
+	free(entries);
+}
+
+static void
+parse_mempool(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_mempool_params *param;
+	struct rte_cfgfile_entry *entries;
+	ssize_t param_idx;
+	int n_entries, ret, i;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->mempool_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->mempool_params, section_name);
+
+	param = &app->mempool_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "buffer_size") == 0)
+			ret = parser_read_uint32(&param->buffer_size,
+				ent->value);
+		else if (strcmp(ent->name, "pool_size") == 0)
+			ret = parser_read_uint32(&param->pool_size,
+				ent->value);
+		else if (strcmp(ent->name, "cache_size") == 0)
+			ret = parser_read_uint32(&param->cache_size,
+				ent->value);
+		else if (strcmp(ent->name, "cpu") == 0)
+			ret = parser_read_uint32(&param->cpu_socket_id,
+				ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_link(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_link_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->link_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->link_params, section_name);
+
+	param = &app->link_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "arp_q") == 0)
+			ret = parser_read_uint32(&param->arp_q,
+				ent->value);
+		else if (strcmp(ent->name, "tcp_syn_q") == 0)
+			ret = parser_read_uint32(&param->tcp_syn_local_q,
+				ent->value);
+		else if (strcmp(ent->name, "ip_local_q") == 0)
+			ret = parser_read_uint32(&param->ip_local_q,
+				ent->value);
+		else if (strcmp(ent->name, "tcp_local_q") == 0)
+			ret = parser_read_uint32(&param->tcp_local_q,
+				ent->value);
+		else if (strcmp(ent->name, "udp_local_q") == 0)
+			ret = parser_read_uint32(&param->udp_local_q,
+				ent->value);
+		else if (strcmp(ent->name, "sctp_local_q") == 0)
+			ret = parser_read_uint32(&param->sctp_local_q,
+				ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_rxq(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_pktq_hwq_in_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->hwq_in_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->hwq_in_params, section_name);
+
+	param = &app->hwq_in_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "mempool") == 0) {
+			int status = validate_name(ent->value, "MEMPOOL", 1);
+			ssize_t idx;
+
+			APP_CHECK((status == 0),
+				"CFG: [%s] entry '%s': invalid mempool\n",
+				section_name,
+				ent->name);
+
+			idx = APP_PARAM_ADD(app->mempool_params, ent->value);
+			PARSER_IMPLICIT_PARAM_ADD_CHECK(idx, section_name);
+			param->mempool_id = idx;
+			ret = 0;
+		} else if (strcmp(ent->name, "size") == 0)
+			ret = parser_read_uint32(&param->size,
+				ent->value);
+		else if (strcmp(ent->name, "burst") == 0)
+			ret = parser_read_uint32(&param->burst,
+				ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_txq(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_pktq_hwq_out_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->hwq_out_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->hwq_out_params, section_name);
+
+	param = &app->hwq_out_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "size") == 0)
+			ret = parser_read_uint32(&param->size, ent->value);
+		else if (strcmp(ent->name, "burst") == 0)
+			ret = parser_read_uint32(&param->burst, ent->value);
+		else if (strcmp(ent->name, "dropless") == 0) {
+			ret = parser_read_arg_bool(ent->value);
+			if (ret >= 0) {
+				param->dropless = ret;
+				ret = 0;
+			}
+		}
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_swq(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_pktq_swq_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->swq_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->swq_params, section_name);
+
+	param = &app->swq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "size") == 0)
+			ret = parser_read_uint32(&param->size,
+				ent->value);
+		else if (strcmp(ent->name, "burst_read") == 0)
+			ret = parser_read_uint32(&param->burst_read,
+				ent->value);
+		else if (strcmp(ent->name, "burst_write") == 0)
+			ret = parser_read_uint32(&param->burst_write,
+				ent->value);
+		else if (strcmp(ent->name, "dropless") == 0) {
+			ret = parser_read_arg_bool(ent->value);
+			if (ret >= 0) {
+				param->dropless = ret;
+				ret = 0;
+			}
+		} else if (strcmp(ent->name, "n_retries") == 0)
+			ret = parser_read_uint64(&param->n_retries,
+				ent->value);
+		else if (strcmp(ent->name, "cpu") == 0)
+			ret = parser_read_uint32(&param->cpu_socket_id,
+				ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_tm(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_pktq_tm_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->tm_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->tm_params, section_name);
+
+	param = &app->tm_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "cfg") == 0) {
+			param->file_name = strdup(ent->value);
+			if (param->file_name == NULL)
+				ret = -EINVAL;
+
+			ret = 0;
+		} else if (strcmp(ent->name, "burst_read") == 0)
+			ret = parser_read_uint32(&param->burst_read,
+				ent->value);
+		else if (strcmp(ent->name, "burst_write") == 0)
+			ret = parser_read_uint32(&param->burst_write,
+				ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret != -EBADF,
+			"CFG: [%s] entry '%s': TM cfg parse error '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_source(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_pktq_source_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->source_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->source_params, section_name);
+
+	param = &app->source_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "mempool") == 0) {
+			int status = validate_name(ent->value, "MEMPOOL", 1);
+			ssize_t idx;
+
+			APP_CHECK((status == 0),
+				"CFG: [%s] entry '%s': invalid mempool\n",
+					section_name,
+					ent->name);
+
+			idx = APP_PARAM_ADD(app->mempool_params, ent->value);
+			PARSER_IMPLICIT_PARAM_ADD_CHECK(idx, section_name);
+			param->mempool_id = idx;
+			ret = 0;
+		} else if (strcmp(ent->name, "burst") == 0)
+			ret = parser_read_uint32(&param->burst, ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_msgq_req_pipeline(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_msgq_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->msgq_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->msgq_params, section_name);
+
+	param = &app->msgq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "size") == 0)
+			ret = parser_read_uint32(&param->size, ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_msgq_rsp_pipeline(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_msgq_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->msgq_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->msgq_params, section_name);
+
+	param = &app->msgq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "size") == 0)
+			ret = parser_read_uint32(&param->size, ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_msgq(struct app_params *app,
+	const char *section_name,
+	struct rte_cfgfile *cfg)
+{
+	struct app_msgq_params *param;
+	struct rte_cfgfile_entry *entries;
+	int n_entries, ret, i;
+	ssize_t param_idx;
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
+	PARSE_ERROR_SECTION_NO_ENTRIES((n_entries > 0), section_name);
+
+	entries = malloc(n_entries * sizeof(struct rte_cfgfile_entry));
+	PARSE_ERROR_MALLOC(entries != NULL);
+
+	rte_cfgfile_section_entries(cfg, section_name, entries, n_entries);
+
+	param_idx = APP_PARAM_ADD(app->msgq_params, section_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->msgq_params, section_name);
+
+	param = &app->msgq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; i < n_entries; i++) {
+		struct rte_cfgfile_entry *ent = &entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "size") == 0)
+			ret = parser_read_uint32(&param->size,
+				ent->value);
+		else if (strcmp(ent->name, "cpu") == 0)
+			ret = parser_read_uint32(&param->cpu_socket_id,
+				ent->value);
+
+		APP_CHECK(ret != -ESRCH,
+			"CFG: [%s] entry '%s': unknown entry\n",
+			section_name,
+			ent->name);
+		APP_CHECK(ret == 0,
+			"CFG: [%s] entry '%s': Invalid value '%s'\n",
+			section_name,
+			ent->name,
+			ent->value);
+	}
+
+	free(entries);
+}
+
+typedef void (*config_section_load)(struct app_params *p,
+	const char *section_name,
+	struct rte_cfgfile *cfg);
+
+struct config_section {
+	const char prefix[CFG_NAME_LEN];
+	int numbers;
+	config_section_load load;
+};
+
+static const struct config_section cfg_file_scheme[] = {
+	{"EAL", 0, parse_eal},
+	{"PIPELINE", 1, parse_pipeline},
+	{"MEMPOOL", 1, parse_mempool},
+	{"LINK", 1, parse_link},
+	{"RXQ", 2, parse_rxq},
+	{"TXQ", 2, parse_txq},
+	{"SWQ", 1, parse_swq},
+	{"TM", 1, parse_tm},
+	{"SOURCE", 1, parse_source},
+	{"MSGQ-REQ-PIPELINE", 1, parse_msgq_req_pipeline},
+	{"MSGQ-RSP-PIPELINE", 1, parse_msgq_rsp_pipeline},
+	{"MSGQ", 1, parse_msgq},
+};
+
+static void
+create_implicit_mempools(struct app_params *app)
+{
+	ssize_t idx;
+
+	idx = APP_PARAM_ADD(app->mempool_params, "MEMPOOL0");
+	PARSER_IMPLICIT_PARAM_ADD_CHECK(idx, "start-up");
+}
+
+static void
+parse_port_mask(struct app_params *app, uint64_t port_mask)
+{
+	uint32_t pmd_id, link_id;
+
+	link_id = 0;
+	for (pmd_id = 0; pmd_id < RTE_MAX_ETHPORTS; pmd_id++) {
+		char name[APP_PARAM_NAME_SIZE];
+		ssize_t idx;
+
+		if ((port_mask & (1LLU << pmd_id)) == 0)
+			continue;
+
+		snprintf(name, sizeof(name), "LINK%" PRIu32, link_id);
+		idx = APP_PARAM_ADD(app->link_params, name);
+		PARSER_IMPLICIT_PARAM_ADD_CHECK(idx, name);
+
+		app->link_params[idx].pmd_id = pmd_id;
+		link_id++;
+	}
+}
+
+int
+app_config_parse(struct app_params *app, const char *file_name)
+{
+	char config_file_out[APP_FILE_NAME_SIZE];
+	struct rte_cfgfile *cfg;
+	char **section_names;
+	int i, j, sect_count;
+
+	/* Implicit mempools */
+	create_implicit_mempools(app);
+
+	/* Port mask */
+	parse_port_mask(app, app->port_mask);
+
+	/* Load application configuration file */
+	cfg = rte_cfgfile_load(file_name, 0);
+	APP_CHECK(cfg != NULL, "Unable to load config file %s", file_name);
+
+	sect_count = rte_cfgfile_num_sections(cfg, NULL, 0);
+	section_names = malloc(sect_count * sizeof(char *));
+	for (i = 0; i < sect_count; i++)
+		section_names[i] = malloc(CFG_NAME_LEN);
+
+	rte_cfgfile_sections(cfg, section_names, sect_count);
+
+	for (i = 0; i < sect_count; i++) {
+		const struct config_section *sch_s;
+		int len, cfg_name_len;
+
+		cfg_name_len = strlen(section_names[i]);
+
+		/* Find section type */
+		for (j = 0; j < (int)RTE_DIM(cfg_file_scheme); j++) {
+			sch_s = &cfg_file_scheme[j];
+			len = strlen(sch_s->prefix);
+
+			if (cfg_name_len < len)
+				continue;
+
+			/* After section name we expect only '\0' or digit or
+			 * digit dot digit, so protect against false matching,
+			 * for example: "ABC" should match section name
+			 * "ABC0.0", but it should not match section_name
+			 * "ABCDEF".
+			 */
+			if ((section_names[i][len] != '\0') &&
+				!isdigit(section_names[i][len]))
+				continue;
+
+			if (strncmp(sch_s->prefix, section_names[i], len) == 0)
+				break;
+		}
+
+		APP_CHECK(j < (int)RTE_DIM(cfg_file_scheme),
+			"Unknown section %s",
+			section_names[i]);
+
+		APP_CHECK(validate_name(section_names[i],
+			sch_s->prefix,
+			sch_s->numbers) == 0,
+			"Invalid section name '%s'",
+			section_names[i]);
+
+		sch_s->load(app, section_names[i], cfg);
+	}
+
+	for (i = 0; i < sect_count; i++)
+		free(section_names[i]);
+
+	free(section_names);
+
+	rte_cfgfile_close(cfg);
+
+	APP_PARAM_COUNT(app->mempool_params, app->n_mempools);
+	APP_PARAM_COUNT(app->link_params, app->n_links);
+	APP_PARAM_COUNT(app->hwq_in_params, app->n_pktq_hwq_in);
+	APP_PARAM_COUNT(app->hwq_out_params, app->n_pktq_hwq_out);
+	APP_PARAM_COUNT(app->swq_params, app->n_pktq_swq);
+	APP_PARAM_COUNT(app->tm_params, app->n_pktq_tm);
+	APP_PARAM_COUNT(app->source_params, app->n_pktq_source);
+	APP_PARAM_COUNT(app->sink_params, app->n_pktq_sink);
+	APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
+	APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
+
+	/* Save configuration to output file */
+	snprintf(config_file_out,
+		APP_FILE_NAME_SIZE,
+		"%s.out",
+		app->config_file);
+	app_config_save(app, config_file_out);
+
+	/* Load TM configuration files */
+	app_config_parse_tm(app);
+
+	return 0;
+}
+
+static void
+save_eal_params(struct app_params *app, FILE *f)
+{
+	struct app_eal_params *p = &app->eal_params;
+
+	fprintf(f, "[EAL]\n");
+
+	if (p->coremap)
+		fprintf(f, "%s = %s\n", "lcores", p->coremap);
+
+	if (p->master_lcore_present)
+		fprintf(f, "%s = %" PRIu32 "\n",
+			"master_lcore", p->master_lcore);
+
+	fprintf(f, "%s = %" PRIu32 "\n", "n", p->channels);
+
+	if (p->memory_present)
+		fprintf(f, "%s = %" PRIu32 "\n", "m", p->memory);
+
+	if (p->ranks_present)
+		fprintf(f, "%s = %" PRIu32 "\n", "r", p->ranks);
+
+	if (p->pci_blacklist)
+		fprintf(f, "%s = %s\n", "pci_blacklist", p->pci_blacklist);
+
+	if (p->pci_whitelist)
+		fprintf(f, "%s = %s\n", "pci_whitelist", p->pci_whitelist);
+
+	if (p->vdev)
+		fprintf(f, "%s = %s\n", "vdev", p->vdev);
+
+	if (p->vmware_tsc_map_present)
+		fprintf(f, "%s = %s\n", "vmware_tsc_map",
+			(p->vmware_tsc_map) ? "yes" : "no");
+
+	if (p->proc_type)
+		fprintf(f, "%s = %s\n", "proc_type", p->proc_type);
+
+	if (p->syslog)
+		fprintf(f, "%s = %s\n", "syslog", p->syslog);
+
+	if (p->log_level_present)
+		fprintf(f, "%s = %" PRIu32 "\n", "log_level", p->log_level);
+
+	if (p->version_present)
+		fprintf(f, "%s = %s\n",	"v", (p->version) ? "yes" : "no");
+
+	if (p->help_present)
+		fprintf(f, "%s = %s\n",	"help", (p->help) ? "yes" : "no");
+
+	if (p->no_huge_present)
+		fprintf(f, "%s = %s\n",	"no_huge", (p->no_huge) ? "yes" : "no");
+
+	if (p->no_pci_present)
+		fprintf(f, "%s = %s\n",	"no_pci", (p->no_pci) ? "yes" : "no");
+
+	if (p->no_hpet_present)
+		fprintf(f, "%s = %s\n",	"no_hpet", (p->no_hpet) ? "yes" : "no");
+
+	if (p->no_shconf_present)
+		fprintf(f, "%s = %s\n", "no_shconf",
+			(p->no_shconf) ? "yes" : "no");
+
+	if (p->add_driver)
+		fprintf(f, "%s = %s\n",	"d", p->add_driver);
+
+	if (p->socket_mem)
+		fprintf(f, "%s = %s\n",	"socket_mem", p->socket_mem);
+
+	if (p->huge_dir)
+		fprintf(f, "%s = %s\n", "huge_dir", p->huge_dir);
+
+	if (p->file_prefix)
+		fprintf(f, "%s = %s\n", "file_prefix", p->file_prefix);
+
+	if (p->base_virtaddr)
+		fprintf(f, "%s = %s\n",	"base_virtaddr", p->base_virtaddr);
+
+	if (p->create_uio_dev_present)
+		fprintf(f, "%s = %s\n", "create_uio_dev",
+			(p->create_uio_dev) ? "yes" : "no");
+
+	if (p->vfio_intr)
+		fprintf(f, "%s = %s\n", "vfio_intr", p->vfio_intr);
+
+	if (p->xen_dom0_present)
+		fprintf(f, "%s = %s\n", "xen_dom0",
+			(p->xen_dom0) ? "yes" : "no");
+
+	fputc('\n', f);
+}
+
+static void
+save_mempool_params(struct app_params *app, FILE *f)
+{
+	struct app_mempool_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->mempool_params);
+	for (i = 0; i < count; i++) {
+		p = &app->mempool_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "%s = %" PRIu32 "\n", "buffer_size", p->buffer_size);
+		fprintf(f, "%s = %" PRIu32 "\n", "pool_size", p->pool_size);
+		fprintf(f, "%s = %" PRIu32 "\n", "cache_size", p->cache_size);
+		fprintf(f, "%s = %" PRIu32 "\n", "cpu", p->cpu_socket_id);
+
+		fputc('\n', f);
+	}
+}
+
+static void
+save_links_params(struct app_params *app, FILE *f)
+{
+	struct app_link_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->link_params);
+	for (i = 0; i < count; i++) {
+		p = &app->link_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "; %s = %" PRIu32 "\n", "pmd_id", p->pmd_id);
+		fprintf(f, "%s = %" PRIu32 "\n", "arp_q", p->arp_q);
+		fprintf(f, "%s = %" PRIu32 "\n", "tcp_syn_local_q",
+			p->tcp_syn_local_q);
+		fprintf(f, "%s = %" PRIu32 "\n", "ip_local_q", p->ip_local_q);
+		fprintf(f, "%s = %" PRIu32 "\n", "tcp_local_q", p->tcp_local_q);
+		fprintf(f, "%s = %" PRIu32 "\n", "udp_local_q", p->udp_local_q);
+		fprintf(f, "%s = %" PRIu32 "\n", "sctp_local_q",
+			p->sctp_local_q);
+
+		fputc('\n', f);
+	}
+}
+
+static void
+save_rxq_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_hwq_in_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->hwq_in_params);
+	for (i = 0; i < count; i++) {
+		p = &app->hwq_in_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "%s = %s\n",
+			"mempool",
+			app->mempool_params[p->mempool_id].name);
+		fprintf(f, "%s = %" PRIu32 "\n", "size", p->size);
+		fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
+
+		fputc('\n', f);
+	}
+}
+
+static void
+save_txq_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_hwq_out_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->hwq_out_params);
+	for (i = 0; i < count; i++) {
+		p = &app->hwq_out_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "%s = %" PRIu32 "\n", "size", p->size);
+		fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
+		fprintf(f, "%s = %s\n",
+			"dropless",
+			p->dropless ? "yes" : "no");
+
+		fputc('\n', f);
+	}
+}
+
+static void
+save_swq_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_swq_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->swq_params);
+	for (i = 0; i < count; i++) {
+		p = &app->swq_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "%s = %" PRIu32 "\n", "size", p->size);
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p->burst_read);
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p->burst_write);
+		fprintf(f, "%s = %s\n", "dropless", p->dropless ? "yes" : "no");
+		fprintf(f, "%s = %" PRIu64 "\n", "n_retries", p->n_retries);
+		fprintf(f, "%s = %" PRIu32 "\n", "cpu", p->cpu_socket_id);
+
+		fputc('\n', f);
+	}
+}
+
+static void
+save_tm_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_tm_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->tm_params);
+	for (i = 0; i < count; i++) {
+		p = &app->tm_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "%s = %s\n", "cfg", p->file_name);
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_read", p->burst_read);
+		fprintf(f, "%s = %" PRIu32 "\n", "burst_write", p->burst_write);
+
+		fputc('\n', f);
+	}
+}
+
+static void
+save_source_params(struct app_params *app, FILE *f)
+{
+	struct app_pktq_source_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->source_params);
+	for (i = 0; i < count; i++) {
+		p = &app->source_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "%s = %s\n",
+			"mempool",
+			app->mempool_params[p->mempool_id].name);
+		fprintf(f, "%s = %" PRIu32 "\n", "burst", p->burst);
+		fputc('\n', f);
+	}
+}
+
+static void
+save_msgq_params(struct app_params *app, FILE *f)
+{
+	struct app_msgq_params *p;
+	size_t i, count;
+
+	count = RTE_DIM(app->msgq_params);
+	for (i = 0; i < count; i++) {
+		p = &app->msgq_params[i];
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		fprintf(f, "[%s]\n", p->name);
+		fprintf(f, "%s = %" PRIu32 "\n", "size", p->size);
+		fprintf(f, "%s = %" PRIu32 "\n", "cpu", p->cpu_socket_id);
+
+		fputc('\n', f);
+	}
+}
+
+static void
+save_pipeline_params(struct app_params *app, FILE *f)
+{
+	size_t i, count;
+
+	count = RTE_DIM(app->pipeline_params);
+	for (i = 0; i < count; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		/* section name */
+		fprintf(f, "[%s]\n", p->name);
+
+		/* type */
+		fprintf(f, "type = %s\n", p->type);
+
+		/* core */
+		fprintf(f, "core = s%" PRIu32 "c%" PRIu32 "%s\n",
+			p->socket_id,
+			p->core_id,
+			(p->hyper_th_id) ? "h" : "");
+
+		/* pktq_in */
+		if (p->n_pktq_in) {
+			uint32_t j;
+
+			fprintf(f, "pktq_in =");
+			for (j = 0; j < p->n_pktq_in; j++) {
+				struct app_pktq_in_params *pp = &p->pktq_in[j];
+				char *name;
+
+				switch (pp->type) {
+				case APP_PKTQ_IN_HWQ:
+					name = app->hwq_in_params[pp->id].name;
+					break;
+				case APP_PKTQ_IN_SWQ:
+					name = app->swq_params[pp->id].name;
+					break;
+				case APP_PKTQ_IN_TM:
+					name = app->tm_params[pp->id].name;
+					break;
+				case APP_PKTQ_IN_SOURCE:
+					name = app->source_params[pp->id].name;
+					break;
+				default:
+					APP_CHECK(0, "Error\n");
+				}
+
+				fprintf(f, " %s", name);
+			}
+			fprintf(f, "\n");
+		}
+
+		/* pktq_in */
+		if (p->n_pktq_out) {
+			uint32_t j;
+
+			fprintf(f, "pktq_out =");
+			for (j = 0; j < p->n_pktq_out; j++) {
+				struct app_pktq_out_params *pp =
+					&p->pktq_out[j];
+				char *name;
+
+				switch (pp->type) {
+				case APP_PKTQ_OUT_HWQ:
+					name = app->hwq_out_params[pp->id].name;
+					break;
+				case APP_PKTQ_OUT_SWQ:
+					name = app->swq_params[pp->id].name;
+					break;
+				case APP_PKTQ_OUT_TM:
+					name = app->tm_params[pp->id].name;
+					break;
+				case APP_PKTQ_OUT_SINK:
+					name = app->sink_params[pp->id].name;
+					break;
+				default:
+					APP_CHECK(0, "Error\n");
+				}
+
+				fprintf(f, " %s", name);
+			}
+			fprintf(f, "\n");
+		}
+
+		/* msgq_in */
+		if (p->n_msgq_in) {
+			uint32_t j;
+
+			fprintf(f, "msgq_in =");
+			for (j = 0; j < p->n_msgq_in; j++) {
+				uint32_t id = p->msgq_in[j];
+				char *name = app->msgq_params[id].name;
+
+				fprintf(f, " %s", name);
+			}
+			fprintf(f, "\n");
+		}
+
+		/* msgq_out */
+		if (p->n_msgq_in) {
+			uint32_t j;
+
+			fprintf(f, "msgq_out =");
+			for (j = 0; j < p->n_msgq_out; j++) {
+				uint32_t id = p->msgq_out[j];
+				char *name = app->msgq_params[id].name;
+
+				fprintf(f, " %s", name);
+			}
+			fprintf(f, "\n");
+		}
+
+		/* timer_period */
+		fprintf(f, "timer_period = %" PRIu32 "\n", p->timer_period);
+
+		/* args */
+		if (p->n_args) {
+			uint32_t j;
+
+			for (j = 0; j < p->n_args; j++)
+				fprintf(f, "%s = %s\n", p->args_name[j],
+					p->args_value[j]);
+		}
+
+		fprintf(f, "\n");
+	}
+}
+
+void
+app_config_save(struct app_params *app, const char *file_name)
+{
+	FILE *file;
+	char *name, *dir_name;
+	int status;
+
+	name = strdup(file_name);
+	dir_name = dirname(name);
+	status = access(dir_name, W_OK);
+	APP_CHECK((status == 0),
+		"Need write access to directory \"%s\" to save configuration\n",
+		dir_name);
+
+	file = fopen(file_name, "w");
+	APP_CHECK((file != NULL),
+		"Failed to save configuration to file \"%s\"", file_name);
+
+	save_eal_params(app, file);
+	save_pipeline_params(app, file);
+	save_mempool_params(app, file);
+	save_links_params(app, file);
+	save_rxq_params(app, file);
+	save_txq_params(app, file);
+	save_swq_params(app, file);
+	save_tm_params(app, file);
+	save_source_params(app, file);
+	save_msgq_params(app, file);
+
+	fclose(file);
+	free(name);
+}
+
+int
+app_config_init(struct app_params *app)
+{
+	size_t i;
+
+	memcpy(app, &app_params_default, sizeof(struct app_params));
+
+	for (i = 0; i < RTE_DIM(app->mempool_params); i++)
+		memcpy(&app->mempool_params[i],
+			&mempool_params_default,
+			sizeof(struct app_mempool_params));
+
+	for (i = 0; i < RTE_DIM(app->link_params); i++)
+		memcpy(&app->link_params[i],
+			&link_params_default,
+			sizeof(struct app_link_params));
+
+	for (i = 0; i < RTE_DIM(app->hwq_in_params); i++)
+		memcpy(&app->hwq_in_params[i],
+			&default_hwq_in_params,
+			sizeof(default_hwq_in_params));
+
+	for (i = 0; i < RTE_DIM(app->hwq_out_params); i++)
+		memcpy(&app->hwq_out_params[i],
+			&default_hwq_out_params,
+			sizeof(default_hwq_out_params));
+
+	for (i = 0; i < RTE_DIM(app->swq_params); i++)
+		memcpy(&app->swq_params[i],
+			&default_swq_params,
+			sizeof(default_swq_params));
+
+	for (i = 0; i < RTE_DIM(app->tm_params); i++)
+		memcpy(&app->tm_params[i],
+			&default_tm_params,
+			sizeof(default_tm_params));
+
+	for (i = 0; i < RTE_DIM(app->source_params); i++)
+		memcpy(&app->source_params[i],
+			&default_source_params,
+			sizeof(default_source_params));
+
+	for (i = 0; i < RTE_DIM(app->sink_params); i++)
+		memcpy(&app->sink_params[i],
+			&default_sink_params,
+			sizeof(default_sink_params));
+
+	for (i = 0; i < RTE_DIM(app->msgq_params); i++)
+		memcpy(&app->msgq_params[i],
+			&default_msgq_params,
+			sizeof(default_msgq_params));
+
+	for (i = 0; i < RTE_DIM(app->pipeline_params); i++)
+		memcpy(&app->pipeline_params[i],
+			&default_pipeline_params,
+			sizeof(default_pipeline_params));
+
+	return 0;
+}
+
+int
+app_config_args(struct app_params *app, int argc, char **argv)
+{
+	int opt;
+	int option_index, f_present, s_present, p_present, l_present;
+	int scaned = 0;
+
+	static struct option lgopts[] = {
+		{NULL, 0, 0, 0}
+	};
+
+	/* Copy application name */
+	strncpy(app->app_name, argv[0], APP_APPNAME_SIZE - 1);
+
+	f_present = 0;
+	s_present = 0;
+	p_present = 0;
+	l_present = 0;
+
+	while ((opt = getopt_long(argc, argv, "f:s:p:l:", lgopts,
+			&option_index)) != EOF)
+		switch (opt) {
+		case 'f':
+			if (f_present)
+				rte_panic("Error: Config file is provided "
+					"more than once\n");
+			f_present = 1;
+
+			if (!strlen(optarg))
+				rte_panic("Error: Config file name is null\n");
+
+			app->config_file = strdup(optarg);
+			if (app->config_file == NULL)
+				rte_panic("Error: Memory allocation failure\n");
+
+			break;
+
+		case 's':
+			if (s_present)
+				rte_panic("Error: Script file is provided "
+					"more than once\n");
+			s_present = 1;
+
+			if (!strlen(optarg))
+				rte_panic("Error: Script file name is null\n");
+
+			app->script_file = strdup(optarg);
+			if (app->script_file == NULL)
+				rte_panic("Error: Memory allocation failure\n");
+
+			break;
+
+		case 'p':
+			if (p_present)
+				rte_panic("Error: PORT_MASK is provided "
+					"more than once\n");
+			p_present = 1;
+
+			if ((sscanf(optarg, "%" SCNx64 "%n", &app->port_mask,
+				&scaned) != 1) ||
+				((size_t) scaned != strlen(optarg)))
+				rte_panic("Error: PORT_MASK is not "
+					"a hexadecimal integer\n");
+
+			if (app->port_mask == 0)
+				rte_panic("Error: PORT_MASK is null\n");
+
+			break;
+
+		case 'l':
+			if (l_present)
+				rte_panic("Error: LOG_LEVEL is provided "
+					"more than once\n");
+			l_present = 1;
+
+			if ((sscanf(optarg, "%" SCNu32 "%n", &app->log_level,
+				&scaned) != 1) ||
+				((size_t) scaned != strlen(optarg)) ||
+				(app->log_level >= APP_LOG_LEVELS))
+				rte_panic("Error: LOG_LEVEL invalid value\n");
+
+			break;
+
+		default:
+			app_print_usage(argv[0]);
+		}
+
+	optind = 0; /* reset getopt lib */
+
+	/* Check that mandatory args have been provided */
+	if (!p_present)
+		rte_panic("Error: PORT_MASK is not provided\n");
+
+	return 0;
+}
diff --git a/examples/ip_pipeline/config_parse_tm.c b/examples/ip_pipeline/config_parse_tm.c
new file mode 100644
index 0000000..cdebbdc
--- /dev/null
+++ b/examples/ip_pipeline/config_parse_tm.c
@@ -0,0 +1,448 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_cfgfile.h>
+#include <rte_string_fns.h>
+
+#include "app.h"
+
+static int
+tm_cfgfile_load_sched_port(
+	struct rte_cfgfile *file,
+	struct rte_sched_port_params *port_params)
+{
+	const char *entry;
+	int j;
+
+	entry = rte_cfgfile_get_entry(file, "port", "frame overhead");
+	if (entry)
+		port_params->frame_overhead = (uint32_t)atoi(entry);
+
+	entry = rte_cfgfile_get_entry(file, "port", "mtu");
+	if (entry)
+		port_params->mtu = (uint32_t)atoi(entry);
+
+	entry = rte_cfgfile_get_entry(file,
+		"port",
+		"number of subports per port");
+	if (entry)
+		port_params->n_subports_per_port = (uint32_t) atoi(entry);
+
+	entry = rte_cfgfile_get_entry(file,
+		"port",
+		"number of pipes per subport");
+	if (entry)
+		port_params->n_pipes_per_subport = (uint32_t) atoi(entry);
+
+	entry = rte_cfgfile_get_entry(file, "port", "queue sizes");
+	if (entry) {
+		char *next;
+
+		for (j = 0; j < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; j++) {
+			port_params->qsize[j] = (uint16_t)
+				strtol(entry, &next, 10);
+			if (next == NULL)
+				break;
+			entry = next;
+		}
+	}
+
+#ifdef RTE_SCHED_RED
+	for (j = 0; j < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; j++) {
+		char str[32];
+
+		/* Parse WRED min thresholds */
+		snprintf(str, sizeof(str), "tc %" PRId32 " wred min", j);
+		entry = rte_cfgfile_get_entry(file, "red", str);
+		if (entry) {
+			char *next;
+			int k;
+
+			/* for each packet colour (green, yellow, red) */
+			for (k = 0; k < e_RTE_METER_COLORS; k++) {
+				port_params->red_params[j][k].min_th
+					= (uint16_t)strtol(entry, &next, 10);
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+		}
+
+		/* Parse WRED max thresholds */
+		snprintf(str, sizeof(str), "tc %" PRId32 " wred max", j);
+		entry = rte_cfgfile_get_entry(file, "red", str);
+		if (entry) {
+			char *next;
+			int k;
+
+			/* for each packet colour (green, yellow, red) */
+			for (k = 0; k < e_RTE_METER_COLORS; k++) {
+				port_params->red_params[j][k].max_th
+					= (uint16_t)strtol(entry, &next, 10);
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+		}
+
+		/* Parse WRED inverse mark probabilities */
+		snprintf(str, sizeof(str), "tc %" PRId32 " wred inv prob", j);
+		entry = rte_cfgfile_get_entry(file, "red", str);
+		if (entry) {
+			char *next;
+			int k;
+
+			/* for each packet colour (green, yellow, red) */
+			for (k = 0; k < e_RTE_METER_COLORS; k++) {
+				port_params->red_params[j][k].maxp_inv
+					= (uint8_t)strtol(entry, &next, 10);
+
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+		}
+
+		/* Parse WRED EWMA filter weights */
+		snprintf(str, sizeof(str), "tc %" PRId32 " wred weight", j);
+		entry = rte_cfgfile_get_entry(file, "red", str);
+		if (entry) {
+			char *next;
+			int k;
+
+			/* for each packet colour (green, yellow, red) */
+			for (k = 0; k < e_RTE_METER_COLORS; k++) {
+				port_params->red_params[j][k].wq_log2
+					= (uint8_t)strtol(entry, &next, 10);
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+		}
+	}
+#endif /* RTE_SCHED_RED */
+
+	return 0;
+}
+
+static int
+tm_cfgfile_load_sched_pipe(
+	struct rte_cfgfile *file,
+	struct rte_sched_port_params *port_params,
+	struct rte_sched_pipe_params *pipe_params)
+{
+	int i, j;
+	char *next;
+	const char *entry;
+	int profiles;
+
+	profiles = rte_cfgfile_num_sections(file,
+		"pipe profile", sizeof("pipe profile") - 1);
+	port_params->n_pipe_profiles = profiles;
+
+	for (j = 0; j < profiles; j++) {
+		char pipe_name[32];
+
+		snprintf(pipe_name, sizeof(pipe_name),
+			"pipe profile %" PRId32, j);
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tb rate");
+		if (entry)
+			pipe_params[j].tb_rate = (uint32_t) atoi(entry);
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tb size");
+		if (entry)
+			pipe_params[j].tb_size = (uint32_t) atoi(entry);
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc period");
+		if (entry)
+			pipe_params[j].tc_period = (uint32_t) atoi(entry);
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc 0 rate");
+		if (entry)
+			pipe_params[j].tc_rate[0] = (uint32_t) atoi(entry);
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc 1 rate");
+		if (entry)
+			pipe_params[j].tc_rate[1] = (uint32_t) atoi(entry);
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc 2 rate");
+		if (entry)
+			pipe_params[j].tc_rate[2] = (uint32_t) atoi(entry);
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc 3 rate");
+		if (entry)
+			pipe_params[j].tc_rate[3] = (uint32_t) atoi(entry);
+
+#ifdef RTE_SCHED_SUBPORT_TC_OV
+		entry = rte_cfgfile_get_entry(file, pipe_name,
+			"tc 3 oversubscription weight");
+		if (entry)
+			pipe_params[j].tc_ov_weight = (uint8_t)atoi(entry);
+#endif
+
+		entry = rte_cfgfile_get_entry(file,
+			pipe_name,
+			"tc 0 wrr weights");
+		if (entry)
+			for (i = 0; i < RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS; i++) {
+				pipe_params[j].wrr_weights[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE*0 + i] =
+					(uint8_t) strtol(entry, &next, 10);
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc 1 wrr weights");
+		if (entry)
+			for (i = 0; i < RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS; i++) {
+				pipe_params[j].wrr_weights[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE*1 + i] =
+					(uint8_t) strtol(entry, &next, 10);
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc 2 wrr weights");
+		if (entry)
+			for (i = 0; i < RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS; i++) {
+				pipe_params[j].wrr_weights[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE*2 + i] =
+					(uint8_t) strtol(entry, &next, 10);
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+
+		entry = rte_cfgfile_get_entry(file, pipe_name, "tc 3 wrr weights");
+		if (entry)
+			for (i = 0; i < RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS; i++) {
+				pipe_params[j].wrr_weights[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE*3 + i] =
+					(uint8_t) strtol(entry, &next, 10);
+				if (next == NULL)
+					break;
+				entry = next;
+			}
+	}
+	return 0;
+}
+
+static int
+tm_cfgfile_load_sched_subport(
+	struct rte_cfgfile *file,
+	struct rte_sched_subport_params *subport_params,
+	int *pipe_to_profile)
+{
+	const char *entry;
+	int i, j, k;
+
+	for (i = 0; i < APP_MAX_SCHED_SUBPORTS; i++) {
+		char sec_name[CFG_NAME_LEN];
+
+		snprintf(sec_name, sizeof(sec_name),
+			"subport %" PRId32, i);
+
+		if (rte_cfgfile_has_section(file, sec_name)) {
+			entry = rte_cfgfile_get_entry(file,
+				sec_name,
+				"tb rate");
+			if (entry)
+				subport_params[i].tb_rate =
+					(uint32_t) atoi(entry);
+
+			entry = rte_cfgfile_get_entry(file,
+				sec_name,
+				"tb size");
+			if (entry)
+				subport_params[i].tb_size =
+					(uint32_t) atoi(entry);
+
+			entry = rte_cfgfile_get_entry(file,
+				sec_name,
+				"tc period");
+			if (entry)
+				subport_params[i].tc_period =
+					(uint32_t) atoi(entry);
+
+			entry = rte_cfgfile_get_entry(file,
+				sec_name,
+				"tc 0 rate");
+			if (entry)
+				subport_params[i].tc_rate[0] =
+					(uint32_t) atoi(entry);
+
+			entry = rte_cfgfile_get_entry(file,
+				sec_name,
+				"tc 1 rate");
+			if (entry)
+				subport_params[i].tc_rate[1] =
+					(uint32_t) atoi(entry);
+
+			entry = rte_cfgfile_get_entry(file,
+				sec_name,
+				"tc 2 rate");
+			if (entry)
+				subport_params[i].tc_rate[2] =
+					(uint32_t) atoi(entry);
+
+			entry = rte_cfgfile_get_entry(file,
+				sec_name,
+				"tc 3 rate");
+			if (entry)
+				subport_params[i].tc_rate[3] =
+					(uint32_t) atoi(entry);
+
+			int n_entries = rte_cfgfile_section_num_entries(file,
+				sec_name);
+			struct rte_cfgfile_entry entries[n_entries];
+
+			rte_cfgfile_section_entries(file,
+				sec_name,
+				entries,
+				n_entries);
+
+			for (j = 0; j < n_entries; j++)
+				if (strncmp("pipe",
+					entries[j].name,
+					sizeof("pipe") - 1) == 0) {
+					int profile;
+					char *tokens[2] = {NULL, NULL};
+					int n_tokens;
+					int begin, end;
+					char name[CFG_NAME_LEN];
+
+					profile = atoi(entries[j].value);
+					strncpy(name,
+						entries[j].name,
+						sizeof(name));
+					n_tokens = rte_strsplit(
+						&name[sizeof("pipe")],
+						strnlen(name, CFG_NAME_LEN),
+							tokens, 2, '-');
+
+					begin =  atoi(tokens[0]);
+					if (n_tokens == 2)
+						end = atoi(tokens[1]);
+					else
+						end = begin;
+
+					if ((end >= APP_MAX_SCHED_PIPES) ||
+						(begin > end))
+						return -1;
+
+					for (k = begin; k <= end; k++) {
+						char profile_name[CFG_NAME_LEN];
+
+						snprintf(profile_name,
+							sizeof(profile_name),
+							"pipe profile %" PRId32,
+							profile);
+						if (rte_cfgfile_has_section(file, profile_name))
+							pipe_to_profile[i * APP_MAX_SCHED_PIPES + k] = profile;
+						else
+							rte_exit(EXIT_FAILURE,
+								"Wrong pipe profile %s\n",
+								entries[j].value);
+					}
+				}
+		}
+	}
+
+	return 0;
+}
+
+static int
+tm_cfgfile_load(struct app_pktq_tm_params *tm)
+{
+	struct rte_cfgfile *file;
+	uint32_t i;
+
+	memset(tm->sched_subport_params, 0, sizeof(tm->sched_subport_params));
+	memset(tm->sched_pipe_profiles, 0, sizeof(tm->sched_pipe_profiles));
+	memset(&tm->sched_port_params, 0, sizeof(tm->sched_pipe_profiles));
+	for (i = 0; i < APP_MAX_SCHED_SUBPORTS * APP_MAX_SCHED_PIPES; i++)
+		tm->sched_pipe_to_profile[i] = -1;
+
+	tm->sched_port_params.pipe_profiles = &tm->sched_pipe_profiles[0];
+
+	if (tm->file_name[0] == '\0')
+		return -1;
+
+	file = rte_cfgfile_load(tm->file_name, 0);
+	if (file == NULL)
+		return -1;
+
+	tm_cfgfile_load_sched_port(file,
+		&tm->sched_port_params);
+	tm_cfgfile_load_sched_subport(file,
+		tm->sched_subport_params,
+		tm->sched_pipe_to_profile);
+	tm_cfgfile_load_sched_pipe(file,
+		&tm->sched_port_params,
+		tm->sched_pipe_profiles);
+
+	rte_cfgfile_close(file);
+	return 0;
+}
+
+int
+app_config_parse_tm(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < RTE_DIM(app->tm_params); i++) {
+		struct app_pktq_tm_params *p = &app->tm_params[i];
+		int status;
+
+		if (!APP_PARAM_VALID(p))
+			break;
+
+		status = tm_cfgfile_load(p);
+		APP_CHECK(status == 0,
+			"Parse error for %s configuration file \"%s\"\n",
+			p->name,
+			p->file_name);
+	}
+
+	return 0;
+}
diff --git a/examples/ip_pipeline/cpu_core_map.c b/examples/ip_pipeline/cpu_core_map.c
new file mode 100644
index 0000000..331b946
--- /dev/null
+++ b/examples/ip_pipeline/cpu_core_map.c
@@ -0,0 +1,493 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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 <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_lcore.h>
+
+#include "cpu_core_map.h"
+
+struct cpu_core_map {
+	uint32_t n_max_sockets;
+	uint32_t n_max_cores_per_socket;
+	uint32_t n_max_ht_per_core;
+	uint32_t n_sockets;
+	uint32_t n_cores_per_socket;
+	uint32_t n_ht_per_core;
+	int map[0];
+};
+
+static inline uint32_t
+cpu_core_map_pos(struct cpu_core_map *map,
+	uint32_t socket_id,
+	uint32_t core_id,
+	uint32_t ht_id)
+{
+	return (socket_id * map->n_max_cores_per_socket + core_id) *
+		map->n_max_ht_per_core + ht_id;
+}
+
+static int
+cpu_core_map_compute_eal(struct cpu_core_map *map);
+
+static int
+cpu_core_map_compute_linux(struct cpu_core_map *map);
+
+static int
+cpu_core_map_compute_and_check(struct cpu_core_map *map);
+
+struct cpu_core_map *
+cpu_core_map_init(uint32_t n_max_sockets,
+	uint32_t n_max_cores_per_socket,
+	uint32_t n_max_ht_per_core,
+	uint32_t eal_initialized)
+{
+	uint32_t map_size, map_mem_size, i;
+	struct cpu_core_map *map;
+	int status;
+
+	/* Check input arguments */
+	if ((n_max_sockets == 0) ||
+		(n_max_cores_per_socket == 0) ||
+		(n_max_ht_per_core == 0))
+		return NULL;
+
+	/* Memory allocation */
+	map_size = n_max_sockets * n_max_cores_per_socket * n_max_ht_per_core;
+	map_mem_size = sizeof(struct cpu_core_map) + map_size * sizeof(int);
+	map = (struct cpu_core_map *) malloc(map_mem_size);
+	if (map == NULL)
+		return NULL;
+
+	/* Initialization */
+	map->n_max_sockets = n_max_sockets;
+	map->n_max_cores_per_socket = n_max_cores_per_socket;
+	map->n_max_ht_per_core = n_max_ht_per_core;
+	map->n_sockets = 0;
+	map->n_cores_per_socket = 0;
+	map->n_ht_per_core = 0;
+
+	for (i = 0; i < map_size; i++)
+		map->map[i] = -1;
+
+	status = (eal_initialized) ?
+		cpu_core_map_compute_eal(map) :
+		cpu_core_map_compute_linux(map);
+
+	if (status) {
+		free(map);
+		return NULL;
+	}
+
+	status = cpu_core_map_compute_and_check(map);
+	if (status) {
+		free(map);
+		return NULL;
+	}
+
+	return map;
+}
+
+int
+cpu_core_map_compute_eal(struct cpu_core_map *map)
+{
+	uint32_t socket_id, core_id, ht_id;
+
+	/* Compute map */
+	for (socket_id = 0; socket_id < map->n_max_sockets; socket_id++) {
+		uint32_t n_detected, core_id_contig;
+		int lcore_id;
+
+		n_detected = 0;
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			struct lcore_config *p = &lcore_config[lcore_id];
+
+			if ((p->detected) && (p->socket_id == socket_id))
+				n_detected++;
+		}
+
+		core_id_contig = 0;
+
+		for (core_id = 0; n_detected ; core_id++) {
+			ht_id = 0;
+
+			for (lcore_id = 0;
+				lcore_id < RTE_MAX_LCORE;
+				lcore_id++) {
+				struct lcore_config *p =
+					&lcore_config[lcore_id];
+
+				if ((p->detected) &&
+					(p->socket_id == socket_id) &&
+					(p->core_id == core_id)) {
+					uint32_t pos = cpu_core_map_pos(map,
+						socket_id,
+						core_id_contig,
+						ht_id);
+
+					map->map[pos] = lcore_id;
+					ht_id++;
+					n_detected--;
+				}
+			}
+
+			if (ht_id) {
+				core_id_contig++;
+				if (core_id_contig ==
+					map->n_max_cores_per_socket)
+					return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+int
+cpu_core_map_compute_and_check(struct cpu_core_map *map)
+{
+	uint32_t socket_id, core_id, ht_id;
+
+	/* Compute n_ht_per_core, n_cores_per_socket, n_sockets */
+	for (ht_id = 0; ht_id < map->n_max_ht_per_core; ht_id++) {
+		if (map->map[ht_id] == -1)
+			break;
+
+		map->n_ht_per_core++;
+	}
+
+	if (map->n_ht_per_core == 0)
+		return -1;
+
+	for (core_id = 0; core_id < map->n_max_cores_per_socket; core_id++) {
+		uint32_t pos = core_id * map->n_max_ht_per_core;
+
+		if (map->map[pos] == -1)
+			break;
+
+		map->n_cores_per_socket++;
+	}
+
+	if (map->n_cores_per_socket == 0)
+		return -1;
+
+	for (socket_id = 0; socket_id < map->n_max_sockets; socket_id++) {
+		uint32_t pos = socket_id * map->n_max_cores_per_socket *
+			map->n_max_ht_per_core;
+
+		if (map->map[pos] == -1)
+			break;
+
+		map->n_sockets++;
+	}
+
+	if (map->n_sockets == 0)
+		return -1;
+
+	/* Check that each socket has exactly the same number of cores
+	and that each core has exactly the same number of hyper-threads */
+	for (socket_id = 0; socket_id < map->n_sockets; socket_id++) {
+		for (core_id = 0; core_id < map->n_cores_per_socket; core_id++)
+			for (ht_id = 0;
+				ht_id < map->n_max_ht_per_core;
+				ht_id++) {
+				uint32_t pos = (socket_id *
+					map->n_max_cores_per_socket + core_id) *
+					map->n_max_ht_per_core + ht_id;
+
+				if (((ht_id < map->n_ht_per_core) &&
+					(map->map[pos] == -1)) ||
+					((ht_id >= map->n_ht_per_core) &&
+					(map->map[pos] != -1)))
+					return -1;
+			}
+
+		for ( ; core_id < map->n_max_cores_per_socket; core_id++)
+			for (ht_id = 0;
+				ht_id < map->n_max_ht_per_core;
+				ht_id++) {
+				uint32_t pos = cpu_core_map_pos(map,
+					socket_id,
+					core_id,
+					ht_id);
+
+				if (map->map[pos] != -1)
+					return -1;
+			}
+	}
+
+	return 0;
+}
+
+#define FILE_LINUX_CPU_N_LCORES \
+	"/sys/devices/system/cpu/present"
+
+static int
+cpu_core_map_get_n_lcores_linux(void)
+{
+	char buffer[64], *string;
+	FILE *fd;
+
+	fd = fopen(FILE_LINUX_CPU_N_LCORES, "r");
+	if (fd == NULL)
+		return -1;
+
+	if (fgets(buffer, sizeof(buffer), fd) == NULL) {
+		fclose(fd);
+		return -1;
+	}
+
+	fclose(fd);
+
+	string = index(buffer, '-');
+	if (string == NULL)
+		return -1;
+
+	return (atoi(++string) + 1);
+}
+
+#define FILE_LINUX_CPU_CORE_ID \
+	"/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_id"
+
+static int
+cpu_core_map_get_core_id_linux(int lcore_id)
+{
+	char buffer[64];
+	FILE *fd;
+	int core_id;
+
+	snprintf(buffer, sizeof(buffer), FILE_LINUX_CPU_CORE_ID, lcore_id);
+	fd = fopen(buffer, "r");
+	if (fd == NULL)
+		return -1;
+
+	if (fgets(buffer, sizeof(buffer), fd) == NULL) {
+		fclose(fd);
+		return -1;
+	}
+
+	fclose(fd);
+
+	core_id = atoi(buffer);
+	return core_id;
+}
+
+#define FILE_LINUX_CPU_SOCKET_ID \
+	"/sys/devices/system/cpu/cpu%" PRIu32 "/topology/physical_package_id"
+
+static int
+cpu_core_map_get_socket_id_linux(int lcore_id)
+{
+	char buffer[64];
+	FILE *fd;
+	int socket_id;
+
+	snprintf(buffer, sizeof(buffer), FILE_LINUX_CPU_SOCKET_ID, lcore_id);
+	fd = fopen(buffer, "r");
+	if (fd == NULL)
+		return -1;
+
+	if (fgets(buffer, sizeof(buffer), fd) == NULL) {
+		fclose(fd);
+		return -1;
+	}
+
+	fclose(fd);
+
+	socket_id = atoi(buffer);
+	return socket_id;
+}
+
+int
+cpu_core_map_compute_linux(struct cpu_core_map *map)
+{
+	uint32_t socket_id, core_id, ht_id;
+	int n_lcores;
+
+	n_lcores = cpu_core_map_get_n_lcores_linux();
+	if (n_lcores <= 0)
+		return -1;
+
+	/* Compute map */
+	for (socket_id = 0; socket_id < map->n_max_sockets; socket_id++) {
+		uint32_t n_detected, core_id_contig;
+		int lcore_id;
+
+		n_detected = 0;
+		for (lcore_id = 0; lcore_id < n_lcores; lcore_id++) {
+			int lcore_socket_id =
+				cpu_core_map_get_socket_id_linux(lcore_id);
+
+			if (lcore_socket_id < 0)
+				return -1;
+
+			if (((uint32_t) lcore_socket_id) == socket_id)
+				n_detected++;
+		}
+
+		core_id_contig = 0;
+
+		for (core_id = 0; n_detected ; core_id++) {
+			ht_id = 0;
+
+			for (lcore_id = 0; lcore_id < n_lcores; lcore_id++) {
+				int lcore_socket_id =
+					cpu_core_map_get_socket_id_linux(
+					lcore_id);
+
+				if (lcore_socket_id < 0)
+					return -1;
+
+				int lcore_core_id =
+					cpu_core_map_get_core_id_linux(
+						lcore_id);
+
+				if (lcore_core_id < 0)
+					return -1;
+
+				if (((uint32_t) lcore_socket_id == socket_id) &&
+					((uint32_t) lcore_core_id == core_id)) {
+					uint32_t pos = cpu_core_map_pos(map,
+						socket_id,
+						core_id_contig,
+						ht_id);
+
+					map->map[pos] = lcore_id;
+					ht_id++;
+					n_detected--;
+				}
+			}
+
+			if (ht_id) {
+				core_id_contig++;
+				if (core_id_contig ==
+					map->n_max_cores_per_socket)
+					return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+void
+cpu_core_map_print(struct cpu_core_map *map)
+{
+	uint32_t socket_id, core_id, ht_id;
+
+	if (map == NULL)
+		return;
+
+	for (socket_id = 0; socket_id < map->n_sockets; socket_id++) {
+		printf("Socket %" PRIu32 ":\n", socket_id);
+
+		for (core_id = 0;
+			core_id < map->n_cores_per_socket;
+			core_id++) {
+			printf("[%" PRIu32 "] = [", core_id);
+
+			for (ht_id = 0; ht_id < map->n_ht_per_core; ht_id++) {
+				int lcore_id = cpu_core_map_get_lcore_id(map,
+					socket_id,
+					core_id,
+					ht_id);
+
+				uint32_t core_id_noncontig =
+					cpu_core_map_get_core_id_linux(
+						lcore_id);
+
+				printf(" %" PRId32 " (%" PRIu32 ") ",
+					lcore_id,
+					core_id_noncontig);
+			}
+
+			printf("]\n");
+		}
+	}
+}
+
+uint32_t
+cpu_core_map_get_n_sockets(struct cpu_core_map *map)
+{
+	if (map == NULL)
+		return 0;
+
+	return map->n_sockets;
+}
+
+uint32_t
+cpu_core_map_get_n_cores_per_socket(struct cpu_core_map *map)
+{
+	if (map == NULL)
+		return 0;
+
+	return map->n_cores_per_socket;
+}
+
+uint32_t
+cpu_core_map_get_n_ht_per_core(struct cpu_core_map *map)
+{
+	if (map == NULL)
+		return 0;
+
+	return map->n_ht_per_core;
+}
+
+int
+cpu_core_map_get_lcore_id(struct cpu_core_map *map,
+	uint32_t socket_id,
+	uint32_t core_id,
+	uint32_t ht_id)
+{
+	uint32_t pos;
+
+	if ((map == NULL) ||
+		(socket_id >= map->n_sockets) ||
+		(core_id >= map->n_cores_per_socket) ||
+		(ht_id >= map->n_ht_per_core))
+		return -1;
+
+	pos = cpu_core_map_pos(map, socket_id, core_id, ht_id);
+
+	return map->map[pos];
+}
+
+void
+cpu_core_map_free(struct cpu_core_map *map)
+{
+	if (map)
+		free(map);
+}
diff --git a/examples/ip_pipeline/cpu_core_map.h b/examples/ip_pipeline/cpu_core_map.h
new file mode 100644
index 0000000..5c2ec72
--- /dev/null
+++ b/examples/ip_pipeline/cpu_core_map.h
@@ -0,0 +1,69 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_CPU_CORE_MAP_H__
+#define __INCLUDE_CPU_CORE_MAP_H__
+
+#include <stdio.h>
+
+#include <rte_lcore.h>
+
+struct cpu_core_map;
+
+struct cpu_core_map *
+cpu_core_map_init(uint32_t n_max_sockets,
+	uint32_t n_max_cores_per_socket,
+	uint32_t n_max_ht_per_core,
+	uint32_t eal_initialized);
+
+uint32_t
+cpu_core_map_get_n_sockets(struct cpu_core_map *map);
+
+uint32_t
+cpu_core_map_get_n_cores_per_socket(struct cpu_core_map *map);
+
+uint32_t
+cpu_core_map_get_n_ht_per_core(struct cpu_core_map *map);
+
+int
+cpu_core_map_get_lcore_id(struct cpu_core_map *map,
+	uint32_t socket_id,
+	uint32_t core_id,
+	uint32_t ht_id);
+
+void cpu_core_map_print(struct cpu_core_map *map);
+
+void
+cpu_core_map_free(struct cpu_core_map *map);
+
+#endif
diff --git a/examples/ip_pipeline/main.c b/examples/ip_pipeline/main.c
index eb750b6..a2d7ef0 100644
--- a/examples/ip_pipeline/main.c
+++ b/examples/ip_pipeline/main.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -31,135 +31,21 @@
  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <getopt.h>
-#include <unistd.h>
+#include "app.h"
 
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memcpy.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_per_lcore.h>
-#include <rte_launch.h>
-#include <rte_atomic.h>
-#include <rte_cycles.h>
-#include <rte_prefetch.h>
-#include <rte_lcore.h>
-#include <rte_per_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_random.h>
-#include <rte_debug.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_ring.h>
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_ip.h>
-#include <rte_tcp.h>
-#include <rte_lpm.h>
-#include <rte_lpm6.h>
-
-#include "main.h"
+static struct app_params app;
 
 int
 main(int argc, char **argv)
 {
-	int ret;
-
-	/* Init EAL */
-	ret = rte_eal_init(argc, argv);
-	if (ret < 0)
-		return -1;
-	argc -= ret;
-	argv += ret;
+	rte_openlog_stream(stderr);
 
-	/* Parse application arguments (after the EAL ones) */
-	ret = app_parse_args(argc, argv);
-	if (ret < 0) {
-		app_print_usage(argv[0]);
-		return -1;
-	}
+	/* Config */
+	app_config_init(&app);
 
-	/* Init */
-	app_init();
+	app_config_args(&app, argc, argv);
 
-	/* Launch per-lcore init on every lcore */
-	rte_eal_mp_remote_launch(app_lcore_main_loop, NULL, CALL_MASTER);
+	app_config_parse(&app, app.config_file);
 
 	return 0;
 }
-
-int
-app_lcore_main_loop(__attribute__((unused)) void *arg)
-{
-	uint32_t core_id, i;
-
-	core_id = rte_lcore_id();
-
-	for (i = 0; i < app.n_cores; i++) {
-		struct app_core_params *p = &app.cores[i];
-
-		if (p->core_id != core_id)
-			continue;
-
-		switch (p->core_type) {
-		case APP_CORE_MASTER:
-			app_ping();
-			app_main_loop_cmdline();
-			return 0;
-		case APP_CORE_RX:
-			app_main_loop_pipeline_rx();
-			/* app_main_loop_rx(); */
-			return 0;
-		case APP_CORE_TX:
-			app_main_loop_pipeline_tx();
-			/* app_main_loop_tx(); */
-			return 0;
-		case APP_CORE_PT:
-			/* app_main_loop_pipeline_passthrough(); */
-			app_main_loop_passthrough();
-			return 0;
-		case APP_CORE_FC:
-			app_main_loop_pipeline_flow_classification();
-			return 0;
-		case APP_CORE_FW:
-		case APP_CORE_RT:
-			app_main_loop_pipeline_routing();
-			return 0;
-
-#ifdef RTE_LIBRTE_ACL
-			app_main_loop_pipeline_firewall();
-			return 0;
-#else
-			rte_exit(EXIT_FAILURE, "ACL not present in build\n");
-#endif
-
-		case APP_CORE_IPV4_FRAG:
-			app_main_loop_pipeline_ipv4_frag();
-			return 0;
-		case APP_CORE_IPV4_RAS:
-			app_main_loop_pipeline_ipv4_ras();
-			return 0;
-
-		default:
-			rte_panic("%s: Invalid core type for core %u\n",
-				__func__, i);
-		}
-	}
-
-	rte_panic("%s: Algorithmic error\n", __func__);
-	return -1;
-}
diff --git a/examples/ip_pipeline/main.h b/examples/ip_pipeline/main.h
deleted file mode 100644
index 6085aaa..0000000
--- a/examples/ip_pipeline/main.h
+++ /dev/null
@@ -1,298 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _MAIN_H_
-#define _MAIN_H_
-
-#include <stdint.h>
-
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_ring.h>
-#include <rte_ethdev.h>
-
-#ifdef RTE_LIBRTE_ACL
-#include <rte_table_acl.h>
-#endif
-
-struct app_flow_key {
-	union {
-		struct {
-			uint8_t ttl; /* needs to be set to 0 */
-			uint8_t proto;
-			uint16_t header_checksum; /* needs to be set to 0 */
-			uint32_t ip_src;
-		};
-		uint64_t slab0;
-	};
-
-	union {
-		struct {
-			uint32_t ip_dst;
-			uint16_t port_src;
-			uint16_t port_dst;
-		};
-		uint64_t slab1;
-	};
-} __attribute__((__packed__));
-
-struct app_arp_key {
-	uint32_t nh_ip;
-	uint32_t nh_iface;
-} __attribute__((__packed__));
-
-struct app_pkt_metadata {
-	uint32_t signature;
-	uint8_t reserved1[28];
-
-	struct app_flow_key flow_key;
-
-	struct app_arp_key arp_key;
-	struct ether_addr nh_arp;
-
-	uint8_t reserved3[2];
-} __attribute__((__packed__));
-
-#ifndef APP_MBUF_ARRAY_SIZE
-#define APP_MBUF_ARRAY_SIZE            256
-#endif
-
-struct app_mbuf_array {
-	struct rte_mbuf *array[APP_MBUF_ARRAY_SIZE];
-	uint32_t n_mbufs;
-};
-
-#ifndef APP_MAX_PORTS
-#define APP_MAX_PORTS                  4
-#endif
-
-#ifndef APP_MAX_SWQ_PER_CORE
-#define APP_MAX_SWQ_PER_CORE           8
-#endif
-
-#define APP_SWQ_INVALID                ((uint32_t)(-1))
-
-#define APP_SWQ_IN_REQ                 (APP_MAX_SWQ_PER_CORE - 1)
-
-#define APP_SWQ_OUT_RESP               (APP_MAX_SWQ_PER_CORE - 1)
-
-enum app_core_type {
-	APP_CORE_NONE = 0, /* Unused */
-	APP_CORE_MASTER,   /* Management */
-	APP_CORE_RX,       /* Reception */
-	APP_CORE_TX,       /* Transmission */
-	APP_CORE_PT,       /* Pass-through */
-	APP_CORE_FC,       /* Flow Classification */
-	APP_CORE_FW,       /* Firewall */
-	APP_CORE_RT,       /* Routing */
-	APP_CORE_TM,       /* Traffic Management */
-	APP_CORE_IPV4_FRAG,/* IPv4 Fragmentation */
-	APP_CORE_IPV4_RAS, /* IPv4 Reassembly */
-};
-
-struct app_core_params {
-	uint32_t core_id;
-	enum app_core_type core_type;
-
-	/* SWQ map */
-	uint32_t swq_in[APP_MAX_SWQ_PER_CORE];
-	uint32_t swq_out[APP_MAX_SWQ_PER_CORE];
-} __rte_cache_aligned;
-
-struct app_params {
-	/* CPU cores */
-	struct app_core_params cores[RTE_MAX_LCORE];
-	uint32_t n_cores;
-
-	/* Ports*/
-	uint32_t ports[APP_MAX_PORTS];
-	uint32_t n_ports;
-	uint32_t rsz_hwq_rx;
-	uint32_t rsz_hwq_tx;
-	uint32_t bsz_hwq_rd;
-	uint32_t bsz_hwq_wr;
-	struct rte_eth_conf port_conf;
-	struct rte_eth_rxconf rx_conf;
-	struct rte_eth_txconf tx_conf;
-
-	/* SW Queues (SWQs) */
-	struct rte_ring **rings;
-	uint32_t rsz_swq;
-	uint32_t bsz_swq_rd;
-	uint32_t bsz_swq_wr;
-
-	/* Buffer pool */
-	struct rte_mempool *pool;
-	struct rte_mempool *indirect_pool;
-	uint32_t pool_buffer_size;
-	uint32_t pool_size;
-	uint32_t pool_cache_size;
-
-	/* Message buffer pool */
-	struct rte_mempool *msg_pool;
-	uint32_t msg_pool_buffer_size;
-	uint32_t msg_pool_size;
-	uint32_t msg_pool_cache_size;
-
-	/* Rule tables */
-	uint32_t max_arp_rules;
-	uint32_t max_routing_rules;
-	uint32_t max_firewall_rules;
-	uint32_t max_flow_rules;
-
-	/* Processing */
-	uint32_t ether_hdr_pop_push;
-} __rte_cache_aligned;
-
-extern struct app_params app;
-
-const char *app_core_type_id_to_string(enum app_core_type id);
-int app_core_type_string_to_id(const char *string, enum app_core_type *id);
-void app_cores_config_print(void);
-
-void app_check_core_params(void);
-struct app_core_params *app_get_core_params(uint32_t core_id);
-uint32_t app_get_first_core_id(enum app_core_type core_type);
-struct rte_ring *app_get_ring_req(uint32_t core_id);
-struct rte_ring *app_get_ring_resp(uint32_t core_id);
-
-int app_parse_args(int argc, char **argv);
-void app_print_usage(char *prgname);
-void app_init(void);
-void app_ping(void);
-int app_lcore_main_loop(void *arg);
-
-/* Hash functions */
-uint64_t test_hash(void *key, uint32_t key_size, uint64_t seed);
-uint32_t rte_jhash2_16(uint32_t *k, uint32_t initval);
-#if defined(__x86_64__)
-uint32_t rte_aeshash_16(uint64_t *k, uint64_t seed);
-uint32_t rte_crchash_16(uint64_t *k, uint64_t seed);
-#endif
-
-/* I/O with no pipeline */
-void app_main_loop_rx(void);
-void app_main_loop_tx(void);
-void app_main_loop_passthrough(void);
-
-/* Pipeline */
-void app_main_loop_pipeline_rx(void);
-void app_main_loop_pipeline_rx_frag(void);
-void app_main_loop_pipeline_tx(void);
-void app_main_loop_pipeline_tx_ras(void);
-void app_main_loop_pipeline_flow_classification(void);
-void app_main_loop_pipeline_firewall(void);
-void app_main_loop_pipeline_routing(void);
-void app_main_loop_pipeline_passthrough(void);
-void app_main_loop_pipeline_ipv4_frag(void);
-void app_main_loop_pipeline_ipv4_ras(void);
-
-/* Command Line Interface (CLI) */
-void app_main_loop_cmdline(void);
-
-/* Messages */
-enum app_msg_req_type {
-	APP_MSG_REQ_PING,
-	APP_MSG_REQ_FC_ADD,
-	APP_MSG_REQ_FC_DEL,
-	APP_MSG_REQ_FC_ADD_ALL,
-	APP_MSG_REQ_FW_ADD,
-	APP_MSG_REQ_FW_DEL,
-	APP_MSG_REQ_RT_ADD,
-	APP_MSG_REQ_RT_DEL,
-	APP_MSG_REQ_ARP_ADD,
-	APP_MSG_REQ_ARP_DEL,
-	APP_MSG_REQ_RX_PORT_ENABLE,
-	APP_MSG_REQ_RX_PORT_DISABLE,
-};
-
-struct app_msg_req {
-	enum app_msg_req_type type;
-	union {
-		struct {
-			uint32_t ip;
-			uint8_t depth;
-			uint8_t port;
-			uint32_t nh_ip;
-		} routing_add;
-		struct {
-			uint32_t ip;
-			uint8_t depth;
-		} routing_del;
-		struct {
-			uint8_t out_iface;
-			uint32_t nh_ip;
-			struct ether_addr nh_arp;
-		} arp_add;
-		struct {
-			uint8_t out_iface;
-			uint32_t nh_ip;
-		} arp_del;
-		struct {
-			union {
-				uint8_t key_raw[16];
-				struct app_flow_key key;
-			};
-			uint8_t port;
-		} flow_classif_add;
-		struct {
-			union {
-				uint8_t key_raw[16];
-				struct app_flow_key key;
-			};
-		} flow_classif_del;
-#ifdef RTE_LIBRTE_ACL
-		struct {
-			struct rte_table_acl_rule_add_params add_params;
-			uint8_t port;
-		} firewall_add;
-		struct {
-			struct rte_table_acl_rule_delete_params delete_params;
-		} firewall_del;
-#endif
-		struct {
-			uint8_t port;
-		} rx_up;
-		struct {
-			uint8_t port;
-		} rx_down;
-	};
-};
-
-struct app_msg_resp {
-	int result;
-};
-
-#define APP_FLUSH 0xFF
-
-#endif /* _MAIN_H_ */
diff --git a/examples/ip_pipeline/pipeline.h b/examples/ip_pipeline/pipeline.h
new file mode 100644
index 0000000..b9a56ea
--- /dev/null
+++ b/examples/ip_pipeline/pipeline.h
@@ -0,0 +1,87 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_H__
+#define __INCLUDE_PIPELINE_H__
+
+#include <cmdline_parse.h>
+
+#include "pipeline_be.h"
+
+/*
+ * Pipeline type front-end operations
+ */
+
+typedef void* (*pipeline_fe_op_init)(struct pipeline_params *params, void *arg);
+
+typedef int (*pipeline_fe_op_free)(void *pipeline);
+
+struct pipeline_fe_ops {
+	pipeline_fe_op_init f_init;
+	pipeline_fe_op_free f_free;
+	cmdline_parse_ctx_t *cmds;
+};
+
+/*
+ * Pipeline type
+ */
+
+struct pipeline_type {
+	const char *name;
+
+	/* pipeline back-end */
+	struct pipeline_be_ops *be_ops;
+
+	/* pipeline front-end */
+	struct pipeline_fe_ops *fe_ops;
+};
+
+static inline uint32_t
+pipeline_type_cmds_count(struct pipeline_type *ptype)
+{
+	cmdline_parse_ctx_t *cmds;
+	uint32_t n_cmds;
+
+	if (ptype->fe_ops == NULL)
+		return 0;
+
+	cmds = ptype->fe_ops->cmds;
+	if (cmds == NULL)
+		return 0;
+
+	for (n_cmds = 0; cmds[n_cmds]; n_cmds++);
+
+	return n_cmds;
+}
+
+#endif
diff --git a/examples/ip_pipeline/pipeline_be.h b/examples/ip_pipeline/pipeline_be.h
new file mode 100644
index 0000000..51f1e4f
--- /dev/null
+++ b/examples/ip_pipeline/pipeline_be.h
@@ -0,0 +1,256 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_BE_H__
+#define __INCLUDE_PIPELINE_BE_H__
+
+#include <rte_port_ethdev.h>
+#include <rte_port_ring.h>
+#include <rte_port_frag.h>
+#include <rte_port_ras.h>
+#include <rte_port_sched.h>
+#include <rte_port_source_sink.h>
+#include <rte_pipeline.h>
+
+enum pipeline_port_in_type {
+	PIPELINE_PORT_IN_ETHDEV_READER,
+	PIPELINE_PORT_IN_RING_READER,
+	PIPELINE_PORT_IN_RING_READER_IPV4_FRAG,
+	PIPELINE_PORT_IN_RING_READER_IPV6_FRAG,
+	PIPELINE_PORT_IN_SCHED_READER,
+	PIPELINE_PORT_IN_SOURCE,
+};
+
+struct pipeline_port_in_params {
+	enum pipeline_port_in_type type;
+	union {
+		struct rte_port_ethdev_reader_params ethdev;
+		struct rte_port_ring_reader_params ring;
+		struct rte_port_ring_reader_ipv4_frag_params ring_ipv4_frag;
+		struct rte_port_ring_reader_ipv6_frag_params ring_ipv6_frag;
+		struct rte_port_sched_reader_params sched;
+		struct rte_port_source_params source;
+	} params;
+	uint32_t burst_size;
+};
+
+static inline void *
+pipeline_port_in_params_convert(struct pipeline_port_in_params  *p)
+{
+	switch (p->type) {
+	case PIPELINE_PORT_IN_ETHDEV_READER:
+		return (void *) &p->params.ethdev;
+	case PIPELINE_PORT_IN_RING_READER:
+		return (void *) &p->params.ring;
+	case PIPELINE_PORT_IN_RING_READER_IPV4_FRAG:
+		return (void *) &p->params.ring_ipv4_frag;
+	case PIPELINE_PORT_IN_RING_READER_IPV6_FRAG:
+		return (void *) &p->params.ring_ipv6_frag;
+	case PIPELINE_PORT_IN_SCHED_READER:
+		return (void *) &p->params.sched;
+	case PIPELINE_PORT_IN_SOURCE:
+		return (void *) &p->params.source;
+	default:
+		return NULL;
+	}
+}
+
+static inline struct rte_port_in_ops *
+pipeline_port_in_params_get_ops(struct pipeline_port_in_params  *p)
+{
+	switch (p->type) {
+	case PIPELINE_PORT_IN_ETHDEV_READER:
+		return &rte_port_ethdev_reader_ops;
+	case PIPELINE_PORT_IN_RING_READER:
+		return &rte_port_ring_reader_ops;
+	case PIPELINE_PORT_IN_RING_READER_IPV4_FRAG:
+		return &rte_port_ring_reader_ipv4_frag_ops;
+	case PIPELINE_PORT_IN_RING_READER_IPV6_FRAG:
+		return &rte_port_ring_reader_ipv6_frag_ops;
+	case PIPELINE_PORT_IN_SCHED_READER:
+		return &rte_port_sched_reader_ops;
+	case PIPELINE_PORT_IN_SOURCE:
+		return &rte_port_source_ops;
+	default:
+		return NULL;
+	}
+}
+
+enum pipeline_port_out_type {
+	PIPELINE_PORT_OUT_ETHDEV_WRITER,
+	PIPELINE_PORT_OUT_ETHDEV_WRITER_NODROP,
+	PIPELINE_PORT_OUT_RING_WRITER,
+	PIPELINE_PORT_OUT_RING_WRITER_NODROP,
+	PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS,
+	PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS,
+	PIPELINE_PORT_OUT_SCHED_WRITER,
+	PIPELINE_PORT_OUT_SINK,
+};
+
+struct pipeline_port_out_params {
+	enum pipeline_port_out_type type;
+	union {
+		struct rte_port_ethdev_writer_params ethdev;
+		struct rte_port_ethdev_writer_nodrop_params ethdev_nodrop;
+		struct rte_port_ring_writer_params ring;
+		struct rte_port_ring_writer_nodrop_params ring_nodrop;
+		struct rte_port_ring_writer_ipv4_ras_params ring_ipv4_ras;
+		struct rte_port_ring_writer_ipv6_ras_params ring_ipv6_ras;
+		struct rte_port_sched_writer_params sched;
+	} params;
+};
+
+static inline void *
+pipeline_port_out_params_convert(struct pipeline_port_out_params  *p)
+{
+	switch (p->type) {
+	case PIPELINE_PORT_OUT_ETHDEV_WRITER:
+		return (void *) &p->params.ethdev;
+	case PIPELINE_PORT_OUT_ETHDEV_WRITER_NODROP:
+		return (void *) &p->params.ethdev_nodrop;
+	case PIPELINE_PORT_OUT_RING_WRITER:
+		return (void *) &p->params.ring;
+	case PIPELINE_PORT_OUT_RING_WRITER_NODROP:
+		return (void *) &p->params.ring_nodrop;
+	case PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS:
+		return (void *) &p->params.ring_ipv4_ras;
+	case PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS:
+		return (void *) &p->params.ring_ipv6_ras;
+	case PIPELINE_PORT_OUT_SCHED_WRITER:
+		return (void *) &p->params.sched;
+	case PIPELINE_PORT_OUT_SINK:
+	default:
+		return NULL;
+	}
+}
+
+static inline void *
+pipeline_port_out_params_get_ops(struct pipeline_port_out_params  *p)
+{
+	switch (p->type) {
+	case PIPELINE_PORT_OUT_ETHDEV_WRITER:
+		return &rte_port_ethdev_writer_ops;
+	case PIPELINE_PORT_OUT_ETHDEV_WRITER_NODROP:
+		return &rte_port_ethdev_writer_nodrop_ops;
+	case PIPELINE_PORT_OUT_RING_WRITER:
+		return &rte_port_ring_writer_ops;
+	case PIPELINE_PORT_OUT_RING_WRITER_NODROP:
+		return &rte_port_ring_writer_nodrop_ops;
+	case PIPELINE_PORT_OUT_RING_WRITER_IPV4_RAS:
+		return &rte_port_ring_writer_ipv4_ras_ops;
+	case PIPELINE_PORT_OUT_RING_WRITER_IPV6_RAS:
+		return &rte_port_ring_writer_ipv6_ras_ops;
+	case PIPELINE_PORT_OUT_SCHED_WRITER:
+		return &rte_port_sched_writer_ops;
+	case PIPELINE_PORT_OUT_SINK:
+		return &rte_port_sink_ops;
+	default:
+		return NULL;
+	}
+}
+
+#ifndef PIPELINE_NAME_SIZE
+#define PIPELINE_NAME_SIZE                       32
+#endif
+
+#ifndef PIPELINE_MAX_PORT_IN
+#define PIPELINE_MAX_PORT_IN                     16
+#endif
+
+#ifndef PIPELINE_MAX_PORT_OUT
+#define PIPELINE_MAX_PORT_OUT                    16
+#endif
+
+#ifndef PIPELINE_MAX_TABLES
+#define PIPELINE_MAX_TABLES                      16
+#endif
+
+#ifndef PIPELINE_MAX_MSGQ_IN
+#define PIPELINE_MAX_MSGQ_IN                     16
+#endif
+
+#ifndef PIPELINE_MAX_MSGQ_OUT
+#define PIPELINE_MAX_MSGQ_OUT                    16
+#endif
+
+#ifndef PIPELINE_MAX_ARGS
+#define PIPELINE_MAX_ARGS                        32
+#endif
+
+struct pipeline_params {
+	char name[PIPELINE_NAME_SIZE];
+
+	struct pipeline_port_in_params port_in[PIPELINE_MAX_PORT_IN];
+	struct pipeline_port_out_params port_out[PIPELINE_MAX_PORT_OUT];
+	struct rte_ring *msgq_in[PIPELINE_MAX_MSGQ_IN];
+	struct rte_ring *msgq_out[PIPELINE_MAX_MSGQ_OUT];
+
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+	uint32_t n_msgq;
+
+	int socket_id;
+
+	char *args_name[PIPELINE_MAX_ARGS];
+	char *args_value[PIPELINE_MAX_ARGS];
+	uint32_t n_args;
+
+	uint32_t log_level;
+};
+
+/*
+ * Pipeline type back-end operations
+ */
+
+typedef void* (*pipeline_be_op_init)(struct pipeline_params *params,
+	void *arg);
+
+typedef int (*pipeline_be_op_free)(void *pipeline);
+
+typedef int (*pipeline_be_op_run)(void *pipeline);
+
+typedef int (*pipeline_be_op_timer)(void *pipeline);
+
+typedef int (*pipeline_be_op_track)(void *pipeline,
+	uint32_t port_in,
+	uint32_t *port_out);
+
+struct pipeline_be_ops {
+	pipeline_be_op_init f_init;
+	pipeline_be_op_free f_free;
+	pipeline_be_op_run f_run;
+	pipeline_be_op_timer f_timer;
+	pipeline_be_op_track f_track;
+};
+
+#endif
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 02/11] ip_pipeline: added config checks
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 01/11] ip_pipeline: add parsing for config files with new syntax Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 03/11] ip_pipeline: modified init to match new params struct Maciej Gajdzica
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

From: Jasvinder Singh <jasvinder.singh@intel.com>

After loading configuration from a file, data integrity is checked.

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
---
 examples/ip_pipeline/Makefile       |    1 +
 examples/ip_pipeline/config_check.c |  399 +++++++++++++++++++++++++++++++++++
 examples/ip_pipeline/main.c         |    2 +
 3 files changed, 402 insertions(+)
 create mode 100644 examples/ip_pipeline/config_check.c

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index b0feb4f..bc50e71 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -45,6 +45,7 @@ APP = ip_pipeline
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse_tm.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_check.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
 
 CFLAGS += -O3
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/config_check.c
new file mode 100644
index 0000000..07f4c8b
--- /dev/null
+++ b/examples/ip_pipeline/config_check.c
@@ -0,0 +1,399 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "app.h"
+
+static void
+check_mempools(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_mempools; i++) {
+		struct app_mempool_params *p = &app->mempool_params[i];
+
+		APP_CHECK((p->pool_size > 0),
+			"Mempool %s size is 0\n", p->name);
+
+		APP_CHECK((p->cache_size > 0),
+			"Mempool %s cache size is 0\n", p->name);
+
+		APP_CHECK(rte_is_power_of_2(p->cache_size),
+			"Mempool %s cache size not a power of 2\n", p->name);
+	}
+}
+
+static void
+check_links(struct app_params *app)
+{
+	uint32_t n_links_port_mask = __builtin_popcountll(app->port_mask);
+	uint32_t i;
+
+	/* Check that number of links matches the port mask */
+	APP_CHECK((app->n_links == n_links_port_mask),
+		"Not enough links provided in the PORT_MASK\n");
+
+	for (i = 0; i < app->n_links; i++) {
+		struct app_link_params *link = &app->link_params[i];
+		uint32_t rxq_max, n_rxq, n_txq, link_id, i;
+
+		APP_PARAM_GET_ID(link, "LINK", link_id);
+
+		/* Check that link RXQs are contiguous */
+		rxq_max = 0;
+		if (link->arp_q > rxq_max)
+			rxq_max = link->arp_q;
+		if (link->tcp_syn_local_q > rxq_max)
+			rxq_max = link->tcp_syn_local_q;
+		if (link->ip_local_q > rxq_max)
+			rxq_max = link->ip_local_q;
+		if (link->tcp_local_q > rxq_max)
+			rxq_max = link->tcp_local_q;
+		if (link->udp_local_q > rxq_max)
+			rxq_max = link->udp_local_q;
+		if (link->sctp_local_q > rxq_max)
+			rxq_max = link->sctp_local_q;
+
+		for (i = 1; i <= rxq_max; i++)
+			APP_CHECK(((link->arp_q == i) ||
+				(link->tcp_syn_local_q == i) ||
+				(link->ip_local_q == i) ||
+				(link->tcp_local_q == i) ||
+				(link->udp_local_q == i) ||
+				(link->sctp_local_q == i)),
+				"%s RXQs are not contiguous (A)\n", link->name);
+
+		n_rxq = app_link_get_n_rxq(app, link);
+
+		APP_CHECK((n_rxq == rxq_max + 1),
+			"%s RXQs are not contiguous (B)\n", link->name);
+
+		for (i = 0; i < n_rxq; i++) {
+			char name[APP_PARAM_NAME_SIZE];
+			int pos;
+
+			sprintf(name, "RXQ%" PRIu32 ".%" PRIu32,
+				link_id, i);
+			pos = APP_PARAM_FIND(app->hwq_in_params, name);
+			APP_CHECK((pos >= 0),
+				"%s RXQs are not contiguous (C)\n", link->name);
+		}
+
+		/* Check that link RXQs are contiguous */
+		n_txq = app_link_get_n_txq(app, link);
+
+		for (i = 0; i < n_txq; i++) {
+			char name[APP_PARAM_NAME_SIZE];
+			int pos;
+
+			sprintf(name, "TXQ%" PRIu32 ".%" PRIu32,
+				link_id, i);
+			pos = APP_PARAM_FIND(app->hwq_out_params, name);
+			APP_CHECK((pos >= 0),
+				"%s TXQs are not contiguous\n", link->name);
+		}
+	}
+}
+
+static void
+check_rxqs(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_hwq_in; i++) {
+		struct app_pktq_hwq_in_params *p = &app->hwq_in_params[i];
+		uint32_t n_readers = app_rxq_get_readers(app, p);
+
+		APP_CHECK((p->size > 0),
+			"%s size is 0\n", p->name);
+
+		APP_CHECK((rte_is_power_of_2(p->size)),
+			"%s size is not a power of 2\n", p->name);
+
+		APP_CHECK((p->burst > 0),
+			"%s burst size is 0\n", p->name);
+
+		APP_CHECK((p->burst <= p->size),
+			"%s burst size is bigger than its size\n", p->name);
+
+		APP_CHECK((n_readers != 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers == 1),
+			"%s has more than one reader\n", p->name);
+	}
+}
+
+static void
+check_txqs(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_hwq_out; i++) {
+		struct app_pktq_hwq_out_params *p = &app->hwq_out_params[i];
+		uint32_t n_writers = app_txq_get_writers(app, p);
+
+		APP_CHECK((p->size > 0),
+			"%s size is 0\n", p->name);
+
+		APP_CHECK((rte_is_power_of_2(p->size)),
+			"%s size is not a power of 2\n", p->name);
+
+		APP_CHECK((p->burst > 0),
+			"%s burst size is 0\n", p->name);
+
+		APP_CHECK((p->burst <= p->size),
+			"%s burst size is bigger than its size\n", p->name);
+
+		APP_CHECK((n_writers != 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers == 1),
+			"%s has more than one writer\n", p->name);
+	}
+}
+
+static void
+check_swqs(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_swq; i++) {
+		struct app_pktq_swq_params *p = &app->swq_params[i];
+		uint32_t n_readers = app_swq_get_readers(app, p);
+		uint32_t n_writers = app_swq_get_writers(app, p);
+
+		APP_CHECK((p->size > 0),
+			"%s size is 0\n", p->name);
+
+		APP_CHECK((rte_is_power_of_2(p->size)),
+			"%s size is not a power of 2\n", p->name);
+
+		APP_CHECK((p->burst_read > 0),
+			"%s read burst size is 0\n", p->name);
+
+		APP_CHECK((p->burst_read <= p->size),
+			"%s read burst size is bigger than its size\n",
+			p->name);
+
+		APP_CHECK((p->burst_write > 0),
+			"%s write burst size is 0\n", p->name);
+
+		APP_CHECK((p->burst_write <= p->size),
+			"%s write burst size is bigger than its size\n",
+			p->name);
+
+		APP_CHECK((n_readers != 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers == 1),
+			"%s has more than one reader\n", p->name);
+
+		APP_CHECK((n_writers != 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers == 1),
+			"%s has more than one writer\n", p->name);
+	}
+}
+
+static void
+check_tms(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_tm; i++) {
+		struct app_pktq_tm_params *p = &app->tm_params[i];
+		uint32_t n_readers = app_tm_get_readers(app, p);
+		uint32_t n_writers = app_tm_get_writers(app, p);
+
+		APP_CHECK((n_readers != 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers == 1),
+			"%s has more than one reader\n", p->name);
+
+		APP_CHECK((n_writers != 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers == 1),
+			"%s has more than one writer\n", p->name);
+	}
+}
+
+static void
+check_sources(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_source; i++) {
+		struct app_pktq_source_params *p = &app->source_params[i];
+		uint32_t n_readers = app_source_get_readers(app, p);
+
+		APP_CHECK((n_readers != 0),
+			"%s has no reader\n", p->name);
+
+		APP_CHECK((n_readers == 1),
+			"%s has more than one reader\n", p->name);
+	}
+}
+
+static void
+check_sinks(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_sink; i++) {
+		struct app_pktq_sink_params *p = &app->sink_params[i];
+		uint32_t n_writers = app_sink_get_writers(app, p);
+
+		APP_CHECK((n_writers != 0),
+			"%s has no writer\n", p->name);
+
+		APP_CHECK((n_writers == 1),
+			"%s has more than one writer\n", p->name);
+	}
+}
+
+static void
+check_msgqs(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_msgq; i++) {
+		struct app_msgq_params *p = &app->msgq_params[i];
+		uint32_t n_readers = app_msgq_get_readers(app, p);
+		uint32_t n_writers = app_msgq_get_writers(app, p);
+		uint32_t msgq_req_pipeline, msgq_rsp_pipeline;
+		uint32_t msgq_req_core, msgq_rsp_core;
+
+		APP_CHECK((p->size > 0),
+			"%s size is 0\n", p->name);
+
+		APP_CHECK((rte_is_power_of_2(p->size)),
+			"%s size is not a power of 2\n", p->name);
+
+		msgq_req_pipeline = (strncmp(p->name, "MSGQ-REQ-PIPELINE",
+			strlen("MSGQ-REQ-PIPELINE")) == 0);
+
+		msgq_rsp_pipeline = (strncmp(p->name, "MSGQ-RSP-PIPELINE",
+			strlen("MSGQ-RSP-PIPELINE")) == 0);
+
+		msgq_req_core = (strncmp(p->name, "MSGQ-REQ-CORE",
+			strlen("MSGQ-REQ-CORE")) == 0);
+
+		msgq_rsp_core = (strncmp(p->name, "MSGQ-RSP-CORE",
+			strlen("MSGQ-RSP-CORE")) == 0);
+
+		if ((msgq_req_pipeline == 0) &&
+			(msgq_rsp_pipeline == 0) &&
+			(msgq_req_core == 0) &&
+			(msgq_rsp_core == 0)) {
+			APP_CHECK((n_readers != 0),
+				"%s has no reader\n", p->name);
+
+			APP_CHECK((n_readers == 1),
+				"%s has more than one reader\n", p->name);
+
+			APP_CHECK((n_writers != 0),
+				"%s has no writer\n", p->name);
+
+			APP_CHECK((n_writers == 1),
+				"%s has more than one writer\n", p->name);
+		}
+
+		if (msgq_req_pipeline) {
+			struct app_pipeline_params *pipeline;
+			uint32_t pipeline_id;
+
+			APP_PARAM_GET_ID(p, "MSGQ-REQ-PIPELINE", pipeline_id);
+
+			APP_PARAM_FIND_BY_ID(app->pipeline_params,
+				"PIPELINE",
+				pipeline_id,
+				pipeline);
+
+			APP_CHECK((pipeline != NULL),
+				"%s is not associated with a valid pipeline\n",
+				p->name);
+		}
+
+		if (msgq_rsp_pipeline) {
+			struct app_pipeline_params *pipeline;
+			uint32_t pipeline_id;
+
+			APP_PARAM_GET_ID(p, "MSGQ-RSP-PIPELINE", pipeline_id);
+
+			APP_PARAM_FIND_BY_ID(app->pipeline_params,
+				"PIPELINE",
+				pipeline_id,
+				pipeline);
+
+			APP_CHECK((pipeline != NULL),
+				"%s is not associated with a valid pipeline\n",
+				p->name);
+		}
+	}
+}
+
+static void
+check_pipelines(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+
+		APP_CHECK((p->n_msgq_in == p->n_msgq_out),
+			"%s number of input MSGQs does not match "
+			"the number of output MSGQs\n", p->name);
+	}
+}
+
+int
+app_config_check(struct app_params *app)
+{
+	check_mempools(app);
+	check_links(app);
+	check_rxqs(app);
+	check_txqs(app);
+	check_swqs(app);
+	check_tms(app);
+	check_sources(app);
+	check_sinks(app);
+	check_msgqs(app);
+	check_pipelines(app);
+
+	return 0;
+}
diff --git a/examples/ip_pipeline/main.c b/examples/ip_pipeline/main.c
index a2d7ef0..612eea9 100644
--- a/examples/ip_pipeline/main.c
+++ b/examples/ip_pipeline/main.c
@@ -47,5 +47,7 @@ main(int argc, char **argv)
 
 	app_config_parse(&app, app.config_file);
 
+	app_config_check(&app);
+
 	return 0;
 }
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 03/11] ip_pipeline: modified init to match new params struct
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 01/11] ip_pipeline: add parsing for config files with new syntax Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 02/11] ip_pipeline: added config checks Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 04/11] ip_pipeline: moved pipelines to separate folder Maciej Gajdzica
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

After changes in config parser, app params struct is changed and
requires modifications in initialization procedures.

Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
---
 examples/ip_pipeline/Makefile |    1 +
 examples/ip_pipeline/init.c   | 1643 ++++++++++++++++++++++++++++++-----------
 examples/ip_pipeline/main.c   |    3 +
 3 files changed, 1216 insertions(+), 431 deletions(-)

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index bc50e71..59bea5b 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -46,6 +46,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse_tm.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_check.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
 
 CFLAGS += -O3
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index d79762f..01e13d2 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -31,562 +31,1343 @@
  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
 #include <inttypes.h>
-#include <sys/types.h>
+#include <stdio.h>
 #include <string.h>
-#include <sys/queue.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memcpy.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_per_lcore.h>
-#include <rte_launch.h>
-#include <rte_atomic.h>
+
 #include <rte_cycles.h>
-#include <rte_prefetch.h>
-#include <rte_lcore.h>
-#include <rte_per_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_random.h>
-#include <rte_debug.h>
-#include <rte_ether.h>
 #include <rte_ethdev.h>
-#include <rte_ring.h>
-#include <rte_mempool.h>
-#include <rte_malloc.h>
-#include <rte_mbuf.h>
-#include <rte_string_fns.h>
+#include <rte_ether.h>
 #include <rte_ip.h>
-#include <rte_tcp.h>
-#include <rte_lpm.h>
-#include <rte_lpm6.h>
-
-#include "main.h"
-
-#define NA                             APP_SWQ_INVALID
-
-struct app_params app = {
-	/* CPU cores */
-	.cores = {
-	{0, APP_CORE_MASTER, {15, 16, 17, NA, NA, NA, NA, NA},
-		{12, 13, 14, NA, NA, NA, NA, NA} },
-	{0, APP_CORE_RX,     {NA, NA, NA, NA, NA, NA, NA, 12},
-		{ 0,  1,  2,  3, NA, NA, NA, 15} },
-	{0, APP_CORE_FC,     { 0,  1,  2,  3, NA, NA, NA, 13},
-		{ 4,  5,  6,  7, NA, NA, NA, 16} },
-	{0, APP_CORE_RT,     { 4,  5,  6,  7, NA, NA, NA, 14},
-		{ 8,  9, 10, 11, NA, NA, NA, 17} },
-	{0, APP_CORE_TX,     { 8,  9, 10, 11, NA, NA, NA, NA},
-		{NA, NA, NA, NA, NA, NA, NA, NA} },
-	},
-
-	/* Ports*/
-	.n_ports = APP_MAX_PORTS,
-	.rsz_hwq_rx = 128,
-	.rsz_hwq_tx = 512,
-	.bsz_hwq_rd = 64,
-	.bsz_hwq_wr = 64,
-
-	.port_conf = {
-		.rxmode = {
-			.split_hdr_size = 0,
-			.header_split   = 0, /* Header Split disabled */
-			.hw_ip_checksum = 1, /* IP checksum offload enabled */
-			.hw_vlan_filter = 0, /* VLAN filtering disabled */
-			.jumbo_frame    = 1, /* Jumbo Frame Support enabled */
-			.max_rx_pkt_len = 9000, /* Jumbo Frame MAC pkt length */
-			.hw_strip_crc   = 0, /* CRC stripped by hardware */
-		},
-		.rx_adv_conf = {
-			.rss_conf = {
-				.rss_key = NULL,
-				.rss_hf = ETH_RSS_IP,
-			},
-		},
-		.txmode = {
-			.mq_mode = ETH_MQ_TX_NONE,
-		},
-	},
-
-	.rx_conf = {
-		.rx_thresh = {
-			.pthresh = 8,
-			.hthresh = 8,
-			.wthresh = 4,
-		},
-		.rx_free_thresh = 64,
-		.rx_drop_en = 0,
-	},
-
-	.tx_conf = {
-		.tx_thresh = {
-			.pthresh = 36,
-			.hthresh = 0,
-			.wthresh = 0,
-		},
-		.tx_free_thresh = 0,
-		.tx_rs_thresh = 0,
-	},
-
-	/* SWQs */
-	.rsz_swq = 128,
-	.bsz_swq_rd = 64,
-	.bsz_swq_wr = 64,
-
-	/* Buffer pool */
-	.pool_buffer_size = RTE_MBUF_DEFAULT_BUF_SIZE,
-	.pool_size = 32 * 1024,
-	.pool_cache_size = 256,
-
-	/* Message buffer pool */
-	.msg_pool_buffer_size = 256,
-	.msg_pool_size = 1024,
-	.msg_pool_cache_size = 64,
-
-	/* Rule tables */
-	.max_arp_rules = 1 << 10,
-	.max_firewall_rules = 1 << 5,
-	.max_routing_rules = 1 << 24,
-	.max_flow_rules = 1 << 24,
-
-	/* Application processing */
-	.ether_hdr_pop_push = 0,
-};
-
-struct app_core_params *
-app_get_core_params(uint32_t core_id)
-{
-	uint32_t i;
+#include <rte_eal.h>
+#include <rte_malloc.h>
 
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
+#include "app.h"
+#include "pipeline.h"
 
-		if (p->core_id != core_id)
-			continue;
+#define APP_NAME_SIZE	32
 
-		return p;
-	}
+static void
+app_init_core_map(struct app_params *app)
+{
+	APP_LOG(app, HIGH, "Initializing CPU core map ...");
+	app->core_map = cpu_core_map_init(4, 32, 4, 0);
 
-	return NULL;
+	if (app->core_map == NULL)
+		rte_panic("Cannot create CPU core map\n");
+
+	if (app->log_level >= APP_LOG_LEVEL_LOW)
+		cpu_core_map_print(app->core_map);
 }
 
-static uint32_t
-app_get_n_swq_in(void)
+static void
+app_init_core_mask(struct app_params *app)
 {
-	uint32_t max_swq_id = 0, i, j;
+	uint64_t mask = 0;
+	uint32_t i;
 
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
+	for (i = 0; i < app->n_pipelines; i++) {
+		struct app_pipeline_params *p = &app->pipeline_params[i];
+		int lcore_id;
 
-		if (p->core_type == APP_CORE_NONE)
-			continue;
+		lcore_id = cpu_core_map_get_lcore_id(app->core_map,
+			p->socket_id,
+			p->core_id,
+			p->hyper_th_id);
 
-		for (j = 0; j < APP_MAX_SWQ_PER_CORE; j++) {
-			uint32_t swq_id = p->swq_in[j];
+		if (lcore_id < 0)
+			rte_panic("Cannot create CPU core mask\n");
 
-			if ((swq_id != APP_SWQ_INVALID) &&
-				(swq_id > max_swq_id))
-				max_swq_id = swq_id;
-		}
+		mask |= 1LLU << lcore_id;
 	}
 
-	return (1 + max_swq_id);
+	app->core_mask = mask;
+	APP_LOG(app, HIGH, "CPU core mask = 0x%016" PRIx64, app->core_mask);
 }
 
-static uint32_t
-app_get_n_swq_out(void)
+static void
+app_init_eal(struct app_params *app)
 {
-	uint32_t max_swq_id = 0, i, j;
+	char buffer[32];
+	struct app_eal_params *p = &app->eal_params;
+	uint32_t n_args = 0;
+	int status;
 
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
+	app->eal_argv[n_args++] = strdup(app->app_name);
 
-		if (p->core_type == APP_CORE_NONE)
-			continue;
+	snprintf(buffer, sizeof(buffer), "-c%" PRIx64, app->core_mask);
+	app->eal_argv[n_args++] = strdup(buffer);
 
-		for (j = 0; j < APP_MAX_SWQ_PER_CORE; j++) {
-			uint32_t swq_id = p->swq_out[j];
+	if (p->coremap) {
+		snprintf(buffer, sizeof(buffer), "--lcores=%s", p->coremap);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-			if ((swq_id != APP_SWQ_INVALID) &&
-				(swq_id > max_swq_id))
-				max_swq_id = swq_id;
-		}
+	if (p->master_lcore_present) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--master-lcore=%" PRIu32,
+			p->master_lcore);
+		app->eal_argv[n_args++] = strdup(buffer);
 	}
 
-	return (1 + max_swq_id);
-}
+	snprintf(buffer, sizeof(buffer), "-n%" PRIu32, p->channels);
+	app->eal_argv[n_args++] = strdup(buffer);
 
-static uint32_t
-app_get_swq_in_count(uint32_t swq_id)
-{
-	uint32_t n, i;
+	if (p->memory_present) {
+		snprintf(buffer, sizeof(buffer), "-m%" PRIu32, p->memory);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-	for (n = 0, i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
-		uint32_t j;
+	if (p->ranks_present) {
+		snprintf(buffer, sizeof(buffer), "-r%" PRIu32, p->ranks);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		if (p->core_type == APP_CORE_NONE)
-			continue;
+	if (p->pci_blacklist) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--pci-blacklist=%s",
+			p->pci_blacklist);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		for (j = 0; j < APP_MAX_SWQ_PER_CORE; j++)
-			if (p->swq_in[j] == swq_id)
-				n++;
+	if (p->pci_whitelist) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--pci-whitelist=%s",
+			p->pci_whitelist);
+		app->eal_argv[n_args++] = strdup(buffer);
 	}
 
-	return n;
-}
+	if (p->vdev) {
+		snprintf(buffer, sizeof(buffer), "--vdev=%s", p->vdev);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-static uint32_t
-app_get_swq_out_count(uint32_t swq_id)
-{
-	uint32_t n, i;
+	if ((p->vmware_tsc_map_present) && p->vmware_tsc_map) {
+		snprintf(buffer, sizeof(buffer), "--vmware-tsc-map");
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-	for (n = 0, i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
-		uint32_t j;
+	if (p->proc_type) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--proc-type=%s",
+			p->proc_type);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		if (p->core_type == APP_CORE_NONE)
-			continue;
+	if (p->syslog) {
+		snprintf(buffer, sizeof(buffer), "--syslog=%s", p->syslog);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		for (j = 0; j < APP_MAX_SWQ_PER_CORE; j++)
-			if (p->swq_out[j] == swq_id)
-				n++;
+	if (p->log_level_present) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--log-level=%" PRIu32,
+			p->log_level);
+		app->eal_argv[n_args++] = strdup(buffer);
 	}
 
-	return n;
-}
+	if ((p->version_present) && p->version) {
+		snprintf(buffer, sizeof(buffer), "-v");
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-void
-app_check_core_params(void)
-{
-	uint32_t n_swq_in = app_get_n_swq_in();
-	uint32_t n_swq_out = app_get_n_swq_out();
-	uint32_t i;
+	if ((p->help_present) && p->help) {
+		snprintf(buffer, sizeof(buffer), "--help");
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-	/* Check that range of SW queues is contiguous and each SW queue has
-	   exactly one reader and one writer */
-	if (n_swq_in != n_swq_out)
-		rte_panic("Number of input SW queues is not equal to the "
-			"number of output SW queues\n");
+	if ((p->no_huge_present) && p->no_huge) {
+		snprintf(buffer, sizeof(buffer), "--no-huge");
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-	for (i = 0; i < n_swq_in; i++) {
-		uint32_t n = app_get_swq_in_count(i);
+	if ((p->no_pci_present) && p->no_pci) {
+		snprintf(buffer, sizeof(buffer), "--no-pci");
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		if (n == 0)
-			rte_panic("SW queue %u has no reader\n", i);
+	if ((p->no_hpet_present) && p->no_hpet) {
+		snprintf(buffer, sizeof(buffer), "--no-hpet");
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		if (n > 1)
-			rte_panic("SW queue %u has more than one reader\n", i);
+	if ((p->no_shconf_present) && p->no_shconf) {
+		snprintf(buffer, sizeof(buffer), "--no-shconf");
+		app->eal_argv[n_args++] = strdup(buffer);
 	}
 
-	for (i = 0; i < n_swq_out; i++) {
-		uint32_t n = app_get_swq_out_count(i);
+	if (p->add_driver) {
+		snprintf(buffer, sizeof(buffer), "-d=%s", p->add_driver);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
+
+	if (p->socket_mem) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--socket-mem=%s",
+			p->socket_mem);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		if (n == 0)
-			rte_panic("SW queue %u has no writer\n", i);
+	if (p->huge_dir) {
+		snprintf(buffer, sizeof(buffer), "--huge-dir=%s", p->huge_dir);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		if (n > 1)
-			rte_panic("SW queue %u has more than one writer\n", i);
+	if (p->file_prefix) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--file-prefix=%s",
+			p->file_prefix);
+		app->eal_argv[n_args++] = strdup(buffer);
 	}
 
-	/* Check the request and response queues are valid */
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
-		uint32_t ring_id_req, ring_id_resp;
+	if (p->base_virtaddr) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--base-virtaddr=%s",
+			p->base_virtaddr);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		if ((p->core_type != APP_CORE_FC) &&
-		    (p->core_type != APP_CORE_FW) &&
-			(p->core_type != APP_CORE_RT)) {
-			continue;
-		}
+	if ((p->create_uio_dev_present) && p->create_uio_dev) {
+		snprintf(buffer, sizeof(buffer), "--create-uio-dev");
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		ring_id_req = p->swq_in[APP_SWQ_IN_REQ];
-		if (ring_id_req == APP_SWQ_INVALID)
-			rte_panic("Core %u of type %u has invalid request "
-				"queue ID\n", p->core_id, p->core_type);
+	if (p->vfio_intr) {
+		snprintf(buffer,
+			sizeof(buffer),
+			"--vfio-intr=%s",
+			p->vfio_intr);
+		app->eal_argv[n_args++] = strdup(buffer);
+	}
 
-		ring_id_resp = p->swq_out[APP_SWQ_OUT_RESP];
-		if (ring_id_resp == APP_SWQ_INVALID)
-			rte_panic("Core %u of type %u has invalid response "
-				"queue ID\n", p->core_id, p->core_type);
+	if ((p->xen_dom0_present) && (p->xen_dom0)) {
+		snprintf(buffer, sizeof(buffer), "--xen-dom0");
+		app->eal_argv[n_args++] = strdup(buffer);
 	}
 
-	return;
+	snprintf(buffer, sizeof(buffer), "--");
+	app->eal_argv[n_args++] = strdup(buffer);
+
+	app->eal_argc = n_args;
+
+	APP_LOG(app, HIGH, "Initializing EAL ...");
+	status = rte_eal_init(app->eal_argc, app->eal_argv);
+	if (status < 0)
+		rte_panic("EAL init error\n");
 }
 
-uint32_t
-app_get_first_core_id(enum app_core_type core_type)
+static void
+app_init_mempool(struct app_params *app)
 {
 	uint32_t i;
 
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
-
-		if (p->core_type == core_type)
-			return p->core_id;
+	for (i = 0; i < app->n_mempools; i++) {
+		struct app_mempool_params *p = &app->mempool_params[i];
+
+		APP_LOG(app, HIGH, "Initializing %s ...", p->name);
+		app->mempool[i] = rte_mempool_create(
+				p->name,
+				p->pool_size,
+				p->buffer_size,
+				p->cache_size,
+				sizeof(struct rte_pktmbuf_pool_private),
+				rte_pktmbuf_pool_init, NULL,
+				rte_pktmbuf_init, NULL,
+				p->cpu_socket_id,
+				0);
+
+		if (app->mempool[i] == NULL)
+			rte_panic("%s init error\n", p->name);
 	}
+}
 
-	return RTE_MAX_LCORE;
+static inline int
+app_link_filter_arp_add(struct app_link_params *link)
+{
+	struct rte_eth_ethertype_filter filter = {
+		.ether_type = ETHER_TYPE_ARP,
+		.flags = 0,
+		.queue = link->arp_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(link->pmd_id,
+		RTE_ETH_FILTER_ETHERTYPE,
+		RTE_ETH_FILTER_ADD,
+		&filter);
 }
 
-struct rte_ring *
-app_get_ring_req(uint32_t core_id)
+static inline int
+app_link_filter_tcp_syn_add(struct app_link_params *link)
 {
-	struct app_core_params *p = app_get_core_params(core_id);
-	uint32_t ring_req_id = p->swq_in[APP_SWQ_IN_REQ];
+	struct rte_eth_syn_filter filter = {
+		.hig_pri = 1,
+		.queue = link->tcp_syn_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(link->pmd_id,
+		RTE_ETH_FILTER_SYN,
+		RTE_ETH_FILTER_ADD,
+		&filter);
+}
 
-	return app.rings[ring_req_id];
+static inline int
+app_link_filter_ip_add(struct app_link_params *l1, struct app_link_params *l2)
+{
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = 0,
+		.proto_mask = 0, /* Disable */
+		.tcp_flags = 0,
+		.priority = 1, /* Lowest */
+		.queue = l1->ip_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_ADD,
+		&filter);
+}
+
+static inline int
+app_link_filter_ip_del(struct app_link_params *l1, struct app_link_params *l2)
+{
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = 0,
+		.proto_mask = 0, /* Disable */
+		.tcp_flags = 0,
+		.priority = 1, /* Lowest */
+		.queue = l1->ip_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_DELETE,
+		&filter);
 }
 
-struct rte_ring *
-app_get_ring_resp(uint32_t core_id)
+static inline int
+app_link_filter_tcp_add(struct app_link_params *l1, struct app_link_params *l2)
 {
-	struct app_core_params *p = app_get_core_params(core_id);
-	uint32_t ring_resp_id = p->swq_out[APP_SWQ_OUT_RESP];
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = IPPROTO_TCP,
+		.proto_mask = UINT8_MAX, /* Enable */
+		.tcp_flags = 0,
+		.priority = 2, /* Higher priority than IP */
+		.queue = l1->tcp_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_ADD,
+		&filter);
+}
 
-	return app.rings[ring_resp_id];
+static inline int
+app_link_filter_tcp_del(struct app_link_params *l1, struct app_link_params *l2)
+{
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = IPPROTO_TCP,
+		.proto_mask = UINT8_MAX, /* Enable */
+		.tcp_flags = 0,
+		.priority = 2, /* Higher priority than IP */
+		.queue = l1->tcp_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_DELETE,
+		&filter);
+}
+
+static inline int
+app_link_filter_udp_add(struct app_link_params *l1, struct app_link_params *l2)
+{
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = IPPROTO_UDP,
+		.proto_mask = UINT8_MAX, /* Enable */
+		.tcp_flags = 0,
+		.priority = 2, /* Higher priority than IP */
+		.queue = l1->udp_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_ADD,
+		&filter);
+}
+
+static inline int
+app_link_filter_udp_del(struct app_link_params *l1, struct app_link_params *l2)
+{
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = IPPROTO_UDP,
+		.proto_mask = UINT8_MAX, /* Enable */
+		.tcp_flags = 0,
+		.priority = 2, /* Higher priority than IP */
+		.queue = l1->udp_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_DELETE,
+		&filter);
+}
+
+static inline int
+app_link_filter_sctp_add(struct app_link_params *l1, struct app_link_params *l2)
+{
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = IPPROTO_SCTP,
+		.proto_mask = UINT8_MAX, /* Enable */
+		.tcp_flags = 0,
+		.priority = 2, /* Higher priority than IP */
+		.queue = l1->sctp_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_ADD,
+		&filter);
+}
+
+static inline int
+app_link_filter_sctp_del(struct app_link_params *l1, struct app_link_params *l2)
+{
+	struct rte_eth_ntuple_filter filter = {
+		.flags = RTE_5TUPLE_FLAGS,
+		.dst_ip = rte_bswap32(l2->ip),
+		.dst_ip_mask = UINT32_MAX, /* Enable */
+		.src_ip = 0,
+		.src_ip_mask = 0, /* Disable */
+		.dst_port = 0,
+		.dst_port_mask = 0, /* Disable */
+		.src_port = 0,
+		.src_port_mask = 0, /* Disable */
+		.proto = IPPROTO_SCTP,
+		.proto_mask = UINT8_MAX, /* Enable */
+		.tcp_flags = 0,
+		.priority = 2, /* Higher priority than IP */
+		.queue = l1->sctp_local_q,
+	};
+
+	return rte_eth_dev_filter_ctrl(l1->pmd_id,
+		RTE_ETH_FILTER_NTUPLE,
+		RTE_ETH_FILTER_DELETE,
+		&filter);
 }
 
 static void
-app_init_mbuf_pools(void)
+app_link_set_arp_filter(struct app_params *app, struct app_link_params *cp)
 {
-	/* Init the buffer pool */
-	RTE_LOG(INFO, USER1, "Creating the mbuf pool ...\n");
-	app.pool = rte_pktmbuf_pool_create("mempool", app.pool_size,
-		app.pool_cache_size, 0, app.pool_buffer_size, rte_socket_id());
-	if (app.pool == NULL)
-		rte_panic("Cannot create mbuf pool\n");
-
-	/* Init the indirect buffer pool */
-	RTE_LOG(INFO, USER1, "Creating the indirect mbuf pool ...\n");
-	app.indirect_pool = rte_pktmbuf_pool_create("indirect mempool",
-		app.pool_size, app.pool_cache_size,
-		sizeof(struct app_pkt_metadata), 0, rte_socket_id());
-	if (app.indirect_pool == NULL)
-		rte_panic("Cannot create mbuf pool\n");
-
-	/* Init the message buffer pool */
-	RTE_LOG(INFO, USER1, "Creating the message pool ...\n");
-	app.msg_pool = rte_mempool_create(
-		"mempool msg",
-		app.msg_pool_size,
-		app.msg_pool_buffer_size,
-		app.msg_pool_cache_size,
-		0,
-		NULL, NULL,
-		rte_ctrlmbuf_init, NULL,
-		rte_socket_id(),
-		0);
-	if (app.msg_pool == NULL)
-		rte_panic("Cannot create message pool\n");
+	if (cp->arp_q != 0) {
+		int status = app_link_filter_arp_add(cp);
+
+		APP_LOG(app, LOW, "%s (%" PRIu32 "): "
+			"Adding ARP filter (queue = %" PRIu32 ")",
+			cp->name, cp->pmd_id, cp->arp_q);
+
+		if (status)
+			rte_panic("%s (%" PRIu32 "): "
+				"Error adding ARP filter "
+				"(queue = %" PRIu32 ") (%" PRId32 ")\n",
+				cp->name, cp->pmd_id, cp->arp_q, status);
+	}
 }
 
 static void
-app_init_rings(void)
+app_link_set_tcp_syn_filter(struct app_params *app, struct app_link_params *cp)
 {
-	uint32_t n_swq, i;
+	if (cp->tcp_syn_local_q != 0) {
+		int status = app_link_filter_tcp_syn_add(cp);
+
+		APP_LOG(app, LOW, "%s (%" PRIu32 "): "
+			"Adding TCP SYN filter (queue = %" PRIu32 ")",
+			cp->name, cp->pmd_id, cp->tcp_syn_local_q);
+
+		if (status)
+			rte_panic("%s (%" PRIu32 "): "
+				"Error adding TCP SYN filter "
+				"(queue = %" PRIu32 ") (%" PRId32 ")\n",
+				cp->name, cp->pmd_id, cp->tcp_syn_local_q,
+				status);
+	}
+}
 
-	n_swq = app_get_n_swq_in();
-	RTE_LOG(INFO, USER1, "Initializing %u SW rings ...\n", n_swq);
+void
+app_link_up_internal(struct app_params *app, struct app_link_params *cp)
+{
+	uint32_t i;
+	int status;
+
+	/* For each link, add filters for IP of current link */
+	if (cp->ip != 0) {
+		for (i = 0; i < app->n_links; i++) {
+			struct app_link_params *p = &app->link_params[i];
+
+			/* IP */
+			if (p->ip_local_q != 0) {
+				int status = app_link_filter_ip_add(p, cp);
+
+				APP_LOG(app, LOW, "%s (%" PRIu32 "): "
+					"Adding IP filter (queue= %" PRIu32
+					", IP = 0x%08" PRIx32 ")",
+					p->name, p->pmd_id, p->ip_local_q,
+					cp->ip);
+
+				if (status)
+					rte_panic("%s (%" PRIu32 "): "
+						"Error adding IP "
+						"filter (queue= %" PRIu32 ", "
+						"IP = 0x%08" PRIx32
+						") (%" PRId32 ")\n",
+						p->name, p->pmd_id,
+						p->ip_local_q, cp->ip, status);
+			}
+
+			/* TCP */
+			if (p->tcp_local_q != 0) {
+				int status = app_link_filter_tcp_add(p, cp);
+
+				APP_LOG(app, LOW, "%s (%" PRIu32 "): "
+					"Adding TCP filter "
+					"(queue = %" PRIu32
+					", IP = 0x%08" PRIx32 ")",
+					p->name, p->pmd_id, p->tcp_local_q,
+					cp->ip);
+
+				if (status)
+					rte_panic("%s (%" PRIu32 "): "
+						"Error adding TCP "
+						"filter (queue = %" PRIu32 ", "
+						"IP = 0x%08" PRIx32
+						") (%" PRId32 ")\n",
+						p->name, p->pmd_id,
+						p->tcp_local_q, cp->ip, status);
+			}
+
+			/* UDP */
+			if (p->udp_local_q != 0) {
+				int status = app_link_filter_udp_add(p, cp);
+
+				APP_LOG(app, LOW, "%s (%" PRIu32 "): "
+					"Adding UDP filter "
+					"(queue = %" PRIu32
+					", IP = 0x%08" PRIx32 ")",
+					p->name, p->pmd_id, p->udp_local_q,
+					cp->ip);
+
+				if (status)
+					rte_panic("%s (%" PRIu32 "): "
+						"Error adding UDP "
+						"filter (queue = %" PRIu32 ", "
+						"IP = 0x%08" PRIx32
+						") (%" PRId32 ")\n",
+						p->name, p->pmd_id,
+						p->udp_local_q, cp->ip, status);
+			}
+
+			/* SCTP */
+			if (p->sctp_local_q != 0) {
+				int status = app_link_filter_sctp_add(p, cp);
+
+				APP_LOG(app, LOW, "%s (%" PRIu32
+					"): Adding SCTP filter "
+					"(queue = %" PRIu32
+					", IP = 0x%08" PRIx32 ")",
+					p->name, p->pmd_id, p->sctp_local_q,
+					cp->ip);
+
+				if (status)
+					rte_panic("%s (%" PRIu32 "): "
+						"Error adding SCTP "
+						"filter (queue = %" PRIu32 ", "
+						"IP = 0x%08" PRIx32
+						") (%" PRId32 ")\n",
+						p->name, p->pmd_id,
+						p->sctp_local_q, cp->ip,
+						status);
+			}
+		}
+	}
 
-	app.rings = rte_malloc_socket(NULL, n_swq * sizeof(struct rte_ring *),
-		RTE_CACHE_LINE_SIZE, rte_socket_id());
-	if (app.rings == NULL)
-		rte_panic("Cannot allocate memory to store ring pointers\n");
+	/* PMD link up */
+	status = rte_eth_dev_set_link_up(cp->pmd_id);
+	if (status < 0)
+		rte_panic("%s (%" PRIu32 "): PMD set up error %" PRId32 "\n",
+			cp->name, cp->pmd_id, status);
 
-	for (i = 0; i < n_swq; i++) {
-		struct rte_ring *ring;
-		char name[32];
+	/* Mark link as UP */
+	cp->state = 1;
+}
 
-		snprintf(name, sizeof(name), "app_ring_%u", i);
+void
+app_link_down_internal(struct app_params *app, struct app_link_params *cp)
+{
+	uint32_t i;
 
-		ring = rte_ring_create(
-			name,
-			app.rsz_swq,
-			rte_socket_id(),
-			RING_F_SP_ENQ | RING_F_SC_DEQ);
+	/* PMD link down */
+	rte_eth_dev_set_link_down(cp->pmd_id);
+
+	/* Mark link as DOWN */
+	cp->state = 0;
+
+	/* Return if current link IP is not valid */
+	if (cp->ip == 0)
+		return;
+
+	/* For each link, remove filters for IP of current link */
+	for (i = 0; i < app->n_links; i++) {
+		struct app_link_params *p = &app->link_params[i];
+
+		/* IP */
+		if (p->ip_local_q != 0) {
+			int status = app_link_filter_ip_del(p, cp);
+
+			APP_LOG(app, LOW, "%s (%" PRIu32
+				"): Deleting IP filter "
+				"(queue = %" PRIu32 ", IP = 0x%" PRIx32 ")",
+				p->name, p->pmd_id, p->ip_local_q, cp->ip);
+
+			if (status)
+				rte_panic("%s (%" PRIu32
+					"): Error deleting IP filter "
+					"(queue = %" PRIu32
+					", IP = 0x%" PRIx32
+					") (%" PRId32 ")\n",
+					p->name, p->pmd_id, p->ip_local_q,
+					cp->ip, status);
+		}
 
-		if (ring == NULL)
-			rte_panic("Cannot create ring %u\n", i);
+		/* TCP */
+		if (p->tcp_local_q != 0) {
+			int status = app_link_filter_tcp_del(p, cp);
+
+			APP_LOG(app, LOW, "%s (%" PRIu32
+				"): Deleting TCP filter "
+				"(queue = %" PRIu32
+				", IP = 0x%" PRIx32 ")",
+				p->name, p->pmd_id, p->tcp_local_q, cp->ip);
+
+			if (status)
+				rte_panic("%s (%" PRIu32
+					"): Error deleting TCP filter "
+					"(queue = %" PRIu32
+					", IP = 0x%" PRIx32
+					") (%" PRId32 ")\n",
+					p->name, p->pmd_id, p->tcp_local_q,
+					cp->ip, status);
+		}
 
-		app.rings[i] = ring;
+		/* UDP */
+		if (p->udp_local_q != 0) {
+			int status = app_link_filter_udp_del(p, cp);
+
+			APP_LOG(app, LOW, "%s (%" PRIu32
+				"): Deleting UDP filter "
+				"(queue = %" PRIu32 ", IP = 0x%" PRIx32 ")",
+				p->name, p->pmd_id, p->udp_local_q, cp->ip);
+
+			if (status)
+				rte_panic("%s (%" PRIu32
+					"): Error deleting UDP filter "
+					"(queue = %" PRIu32
+					", IP = 0x%" PRIx32
+					") (%" PRId32 ")\n",
+					p->name, p->pmd_id, p->udp_local_q,
+					cp->ip, status);
+		}
+
+		/* SCTP */
+		if (p->sctp_local_q != 0) {
+			int status = app_link_filter_sctp_del(p, cp);
+
+			APP_LOG(app, LOW, "%s (%" PRIu32
+				"): Deleting SCTP filter "
+				"(queue = %" PRIu32
+				", IP = 0x%" PRIx32 ")",
+				p->name, p->pmd_id, p->sctp_local_q, cp->ip);
+
+			if (status)
+				rte_panic("%s (%" PRIu32
+					"): Error deleting SCTP filter "
+					"(queue = %" PRIu32
+					", IP = 0x%" PRIx32
+					") (%" PRId32 ")\n",
+					p->name, p->pmd_id, p->sctp_local_q,
+					cp->ip, status);
+		}
 	}
 }
 
 static void
-app_ports_check_link(void)
+app_check_link(struct app_params *app)
 {
-	uint32_t all_ports_up, i;
+	uint32_t all_links_up, i;
+
+	all_links_up = 1;
 
-	all_ports_up = 1;
+	for (i = 0; i < app->n_links; i++) {
+		struct app_link_params *p = &app->link_params[i];
+		struct rte_eth_link link_params;
 
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_eth_link link;
-		uint32_t port;
+		memset(&link_params, 0, sizeof(link_params));
+		rte_eth_link_get(p->pmd_id, &link_params);
 
-		port = app.ports[i];
-		memset(&link, 0, sizeof(link));
-		rte_eth_link_get_nowait(port, &link);
-		RTE_LOG(INFO, USER1, "Port %u (%u Gbps) %s\n",
-			port,
-			link.link_speed / 1000,
-			link.link_status ? "UP" : "DOWN");
+		APP_LOG(app, HIGH, "%s (%" PRIu32 ") (%" PRIu32 " Gbps) %s",
+			p->name,
+			p->pmd_id,
+			link_params.link_speed / 1000,
+			link_params.link_status ? "UP" : "DOWN");
 
-		if (link.link_status == 0)
-			all_ports_up = 0;
+		if (link_params.link_status == 0)
+			all_links_up = 0;
 	}
 
-	if (all_ports_up == 0)
-		rte_panic("Some NIC ports are DOWN\n");
+	if (all_links_up == 0)
+		rte_panic("Some links are DOWN\n");
 }
 
 static void
-app_init_ports(void)
+app_init_link(struct app_params *app)
 {
 	uint32_t i;
 
-	/* Init NIC ports, then start the ports */
-	for (i = 0; i < app.n_ports; i++) {
-		uint32_t port;
-		int ret;
-
-		port = app.ports[i];
-		RTE_LOG(INFO, USER1, "Initializing NIC port %u ...\n", port);
-
-		/* Init port */
-		ret = rte_eth_dev_configure(
-			port,
-			1,
-			1,
-			&app.port_conf);
-		if (ret < 0)
-			rte_panic("Cannot init NIC port %u (%d)\n", port, ret);
-		rte_eth_promiscuous_enable(port);
-
-		/* Init RX queues */
-		ret = rte_eth_rx_queue_setup(
-			port,
-			0,
-			app.rsz_hwq_rx,
-			rte_eth_dev_socket_id(port),
-			&app.rx_conf,
-			app.pool);
-		if (ret < 0)
-			rte_panic("Cannot init RX for port %u (%d)\n",
-				(uint32_t) port, ret);
-
-		/* Init TX queues */
-		ret = rte_eth_tx_queue_setup(
-			port,
-			0,
-			app.rsz_hwq_tx,
-			rte_eth_dev_socket_id(port),
-			&app.tx_conf);
-		if (ret < 0)
-			rte_panic("Cannot init TX for port %u (%d)\n", port,
-				ret);
-
-		/* Start port */
-		ret = rte_eth_dev_start(port);
-		if (ret < 0)
-			rte_panic("Cannot start port %u (%d)\n", port, ret);
-	}
-
-	app_ports_check_link();
-}
+	for (i = 0; i < app->n_links; i++) {
+		struct app_link_params *p_link = &app->link_params[i];
+		uint32_t link_id, n_hwq_in, n_hwq_out, j;
+		int status;
+
+		sscanf(p_link->name, "LINK%" PRIu32, &link_id);
+		n_hwq_in = app_link_get_n_rxq(app, p_link);
+		n_hwq_out = app_link_get_n_txq(app, p_link);
+
+		APP_LOG(app, HIGH, "Initializing %s (%" PRIu32") "
+			"(%" PRIu32 " RXQ, %" PRIu32 " TXQ) ...",
+			p_link->name,
+			p_link->pmd_id,
+			n_hwq_in,
+			n_hwq_out);
+
+		/* LINK */
+		status = rte_eth_dev_configure(
+			p_link->pmd_id,
+			n_hwq_in,
+			n_hwq_out,
+			&p_link->conf);
+		if (status < 0)
+			rte_panic("%s (%" PRId32 "): "
+				"init error (%" PRId32 ")\n",
+				p_link->name, p_link->pmd_id, status);
+
+		rte_eth_macaddr_get(p_link->pmd_id,
+			(struct ether_addr *) &p_link->mac_addr);
+
+		if (p_link->promisc)
+			rte_eth_promiscuous_enable(p_link->pmd_id);
+
+		/* RXQ */
+		for (j = 0; j < app->n_pktq_hwq_in; j++) {
+			struct app_pktq_hwq_in_params *p_rxq =
+				&app->hwq_in_params[j];
+			uint32_t rxq_link_id, rxq_queue_id;
+
+			sscanf(p_rxq->name, "RXQ%" PRIu32 ".%" PRIu32,
+				&rxq_link_id, &rxq_queue_id);
+			if (rxq_link_id != link_id)
+				continue;
+
+			status = rte_eth_rx_queue_setup(
+				p_link->pmd_id,
+				rxq_queue_id,
+				p_rxq->size,
+				rte_eth_dev_socket_id(p_link->pmd_id),
+				&p_rxq->conf,
+				app->mempool[p_rxq->mempool_id]);
+			if (status < 0)
+				rte_panic("%s (%" PRIu32 "): "
+					"%s init error (%" PRId32 ")\n",
+					p_link->name,
+					p_link->pmd_id,
+					p_rxq->name,
+					status);
+		}
 
-#define APP_PING_TIMEOUT_SEC                               5
+		/* TXQ */
+		for (j = 0; j < app->n_pktq_hwq_out; j++) {
+			struct app_pktq_hwq_out_params *p_txq =
+				&app->hwq_out_params[j];
+			uint32_t txq_link_id, txq_queue_id;
+
+			sscanf(p_txq->name, "TXQ%" PRIu32 ".%" PRIu32,
+				&txq_link_id, &txq_queue_id);
+			if (txq_link_id != link_id)
+				continue;
+
+			status = rte_eth_tx_queue_setup(
+				p_link->pmd_id,
+				txq_queue_id,
+				p_txq->size,
+				rte_eth_dev_socket_id(p_link->pmd_id),
+				&p_txq->conf);
+			if (status < 0)
+				rte_panic("%s (%" PRIu32 "): "
+					"%s init error (%" PRId32 ")\n",
+					p_link->name,
+					p_link->pmd_id,
+					p_txq->name,
+					status);
+		}
 
-void
-app_ping(void)
+		/* LINK START */
+		status = rte_eth_dev_start(p_link->pmd_id);
+		if (status < 0)
+			rte_panic("Cannot start %s (error %" PRId32 ")\n",
+				p_link->name, status);
+
+		/* LINK UP */
+		app_link_set_arp_filter(app, p_link);
+		app_link_set_tcp_syn_filter(app, p_link);
+		app_link_up_internal(app, p_link);
+	}
+
+	app_check_link(app);
+}
+
+static void
+app_init_swq(struct app_params *app)
 {
-	unsigned i;
-	uint64_t timestamp, diff_tsc;
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_swq; i++) {
+		struct app_pktq_swq_params *p = &app->swq_params[i];
 
-	const uint64_t timeout = rte_get_tsc_hz() * APP_PING_TIMEOUT_SEC;
+		APP_LOG(app, HIGH, "Initializing %s...", p->name);
+		app->swq[i] = rte_ring_create(
+				p->name,
+				p->size,
+				p->cpu_socket_id,
+				RING_F_SP_ENQ | RING_F_SC_DEQ);
 
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		struct app_core_params *p = &app.cores[i];
-		struct rte_ring *ring_req, *ring_resp;
-		void *msg;
-		struct app_msg_req *req;
+		if (app->swq[i] == NULL)
+			rte_panic("%s init error\n", p->name);
+	}
+}
+
+static void
+app_init_tm(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_pktq_tm; i++) {
+		struct app_pktq_tm_params *p_tm = &app->tm_params[i];
+		struct app_link_params *p_link;
+		struct rte_eth_link link_eth_params;
+		struct rte_sched_port *sched;
+		uint32_t n_subports, subport_id;
 		int status;
 
-		if ((p->core_type != APP_CORE_FC) &&
-		    (p->core_type != APP_CORE_FW) &&
-			(p->core_type != APP_CORE_RT) &&
-			(p->core_type != APP_CORE_RX))
-			continue;
+		p_link = app_get_link_for_tm(app, p_tm);
+		/* LINK */
+		rte_eth_link_get(p_link->pmd_id, &link_eth_params);
+
+		/* TM */
+		p_tm->sched_port_params.name = p_tm->name;
+		p_tm->sched_port_params.socket =
+			rte_eth_dev_socket_id(p_link->pmd_id);
+		p_tm->sched_port_params.rate =
+			(uint64_t) link_eth_params.link_speed * 1000 * 1000 / 8;
+
+		APP_LOG(app, HIGH, "Initializing %s ...", p_tm->name);
+		sched = rte_sched_port_config(&p_tm->sched_port_params);
+		if (sched == NULL)
+			rte_panic("%s init error\n", p_tm->name);
+		app->tm[i] = sched;
+
+		/* Subport */
+		n_subports = p_tm->sched_port_params.n_subports_per_port;
+		for (subport_id = 0; subport_id < n_subports; subport_id++) {
+			uint32_t n_pipes_per_subport, pipe_id;
+
+			status = rte_sched_subport_config(sched,
+				subport_id,
+				&p_tm->sched_subport_params[subport_id]);
+			if (status)
+				rte_panic("%s subport %" PRIu32
+					" init error (%" PRId32 ")\n",
+					p_tm->name, subport_id, status);
+
+			/* Pipe */
+			n_pipes_per_subport =
+				p_tm->sched_port_params.n_pipes_per_subport;
+			for (pipe_id = 0;
+				pipe_id < n_pipes_per_subport;
+				pipe_id++) {
+				int profile_id = p_tm->sched_pipe_to_profile[
+					subport_id * APP_MAX_SCHED_PIPES +
+					pipe_id];
+
+				if (profile_id == -1)
+					continue;
+
+				status = rte_sched_pipe_config(sched,
+					subport_id,
+					pipe_id,
+					profile_id);
+				if (status)
+					rte_panic("%s subport %" PRIu32
+						" pipe %" PRIu32
+						" (profile %" PRId32 ") "
+						"init error (% " PRId32 ")\n",
+						p_tm->name, subport_id, pipe_id,
+						profile_id, status);
+			}
+		}
+	}
+}
+
+static void
+app_init_msgq(struct app_params *app)
+{
+	uint32_t i;
+
+	for (i = 0; i < app->n_msgq; i++) {
+		struct app_msgq_params *p = &app->msgq_params[i];
+
+		APP_LOG(app, HIGH, "Initializing %s ...", p->name);
+		app->msgq[i] = rte_ring_create(
+				p->name,
+				p->size,
+				p->cpu_socket_id,
+				RING_F_SP_ENQ | RING_F_SC_DEQ);
+
+		if (app->msgq[i] == NULL)
+			rte_panic("%s init error\n", p->name);
+	}
+}
+
+static void app_pipeline_params_get(struct app_params *app,
+	struct app_pipeline_params *p_in,
+	struct pipeline_params *p_out)
+{
+	uint32_t i;
 
-		ring_req = app_get_ring_req(p->core_id);
-		ring_resp = app_get_ring_resp(p->core_id);
+	strcpy(p_out->name, p_in->name);
 
-		/* Fill request message */
-		msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-		if (msg == NULL)
-			rte_panic("Unable to allocate new message\n");
+	p_out->socket_id = (int) p_in->socket_id;
 
-		req = (struct app_msg_req *)
-				rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-		req->type = APP_MSG_REQ_PING;
+	p_out->log_level = app->log_level;
 
-		/* Send request */
-		do {
-			status = rte_ring_sp_enqueue(ring_req, msg);
-		} while (status == -ENOBUFS);
+	/* pktq_in */
+	p_out->n_ports_in = p_in->n_pktq_in;
+	for (i = 0; i < p_in->n_pktq_in; i++) {
+		struct app_pktq_in_params *in = &p_in->pktq_in[i];
+		struct pipeline_port_in_params *out = &p_out->port_in[i];
 
-		/* Wait for response */
-		timestamp = rte_rdtsc();
-		do {
-			status = rte_ring_sc_dequeue(ring_resp, &msg);
-			diff_tsc = rte_rdtsc() - timestamp;
+		switch (in->type) {
+		case APP_PKTQ_IN_HWQ:
+		{
+			struct app_pktq_hwq_in_params *p_hwq_in =
+				&app->hwq_in_params[in->id];
+			struct app_link_params *p_link =
+				app_get_link_for_rxq(app, p_hwq_in);
+			uint32_t rxq_link_id, rxq_queue_id;
 
-			if (unlikely(diff_tsc > timeout))
-				rte_panic("Core %u of type %d does not respond "
-					"to requests\n", p->core_id,
-					p->core_type);
-		} while (status != 0);
+			sscanf(p_hwq_in->name, "RXQ%" SCNu32 ".%" SCNu32,
+				&rxq_link_id,
+				&rxq_queue_id);
 
-		/* Free message buffer */
-		rte_ctrlmbuf_free(msg);
+			out->type = PIPELINE_PORT_IN_ETHDEV_READER;
+			out->params.ethdev.port_id = p_link->pmd_id;
+			out->params.ethdev.queue_id = rxq_queue_id;
+			out->burst_size = p_hwq_in->burst;
+			break;
+		}
+		case APP_PKTQ_IN_SWQ:
+			out->type = PIPELINE_PORT_IN_RING_READER;
+			out->params.ring.ring = app->swq[in->id];
+			out->burst_size = app->swq_params[in->id].burst_read;
+			/* What about frag and ras ports? */
+			break;
+		case APP_PKTQ_IN_TM:
+			out->type = PIPELINE_PORT_IN_SCHED_READER;
+			out->params.sched.sched = app->tm[in->id];
+			out->burst_size = app->tm_params[in->id].burst_read;
+			break;
+		case APP_PKTQ_IN_SOURCE:
+			out->type = PIPELINE_PORT_IN_SOURCE;
+			out->params.source.mempool = app->mempool[in->id];
+			out->burst_size = app->source_params[in->id].burst;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* pktq_out */
+	p_out->n_ports_out = p_in->n_pktq_out;
+	for (i = 0; i < p_in->n_pktq_out; i++) {
+		struct app_pktq_out_params *in = &p_in->pktq_out[i];
+		struct pipeline_port_out_params *out = &p_out->port_out[i];
+
+		switch (in->type) {
+		case APP_PKTQ_OUT_HWQ:
+		{
+			struct app_pktq_hwq_out_params *p_hwq_out =
+				&app->hwq_out_params[in->id];
+			struct app_link_params *p_link =
+				app_get_link_for_txq(app, p_hwq_out);
+			uint32_t txq_link_id, txq_queue_id;
+
+			sscanf(p_hwq_out->name,
+				"TXQ%" SCNu32 ".%" SCNu32,
+				&txq_link_id,
+				&txq_queue_id);
+
+			if (p_hwq_out->dropless == 0) {
+				struct rte_port_ethdev_writer_params *params =
+					&out->params.ethdev;
+
+				out->type = PIPELINE_PORT_OUT_ETHDEV_WRITER;
+				params->port_id = p_link->pmd_id;
+				params->queue_id = txq_queue_id;
+				params->tx_burst_sz =
+					app->hwq_out_params[in->id].burst;
+			} else {
+				struct rte_port_ethdev_writer_nodrop_params
+					*params = &out->params.ethdev_nodrop;
+
+				out->type =
+					PIPELINE_PORT_OUT_ETHDEV_WRITER_NODROP;
+				params->port_id = p_link->pmd_id;
+				params->queue_id = txq_queue_id;
+				params->tx_burst_sz = p_hwq_out->burst;
+				params->n_retries = p_hwq_out->n_retries;
+			}
+			break;
+		}
+		case APP_PKTQ_OUT_SWQ:
+			if (app->swq_params[in->id].dropless == 0) {
+				struct rte_port_ring_writer_params *params =
+					&out->params.ring;
+
+				out->type = PIPELINE_PORT_OUT_RING_WRITER;
+				params->ring = app->swq[in->id];
+				params->tx_burst_sz =
+					app->swq_params[in->id].burst_write;
+			} else {
+				struct rte_port_ring_writer_nodrop_params
+					*params = &out->params.ring_nodrop;
+
+				out->type =
+					PIPELINE_PORT_OUT_RING_WRITER_NODROP;
+				params->ring = app->swq[in->id];
+				params->tx_burst_sz =
+					app->swq_params[in->id].burst_write;
+				params->n_retries =
+					app->swq_params[in->id].n_retries;
+			}
+			/* What about frag and ras ports? */
+			break;
+		case APP_PKTQ_OUT_TM: {
+			struct rte_port_sched_writer_params *params =
+				&out->params.sched;
+
+			out->type = PIPELINE_PORT_OUT_SCHED_WRITER;
+			params->sched = app->tm[in->id];
+			params->tx_burst_sz =
+				app->tm_params[in->id].burst_write;
+			break;
+		}
+		case APP_PKTQ_OUT_SINK:
+			out->type = PIPELINE_PORT_OUT_SINK;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* msgq */
+	p_out->n_msgq = p_in->n_msgq_in;
+
+	for (i = 0; i < p_in->n_msgq_in; i++)
+		p_out->msgq_in[i] = app->msgq[p_in->msgq_in[i]];
+
+	for (i = 0; i < p_in->n_msgq_out; i++)
+		p_out->msgq_out[i] = app->msgq[p_in->msgq_out[i]];
+
+	/* args */
+	p_out->n_args = p_in->n_args;
+	for (i = 0; i < p_in->n_args; i++) {
+		p_out->args_name[i] = p_in->args_name[i];
+		p_out->args_value[i] = p_in->args_value[i];
 	}
 }
 
 static void
-app_init_etc(void)
+app_init_pipelines(struct app_params *app)
 {
-	if ((app_get_first_core_id(APP_CORE_IPV4_FRAG) != RTE_MAX_LCORE) ||
-		(app_get_first_core_id(APP_CORE_IPV4_RAS) != RTE_MAX_LCORE)) {
-		RTE_LOG(INFO, USER1,
-			"Activating the Ethernet header pop/push ...\n");
-		app.ether_hdr_pop_push = 1;
+	uint32_t p_id;
+
+	for (p_id = 0; p_id < app->n_pipelines; p_id++) {
+		struct app_pipeline_params *params =
+			&app->pipeline_params[p_id];
+		struct app_pipeline_data *data = &app->pipeline_data[p_id];
+		struct pipeline_type *ptype;
+		struct pipeline_params pp;
+
+		APP_LOG(app, HIGH, "Initializing %s ...", params->name);
+
+		ptype = app_pipeline_type_find(app, params->type);
+		if (ptype == NULL)
+			rte_panic("Init error: Unknown pipeline type \"%s\"\n",
+				params->type);
+
+		app_pipeline_params_get(app, params, &pp);
+
+		/* Back-end */
+		data->be = NULL;
+		if (ptype->be_ops->f_init) {
+			data->be = ptype->be_ops->f_init(&pp, (void *) app);
+
+			if (data->be == NULL)
+				rte_panic("Pipeline instance \"%s\" back-end "
+					"init error\n", params->name);
+		}
+
+		/* Front-end */
+		data->fe = NULL;
+		if (ptype->fe_ops->f_init) {
+			data->fe = ptype->fe_ops->f_init(&pp, (void *) app);
+
+			if (data->fe == NULL)
+				rte_panic("Pipeline instance \"%s\" front-end "
+				"init error\n", params->name);
+		}
+
+		data->timer_period = (rte_get_tsc_hz() * params->timer_period)
+			/ 1000;
 	}
 }
 
-void
-app_init(void)
+static void
+app_init_threads(struct app_params *app)
+{
+	uint64_t time = rte_get_tsc_cycles();
+	uint32_t p_id;
+
+	for (p_id = 0; p_id < app->n_pipelines; p_id++) {
+		struct app_pipeline_params *params =
+			&app->pipeline_params[p_id];
+		struct app_pipeline_data *data = &app->pipeline_data[p_id];
+		struct pipeline_type *ptype;
+		struct app_thread_data *t;
+		struct app_thread_pipeline_data *p;
+		int lcore_id;
+
+		lcore_id = cpu_core_map_get_lcore_id(app->core_map,
+			params->socket_id,
+			params->core_id,
+			params->hyper_th_id);
+
+		if (lcore_id < 0)
+			rte_panic("Invalid core s%" PRIu32 "c%" PRIu32 "%s\n",
+				params->socket_id,
+				params->core_id,
+				(params->hyper_th_id) ? "h" : "");
+
+		t = &app->thread_data[lcore_id];
+
+		ptype = app_pipeline_type_find(app, params->type);
+		if (ptype == NULL)
+			rte_panic("Init error: Unknown pipeline "
+				"type \"%s\"\n", params->type);
+
+		p = (ptype->be_ops->f_run == NULL) ?
+			&t->regular[t->n_regular] :
+			&t->custom[t->n_custom];
+
+		p->be = data->be;
+		p->f_run = ptype->be_ops->f_run;
+		p->f_timer = ptype->be_ops->f_timer;
+		p->timer_period = data->timer_period;
+		p->deadline = time + data->timer_period;
+
+		if (ptype->be_ops->f_run == NULL)
+			t->n_regular++;
+		else
+			t->n_custom++;
+	}
+}
+
+int app_init(struct app_params *app)
+{
+	app_init_core_map(app);
+	app_init_core_mask(app);
+
+	app_init_eal(app);
+	app_init_mempool(app);
+	app_init_link(app);
+	app_init_swq(app);
+	app_init_tm(app);
+	app_init_msgq(app);
+
+	app_init_pipelines(app);
+	app_init_threads(app);
+
+	return 0;
+}
+
+static int
+app_pipeline_type_cmd_push(struct app_params *app,
+	struct pipeline_type *ptype)
+{
+	cmdline_parse_ctx_t *cmds;
+	uint32_t n_cmds, i;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(ptype == NULL))
+		return -EINVAL;
+
+	n_cmds = pipeline_type_cmds_count(ptype);
+	if (n_cmds == 0)
+		return 0;
+
+	cmds = ptype->fe_ops->cmds;
+
+	/* Check for available slots in the application commands array */
+	if (n_cmds > APP_MAX_CMDS - app->n_cmds)
+		return -ENOMEM;
+
+	/* Push pipeline commands into the application */
+	memcpy(&app->cmds[app->n_cmds],
+		cmds,
+		n_cmds * sizeof(cmdline_parse_ctx_t *));
+
+	for (i = 0; i < n_cmds; i++)
+		app->cmds[app->n_cmds + i]->data = app;
+
+	app->n_cmds += n_cmds;
+	app->cmds[app->n_cmds] = NULL;
+
+	return 0;
+}
+
+int
+app_pipeline_type_register(struct app_params *app, struct pipeline_type *ptype)
 {
-	if ((sizeof(struct app_pkt_metadata) % RTE_CACHE_LINE_SIZE) != 0)
-		rte_panic("Application pkt meta-data size mismatch\n");
+	uint32_t n_cmds, i;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(ptype == NULL) ||
+		(ptype->name == NULL) ||
+		(strlen(ptype->name) == 0) ||
+		(ptype->be_ops->f_init == NULL) ||
+		(ptype->be_ops->f_timer == NULL))
+		return -EINVAL;
+
+	/* Check for duplicate entry */
+	for (i = 0; i < app->n_pipeline_types; i++)
+		if (strcmp(app->pipeline_type[i].name, ptype->name) == 0)
+			return -EEXIST;
+
+	/* Check for resource availability */
+	n_cmds = pipeline_type_cmds_count(ptype);
+	if ((app->n_pipeline_types == APP_MAX_PIPELINE_TYPES) ||
+		(n_cmds > APP_MAX_CMDS - app->n_cmds))
+		return -ENOMEM;
+
+	/* Copy pipeline type */
+	memcpy(&app->pipeline_type[app->n_pipeline_types++],
+		ptype,
+		sizeof(struct pipeline_type));
+
+	/* Copy CLI commands */
+	if (n_cmds)
+		app_pipeline_type_cmd_push(app, ptype);
+
+	return 0;
+}
 
-	app_check_core_params();
+struct
+pipeline_type *app_pipeline_type_find(struct app_params *app, char *name)
+{
+	uint32_t i;
 
-	app_init_mbuf_pools();
-	app_init_rings();
-	app_init_ports();
-	app_init_etc();
+	for (i = 0; i < app->n_pipeline_types; i++)
+		if (strcmp(app->pipeline_type[i].name, name) == 0)
+			return &app->pipeline_type[i];
 
-	RTE_LOG(INFO, USER1, "Initialization completed\n");
+	return NULL;
 }
diff --git a/examples/ip_pipeline/main.c b/examples/ip_pipeline/main.c
index 612eea9..ef68c86 100644
--- a/examples/ip_pipeline/main.c
+++ b/examples/ip_pipeline/main.c
@@ -49,5 +49,8 @@ main(int argc, char **argv)
 
 	app_config_check(&app);
 
+	/* Init */
+	app_init(&app);
+
 	return 0;
 }
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 04/11] ip_pipeline: moved pipelines to separate folder
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (2 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 03/11] ip_pipeline: modified init to match new params struct Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline Maciej Gajdzica
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

Moved pipelines to separate folder, removed not needed pipelines and
modified Makefile to match that change.

Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
---
 examples/ip_pipeline/Makefile                      |    9 +-
 examples/ip_pipeline/pipeline/pipeline_firewall.c  |  313 +++++++++++++
 .../pipeline/pipeline_flow_classification.c        |  306 +++++++++++++
 .../ip_pipeline/pipeline/pipeline_passthrough.c    |  213 +++++++++
 examples/ip_pipeline/pipeline/pipeline_routing.c   |  474 ++++++++++++++++++++
 examples/ip_pipeline/pipeline_firewall.c           |  313 -------------
 .../ip_pipeline/pipeline_flow_classification.c     |  306 -------------
 examples/ip_pipeline/pipeline_ipv4_frag.c          |  184 --------
 examples/ip_pipeline/pipeline_ipv4_ras.c           |  181 --------
 examples/ip_pipeline/pipeline_passthrough.c        |  213 ---------
 examples/ip_pipeline/pipeline_routing.c            |  474 --------------------
 examples/ip_pipeline/pipeline_rx.c                 |  385 ----------------
 examples/ip_pipeline/pipeline_tx.c                 |  283 ------------
 13 files changed, 1314 insertions(+), 2340 deletions(-)
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing.c
 delete mode 100644 examples/ip_pipeline/pipeline_firewall.c
 delete mode 100644 examples/ip_pipeline/pipeline_flow_classification.c
 delete mode 100644 examples/ip_pipeline/pipeline_ipv4_frag.c
 delete mode 100644 examples/ip_pipeline/pipeline_ipv4_ras.c
 delete mode 100644 examples/ip_pipeline/pipeline_passthrough.c
 delete mode 100644 examples/ip_pipeline/pipeline_routing.c
 delete mode 100644 examples/ip_pipeline/pipeline_rx.c
 delete mode 100644 examples/ip_pipeline/pipeline_tx.c

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 59bea5b..213e879 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -36,11 +36,17 @@ endif
 # Default target, can be overridden by command line or environment
 RTE_TARGET ?= x86_64-native-linuxapp-gcc
 
+DIRS-(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline
+
 include $(RTE_SDK)/mk/rte.vars.mk
 
 # binary name
 APP = ip_pipeline
 
+VPATH += $(SRCDIR)/pipeline
+
+INC += $(wildcard *.h) $(wildcard pipeline/*.h)
+
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) := main.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse.c
@@ -49,7 +55,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_check.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
 
+CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
 CFLAGS += -O3
-CFLAGS += $(WERROR_FLAGS)
+CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-error=unused-variable
 
 include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall.c b/examples/ip_pipeline/pipeline/pipeline_firewall.c
new file mode 100644
index 0000000..b70260e
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall.c
@@ -0,0 +1,313 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <rte_malloc.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_byteorder.h>
+
+#include <rte_port_ring.h>
+#include <rte_table_acl.h>
+#include <rte_pipeline.h>
+
+#include "main.h"
+
+struct app_core_firewall_message_handle_params {
+	struct rte_ring *ring_req;
+	struct rte_ring *ring_resp;
+
+	struct rte_pipeline *p;
+	uint32_t *port_out_id;
+	uint32_t table_id;
+};
+
+static void
+app_message_handle(struct app_core_firewall_message_handle_params *params);
+
+enum {
+	PROTO_FIELD_IPV4,
+	SRC_FIELD_IPV4,
+	DST_FIELD_IPV4,
+	SRCP_FIELD_IPV4,
+	DSTP_FIELD_IPV4,
+	NUM_FIELDS_IPV4
+};
+
+struct rte_acl_field_def ipv4_field_formats[NUM_FIELDS_IPV4] = {
+	{
+		.type = RTE_ACL_FIELD_TYPE_BITMASK,
+		.size = sizeof(uint8_t),
+		.field_index = PROTO_FIELD_IPV4,
+		.input_index = PROTO_FIELD_IPV4,
+		.offset = sizeof(struct ether_hdr) +
+			offsetof(struct ipv4_hdr, next_proto_id),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = SRC_FIELD_IPV4,
+		.input_index = SRC_FIELD_IPV4,
+		.offset = sizeof(struct ether_hdr) +
+			offsetof(struct ipv4_hdr, src_addr),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = DST_FIELD_IPV4,
+		.input_index = DST_FIELD_IPV4,
+		.offset = sizeof(struct ether_hdr) +
+			offsetof(struct ipv4_hdr, dst_addr),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = SRCP_FIELD_IPV4,
+		.input_index = SRCP_FIELD_IPV4,
+		.offset = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = DSTP_FIELD_IPV4,
+		.input_index = SRCP_FIELD_IPV4,
+		.offset = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) +
+			sizeof(uint16_t),
+	},
+};
+
+void
+app_main_loop_pipeline_firewall(void) {
+	struct rte_pipeline_params pipeline_params = {
+		.name = "pipeline",
+		.socket_id = rte_socket_id(),
+	};
+
+	struct rte_pipeline *p;
+	uint32_t port_in_id[APP_MAX_PORTS];
+	uint32_t port_out_id[APP_MAX_PORTS];
+	uint32_t table_id;
+	uint32_t i;
+
+	uint32_t core_id = rte_lcore_id();
+	struct app_core_params *core_params = app_get_core_params(core_id);
+	struct app_core_firewall_message_handle_params mh_params;
+
+	if ((core_params == NULL) || (core_params->core_type != APP_CORE_FW))
+		rte_panic("Core %u misconfiguration\n", core_id);
+
+	RTE_LOG(INFO, USER1, "Core %u is doing firewall\n", core_id);
+
+	/* Pipeline configuration */
+	p = rte_pipeline_create(&pipeline_params);
+	if (p == NULL)
+		rte_panic("Unable to configure the pipeline\n");
+
+	/* Input port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_reader_params port_ring_params = {
+			.ring = app.rings[core_params->swq_in[i]],
+		};
+
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = &rte_port_ring_reader_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = app.bsz_swq_rd,
+		};
+
+		if (rte_pipeline_port_in_create(p, &port_params,
+			&port_in_id[i]))
+			rte_panic("Unable to configure input port for "
+				"ring %d\n", i);
+	}
+
+	/* Output port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_writer_params port_ring_params = {
+			.ring = app.rings[core_params->swq_out[i]],
+			.tx_burst_sz = app.bsz_swq_wr,
+		};
+
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = &rte_port_ring_writer_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		if (rte_pipeline_port_out_create(p, &port_params,
+			&port_out_id[i]))
+			rte_panic("Unable to configure output port for "
+				"ring %d\n", i);
+	}
+
+	/* Table configuration */
+	{
+		struct rte_table_acl_params table_acl_params = {
+			.name = "test", /* unique identifier for acl contexts */
+			.n_rules = app.max_firewall_rules,
+			.n_rule_fields = DIM(ipv4_field_formats),
+		};
+
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_acl_ops,
+			.arg_create = &table_acl_params,
+			.f_action_hit = NULL,
+			.f_action_miss = NULL,
+			.arg_ah = NULL,
+			.action_data_size = 0,
+		};
+
+		memcpy(table_acl_params.field_format, ipv4_field_formats,
+			sizeof(ipv4_field_formats));
+
+		if (rte_pipeline_table_create(p, &table_params, &table_id))
+			rte_panic("Unable to configure the ACL table\n");
+	}
+
+	/* Interconnecting ports and tables */
+	for (i = 0; i < app.n_ports; i++)
+		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
+			table_id))
+			rte_panic("Unable to connect input port %u to "
+				"table %u\n", port_in_id[i],  table_id);
+
+	/* Enable input ports */
+	for (i = 0; i < app.n_ports; i++)
+		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
+			rte_panic("Unable to enable input port %u\n",
+				port_in_id[i]);
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p) < 0)
+		rte_panic("Pipeline consistency check failed\n");
+
+	/* Message handling */
+	mh_params.ring_req = app_get_ring_req(
+		app_get_first_core_id(APP_CORE_FW));
+	mh_params.ring_resp = app_get_ring_resp(
+		app_get_first_core_id(APP_CORE_FW));
+	mh_params.p = p;
+	mh_params.port_out_id = port_out_id;
+	mh_params.table_id = table_id;
+
+	/* Run-time */
+	for (i = 0; ; i++) {
+		rte_pipeline_run(p);
+
+		if ((i & APP_FLUSH) == 0) {
+			rte_pipeline_flush(p);
+			app_message_handle(&mh_params);
+		}
+	}
+}
+
+void
+app_message_handle(struct app_core_firewall_message_handle_params *params)
+{
+	struct rte_ring *ring_req = params->ring_req;
+	struct rte_ring *ring_resp;
+	struct rte_mbuf *msg;
+	struct app_msg_req *req;
+	struct app_msg_resp *resp;
+	struct rte_pipeline *p;
+	uint32_t *port_out_id;
+	uint32_t table_id;
+	int result;
+
+	/* Read request message */
+	result = rte_ring_sc_dequeue(ring_req, (void **) &msg);
+	if (result != 0)
+		return;
+
+	ring_resp = params->ring_resp;
+	p = params->p;
+	port_out_id = params->port_out_id;
+	table_id = params->table_id;
+
+	/* Handle request */
+	req = (struct app_msg_req *)rte_ctrlmbuf_data(msg);
+	switch (req->type) {
+	case APP_MSG_REQ_PING:
+	{
+		result = 0;
+		break;
+	}
+
+	case APP_MSG_REQ_FW_ADD:
+	{
+		struct rte_pipeline_table_entry entry = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = port_out_id[req->firewall_add.port]},
+		};
+
+		struct rte_pipeline_table_entry *entry_ptr;
+
+		int key_found;
+
+		result = rte_pipeline_table_entry_add(p, table_id,
+			&req->firewall_add.add_params, &entry, &key_found,
+			&entry_ptr);
+		break;
+	}
+
+	case APP_MSG_REQ_FW_DEL:
+	{
+		int key_found;
+
+		result = rte_pipeline_table_entry_delete(p, table_id,
+			&req->firewall_del.delete_params, &key_found, NULL);
+		break;
+	}
+
+	default:
+		rte_panic("FW unrecognized message type (%u)\n", req->type);
+	}
+
+	/* Fill in response message */
+	resp = (struct app_msg_resp *)rte_ctrlmbuf_data(msg);
+	resp->result = result;
+
+	/* Send response */
+	do {
+		result = rte_ring_sp_enqueue(ring_resp, (void *) msg);
+	} while (result == -ENOBUFS);
+}
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_classification.c b/examples/ip_pipeline/pipeline/pipeline_flow_classification.c
new file mode 100644
index 0000000..cc0cbf1
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_classification.c
@@ -0,0 +1,306 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <rte_malloc.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_byteorder.h>
+
+#include <rte_port_ring.h>
+#include <rte_table_hash.h>
+#include <rte_pipeline.h>
+
+#include "main.h"
+
+struct app_core_fc_message_handle_params {
+	struct rte_ring *ring_req;
+	struct rte_ring *ring_resp;
+
+	struct rte_pipeline *p;
+	uint32_t *port_out_id;
+	uint32_t table_id;
+};
+
+static void
+app_message_handle(struct app_core_fc_message_handle_params *params);
+
+static int app_flow_classification_table_init(
+	struct rte_pipeline *p,
+	uint32_t *port_out_id,
+	uint32_t table_id)
+{
+	struct app_flow_key flow_key;
+	uint32_t i;
+
+	/* Add entries to tables */
+	for (i = 0; i < (1 << 24); i++) {
+		struct rte_pipeline_table_entry entry = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = port_out_id[i & (app.n_ports - 1)]},
+		};
+		struct rte_pipeline_table_entry *entry_ptr;
+		int key_found, status;
+
+		flow_key.ttl = 0;
+		flow_key.proto = 6; /* TCP */
+		flow_key.header_checksum = 0;
+		flow_key.ip_src = 0;
+		flow_key.ip_dst = rte_bswap32(i);
+		flow_key.port_src = 0;
+		flow_key.port_dst = 0;
+
+		status = rte_pipeline_table_entry_add(p, table_id,
+			(void *) &flow_key, &entry, &key_found, &entry_ptr);
+		if (status < 0)
+			rte_panic("Unable to add entry to table %u (%d)\n",
+				table_id, status);
+	}
+
+	return 0;
+}
+
+void
+app_main_loop_pipeline_flow_classification(void) {
+	struct rte_pipeline_params pipeline_params = {
+		.name = "pipeline",
+		.socket_id = rte_socket_id(),
+	};
+
+	struct rte_pipeline *p;
+	uint32_t port_in_id[APP_MAX_PORTS];
+	uint32_t port_out_id[APP_MAX_PORTS];
+	uint32_t table_id;
+	uint32_t i;
+
+	uint32_t core_id = rte_lcore_id();
+	struct app_core_params *core_params = app_get_core_params(core_id);
+	struct app_core_fc_message_handle_params mh_params;
+
+	if ((core_params == NULL) || (core_params->core_type != APP_CORE_FC))
+		rte_panic("Core %u misconfiguration\n", core_id);
+
+	RTE_LOG(INFO, USER1, "Core %u is doing flow classification "
+		"(pipeline with hash table, 16-byte key, LRU)\n", core_id);
+
+	/* Pipeline configuration */
+	p = rte_pipeline_create(&pipeline_params);
+	if (p == NULL)
+		rte_panic("Unable to configure the pipeline\n");
+
+	/* Input port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_reader_params port_ring_params = {
+			.ring = app.rings[core_params->swq_in[i]],
+		};
+
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = &rte_port_ring_reader_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = app.bsz_swq_rd,
+		};
+
+		if (rte_pipeline_port_in_create(p, &port_params,
+			&port_in_id[i]))
+			rte_panic("Unable to configure input port for "
+				"ring %d\n", i);
+	}
+
+	/* Output port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_writer_params port_ring_params = {
+			.ring = app.rings[core_params->swq_out[i]],
+			.tx_burst_sz = app.bsz_swq_wr,
+		};
+
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = &rte_port_ring_writer_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		if (rte_pipeline_port_out_create(p, &port_params,
+			&port_out_id[i]))
+			rte_panic("Unable to configure output port for "
+				"ring %d\n", i);
+	}
+
+	/* Table configuration */
+	{
+		struct rte_table_hash_key16_lru_params table_hash_params = {
+			.n_entries = 1 << 24,
+			.signature_offset = __builtin_offsetof(
+				struct app_pkt_metadata, signature),
+			.key_offset = __builtin_offsetof(
+				struct app_pkt_metadata, flow_key),
+			.f_hash = test_hash,
+			.seed = 0,
+		};
+
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_hash_key16_lru_ops,
+			.arg_create = &table_hash_params,
+			.f_action_hit = NULL,
+			.f_action_miss = NULL,
+			.arg_ah = NULL,
+			.action_data_size = 0,
+		};
+
+		if (rte_pipeline_table_create(p, &table_params, &table_id))
+			rte_panic("Unable to configure the hash table\n");
+	}
+
+	/* Interconnecting ports and tables */
+	for (i = 0; i < app.n_ports; i++)
+		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
+			table_id))
+			rte_panic("Unable to connect input port %u to "
+				"table %u\n", port_in_id[i],  table_id);
+
+	/* Enable input ports */
+	for (i = 0; i < app.n_ports; i++)
+		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
+			rte_panic("Unable to enable input port %u\n",
+				port_in_id[i]);
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p) < 0)
+		rte_panic("Pipeline consistency check failed\n");
+
+	/* Message handling */
+	mh_params.ring_req = app_get_ring_req(
+		app_get_first_core_id(APP_CORE_FC));
+	mh_params.ring_resp = app_get_ring_resp(
+		app_get_first_core_id(APP_CORE_FC));
+	mh_params.p = p;
+	mh_params.port_out_id = port_out_id;
+	mh_params.table_id = table_id;
+
+	/* Run-time */
+	for (i = 0; ; i++) {
+		rte_pipeline_run(p);
+
+		if ((i & APP_FLUSH) == 0) {
+			rte_pipeline_flush(p);
+			app_message_handle(&mh_params);
+		}
+	}
+}
+
+void
+app_message_handle(struct app_core_fc_message_handle_params *params)
+{
+	struct rte_ring *ring_req = params->ring_req;
+	struct rte_ring *ring_resp;
+	void *msg;
+	struct app_msg_req *req;
+	struct app_msg_resp *resp;
+	struct rte_pipeline *p;
+	uint32_t *port_out_id;
+	uint32_t table_id;
+	int result;
+
+	/* Read request message */
+	result = rte_ring_sc_dequeue(ring_req, &msg);
+	if (result != 0)
+		return;
+
+	ring_resp = params->ring_resp;
+	p = params->p;
+	port_out_id = params->port_out_id;
+	table_id = params->table_id;
+
+	/* Handle request */
+	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
+	switch (req->type) {
+	case APP_MSG_REQ_PING:
+	{
+		result = 0;
+		break;
+	}
+
+	case APP_MSG_REQ_FC_ADD_ALL:
+	{
+		result = app_flow_classification_table_init(p, port_out_id,
+			table_id);
+		break;
+	}
+
+	case APP_MSG_REQ_FC_ADD:
+	{
+		struct rte_pipeline_table_entry entry = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = port_out_id[req->flow_classif_add.port]},
+		};
+
+		struct rte_pipeline_table_entry *entry_ptr;
+
+		int key_found;
+
+		result = rte_pipeline_table_entry_add(p, table_id,
+			req->flow_classif_add.key_raw, &entry, &key_found,
+			&entry_ptr);
+		break;
+	}
+
+	case APP_MSG_REQ_FC_DEL:
+	{
+		int key_found;
+
+		result = rte_pipeline_table_entry_delete(p, table_id,
+			req->flow_classif_add.key_raw, &key_found, NULL);
+		break;
+	}
+
+	default:
+		rte_panic("FC Unrecognized message type (%u)\n", req->type);
+	}
+
+	/* Fill in response message */
+	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
+	resp->result = result;
+
+	/* Send response */
+	do {
+		result = rte_ring_sp_enqueue(ring_resp, msg);
+	} while (result == -ENOBUFS);
+}
diff --git a/examples/ip_pipeline/pipeline/pipeline_passthrough.c b/examples/ip_pipeline/pipeline/pipeline_passthrough.c
new file mode 100644
index 0000000..948b2c1
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_passthrough.c
@@ -0,0 +1,213 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <rte_malloc.h>
+#include <rte_log.h>
+
+#include <rte_port_ring.h>
+#include <rte_table_stub.h>
+#include <rte_pipeline.h>
+
+#include "main.h"
+
+void
+app_main_loop_pipeline_passthrough(void) {
+	struct rte_pipeline_params pipeline_params = {
+		.name = "pipeline",
+		.socket_id = rte_socket_id(),
+	};
+
+	struct rte_pipeline *p;
+	uint32_t port_in_id[APP_MAX_PORTS];
+	uint32_t port_out_id[APP_MAX_PORTS];
+	uint32_t table_id[APP_MAX_PORTS];
+	uint32_t i;
+
+	uint32_t core_id = rte_lcore_id();
+	struct app_core_params *core_params = app_get_core_params(core_id);
+
+	if ((core_params == NULL) || (core_params->core_type != APP_CORE_PT))
+		rte_panic("Core %u misconfiguration\n", core_id);
+
+	RTE_LOG(INFO, USER1, "Core %u is doing pass-through\n", core_id);
+
+	/* Pipeline configuration */
+	p = rte_pipeline_create(&pipeline_params);
+	if (p == NULL)
+		rte_panic("%s: Unable to configure the pipeline\n", __func__);
+
+	/* Input port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_reader_params port_ring_params = {
+			.ring = app.rings[core_params->swq_in[i]],
+		};
+
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = &rte_port_ring_reader_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = app.bsz_swq_rd,
+		};
+
+		if (rte_pipeline_port_in_create(p, &port_params,
+			&port_in_id[i])) {
+			rte_panic("%s: Unable to configure input port for "
+				"ring %d\n", __func__, i);
+		}
+	}
+
+	/* Output port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_writer_params port_ring_params = {
+			.ring = app.rings[core_params->swq_out[i]],
+			.tx_burst_sz = app.bsz_swq_wr,
+		};
+
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = &rte_port_ring_writer_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		if (rte_pipeline_port_out_create(p, &port_params,
+			&port_out_id[i])) {
+			rte_panic("%s: Unable to configure output port for "
+				"ring %d\n", __func__, i);
+		}
+	}
+
+	/* Table configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_stub_ops,
+			.arg_create = NULL,
+			.f_action_hit = NULL,
+			.f_action_miss = NULL,
+			.arg_ah = NULL,
+			.action_data_size = 0,
+		};
+
+		if (rte_pipeline_table_create(p, &table_params, &table_id[i]))
+			rte_panic("%s: Unable to configure table %u\n",
+				__func__, i);
+	}
+
+	/* Interconnecting ports and tables */
+	for (i = 0; i < app.n_ports; i++) {
+		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
+			table_id[i])) {
+			rte_panic("%s: Unable to connect input port %u to "
+				"table %u\n", __func__, port_in_id[i],
+				table_id[i]);
+		}
+	}
+
+	/* Add entries to tables */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_pipeline_table_entry default_entry = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = port_out_id[i]},
+		};
+
+		struct rte_pipeline_table_entry *default_entry_ptr;
+
+		if (rte_pipeline_table_default_entry_add(p, table_id[i],
+			&default_entry, &default_entry_ptr))
+			rte_panic("%s: Unable to add default entry to "
+				"table %u\n", __func__, table_id[i]);
+	}
+
+	/* Enable input ports */
+	for (i = 0; i < app.n_ports; i++)
+		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
+			rte_panic("Unable to enable input port %u\n",
+				port_in_id[i]);
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p) < 0)
+		rte_panic("%s: Pipeline consistency check failed\n", __func__);
+
+	/* Run-time */
+	for (i = 0; ; i++) {
+		rte_pipeline_run(p);
+
+		if ((i & APP_FLUSH) == 0)
+			rte_pipeline_flush(p);
+	}
+}
+
+void
+app_main_loop_passthrough(void) {
+	struct app_mbuf_array *m;
+	uint32_t i;
+
+	uint32_t core_id = rte_lcore_id();
+	struct app_core_params *core_params = app_get_core_params(core_id);
+
+	if ((core_params == NULL) || (core_params->core_type != APP_CORE_PT))
+		rte_panic("Core %u misconfiguration\n", core_id);
+
+	RTE_LOG(INFO, USER1, "Core %u is doing pass-through (no pipeline)\n",
+		core_id);
+
+	m = rte_malloc_socket(NULL, sizeof(struct app_mbuf_array),
+		RTE_CACHE_LINE_SIZE, rte_socket_id());
+	if (m == NULL)
+		rte_panic("%s: cannot allocate buffer space\n", __func__);
+
+	for (i = 0; ; i = ((i + 1) & (app.n_ports - 1))) {
+		int ret;
+
+		ret = rte_ring_sc_dequeue_bulk(
+			app.rings[core_params->swq_in[i]],
+			(void **) m->array,
+			app.bsz_swq_rd);
+
+		if (ret == -ENOENT)
+			continue;
+
+		do {
+			ret = rte_ring_sp_enqueue_bulk(
+				app.rings[core_params->swq_out[i]],
+				(void **) m->array,
+				app.bsz_swq_wr);
+		} while (ret < 0);
+	}
+}
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline/pipeline_routing.c
new file mode 100644
index 0000000..b1ce624
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.c
@@ -0,0 +1,474 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <rte_malloc.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_byteorder.h>
+
+#include <rte_port_ring.h>
+#include <rte_table_lpm.h>
+#include <rte_table_hash.h>
+#include <rte_pipeline.h>
+
+#include "main.h"
+
+#include <unistd.h>
+
+struct app_routing_table_entry {
+	struct rte_pipeline_table_entry head;
+	uint32_t nh_ip;
+	uint32_t nh_iface;
+};
+
+struct app_arp_table_entry {
+	struct rte_pipeline_table_entry head;
+	struct ether_addr nh_arp;
+};
+
+static inline void
+app_routing_table_write_metadata(
+	struct rte_mbuf *pkt,
+	struct app_routing_table_entry *entry)
+{
+	struct app_pkt_metadata *c =
+		(struct app_pkt_metadata *) RTE_MBUF_METADATA_UINT8_PTR(pkt, 0);
+
+	c->arp_key.nh_ip = entry->nh_ip;
+	c->arp_key.nh_iface = entry->nh_iface;
+}
+
+static int
+app_routing_table_ah(
+	struct rte_mbuf **pkts,
+	uint64_t *pkts_mask,
+	struct rte_pipeline_table_entry **entries,
+	__attribute__((unused)) void *arg)
+{
+	uint64_t pkts_in_mask = *pkts_mask;
+
+	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);
+		uint32_t i;
+
+		for (i = 0; i < n_pkts; i++) {
+			struct rte_mbuf *m = pkts[i];
+			struct app_routing_table_entry *a =
+				(struct app_routing_table_entry *) entries[i];
+
+			app_routing_table_write_metadata(m, a);
+		}
+	} else
+		for ( ; pkts_in_mask; ) {
+			struct rte_mbuf *m;
+			struct app_routing_table_entry *a;
+			uint64_t pkt_mask;
+			uint32_t packet_index;
+
+			packet_index = __builtin_ctzll(pkts_in_mask);
+			pkt_mask = 1LLU << packet_index;
+			pkts_in_mask &= ~pkt_mask;
+
+			m = pkts[packet_index];
+			a = (struct app_routing_table_entry *)
+				entries[packet_index];
+			app_routing_table_write_metadata(m, a);
+		}
+
+	return 0;
+}
+
+static inline void
+app_arp_table_write_metadata(
+	struct rte_mbuf *pkt,
+	struct app_arp_table_entry *entry)
+{
+	struct app_pkt_metadata *c =
+		(struct app_pkt_metadata *) RTE_MBUF_METADATA_UINT8_PTR(pkt, 0);
+	ether_addr_copy(&entry->nh_arp, &c->nh_arp);
+}
+
+static int
+app_arp_table_ah(
+	struct rte_mbuf **pkts,
+	uint64_t *pkts_mask,
+	struct rte_pipeline_table_entry **entries,
+	__attribute__((unused)) void *arg)
+{
+	uint64_t pkts_in_mask = *pkts_mask;
+
+	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {
+		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);
+		uint32_t i;
+
+		for (i = 0; i < n_pkts; i++) {
+			struct rte_mbuf *m = pkts[i];
+			struct app_arp_table_entry *a =
+				(struct app_arp_table_entry *) entries[i];
+
+			app_arp_table_write_metadata(m, a);
+		}
+	} else {
+		for ( ; pkts_in_mask; ) {
+			struct rte_mbuf *m;
+			struct app_arp_table_entry *a;
+			uint64_t pkt_mask;
+			uint32_t packet_index;
+
+			packet_index = __builtin_ctzll(pkts_in_mask);
+			pkt_mask = 1LLU << packet_index;
+			pkts_in_mask &= ~pkt_mask;
+
+			m = pkts[packet_index];
+			a = (struct app_arp_table_entry *)
+				entries[packet_index];
+			app_arp_table_write_metadata(m, a);
+		}
+	}
+
+	return 0;
+}
+
+static uint64_t app_arp_table_hash(
+	void *key,
+	__attribute__((unused)) uint32_t key_size,
+	__attribute__((unused)) uint64_t seed)
+{
+	uint32_t *k = (uint32_t *) key;
+
+	return k[1];
+}
+
+struct app_core_routing_message_handle_params {
+	struct rte_ring *ring_req;
+	struct rte_ring *ring_resp;
+	struct rte_pipeline *p;
+	uint32_t *port_out_id;
+	uint32_t routing_table_id;
+	uint32_t arp_table_id;
+};
+
+static void
+app_message_handle(struct app_core_routing_message_handle_params *params);
+
+void
+app_main_loop_pipeline_routing(void) {
+	struct rte_pipeline_params pipeline_params = {
+		.name = "pipeline",
+		.socket_id = rte_socket_id(),
+	};
+
+	struct rte_pipeline *p;
+	uint32_t port_in_id[APP_MAX_PORTS];
+	uint32_t port_out_id[APP_MAX_PORTS];
+	uint32_t routing_table_id, arp_table_id;
+	uint32_t i;
+
+	uint32_t core_id = rte_lcore_id();
+	struct app_core_params *core_params = app_get_core_params(core_id);
+	struct app_core_routing_message_handle_params mh_params;
+
+	if ((core_params == NULL) || (core_params->core_type != APP_CORE_RT))
+		rte_panic("Core %u misconfiguration\n", core_id);
+
+	RTE_LOG(INFO, USER1, "Core %u is doing routing\n", core_id);
+
+	/* Pipeline configuration */
+	p = rte_pipeline_create(&pipeline_params);
+	if (p == NULL)
+		rte_panic("Unable to configure the pipeline\n");
+
+	/* Input port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_reader_params port_ring_params = {
+			.ring = app.rings[core_params->swq_in[i]],
+		};
+
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = &rte_port_ring_reader_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = app.bsz_swq_rd,
+		};
+
+		if (rte_pipeline_port_in_create(p, &port_params,
+			&port_in_id[i]))
+			rte_panic("Unable to configure input port for "
+				"ring %d\n", i);
+	}
+
+	/* Output port configuration */
+	for (i = 0; i < app.n_ports; i++) {
+		struct rte_port_ring_writer_params port_ring_params = {
+			.ring = app.rings[core_params->swq_out[i]],
+			.tx_burst_sz = app.bsz_swq_wr,
+		};
+
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = &rte_port_ring_writer_ops,
+			.arg_create = (void *) &port_ring_params,
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		if (rte_pipeline_port_out_create(p, &port_params,
+			&port_out_id[i]))
+			rte_panic("Unable to configure output port for "
+				"ring %d\n", i);
+	}
+
+	/* Routing table configuration */
+	{
+		struct rte_table_lpm_params table_lpm_params = {
+			.n_rules = app.max_routing_rules,
+			.entry_unique_size =
+				sizeof(struct app_routing_table_entry),
+			.offset = __builtin_offsetof(struct app_pkt_metadata,
+				flow_key.ip_dst),
+		};
+
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_lpm_ops,
+			.arg_create = &table_lpm_params,
+			.f_action_hit = app_routing_table_ah,
+			.f_action_miss = NULL,
+			.arg_ah = NULL,
+			.action_data_size =
+				sizeof(struct app_routing_table_entry) -
+				sizeof(struct rte_pipeline_table_entry),
+		};
+
+		if (rte_pipeline_table_create(p, &table_params,
+			&routing_table_id))
+			rte_panic("Unable to configure the LPM table\n");
+	}
+
+	/* ARP table configuration */
+	{
+		struct rte_table_hash_key8_lru_params table_arp_params = {
+			.n_entries = app.max_arp_rules,
+			.f_hash = app_arp_table_hash,
+			.seed = 0,
+			.signature_offset = 0, /* Unused */
+			.key_offset = __builtin_offsetof(
+				struct app_pkt_metadata, arp_key),
+		};
+
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_hash_key8_lru_dosig_ops,
+			.arg_create = &table_arp_params,
+			.f_action_hit = app_arp_table_ah,
+			.f_action_miss = NULL,
+			.arg_ah = NULL,
+			.action_data_size = sizeof(struct app_arp_table_entry) -
+				sizeof(struct rte_pipeline_table_entry),
+		};
+
+		if (rte_pipeline_table_create(p, &table_params, &arp_table_id))
+			rte_panic("Unable to configure the ARP table\n");
+	}
+
+	/* Interconnecting ports and tables */
+	for (i = 0; i < app.n_ports; i++) {
+		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
+			routing_table_id))
+			rte_panic("Unable to connect input port %u to "
+				"table %u\n", port_in_id[i],  routing_table_id);
+	}
+
+	/* Enable input ports */
+	for (i = 0; i < app.n_ports; i++)
+		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
+			rte_panic("Unable to enable input port %u\n",
+				port_in_id[i]);
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p) < 0)
+		rte_panic("Pipeline consistency check failed\n");
+
+	/* Message handling */
+	mh_params.ring_req =
+		app_get_ring_req(app_get_first_core_id(APP_CORE_RT));
+	mh_params.ring_resp =
+		app_get_ring_resp(app_get_first_core_id(APP_CORE_RT));
+	mh_params.p = p;
+	mh_params.port_out_id = port_out_id;
+	mh_params.routing_table_id = routing_table_id;
+	mh_params.arp_table_id = arp_table_id;
+
+	/* Run-time */
+	for (i = 0; ; i++) {
+		rte_pipeline_run(p);
+
+		if ((i & APP_FLUSH) == 0) {
+			rte_pipeline_flush(p);
+			app_message_handle(&mh_params);
+		}
+	}
+}
+
+void
+app_message_handle(struct app_core_routing_message_handle_params *params)
+{
+	struct rte_ring *ring_req = params->ring_req;
+	struct rte_ring *ring_resp;
+	void *msg;
+	struct app_msg_req *req;
+	struct app_msg_resp *resp;
+	struct rte_pipeline *p;
+	uint32_t *port_out_id;
+	uint32_t routing_table_id, arp_table_id;
+	int result;
+
+	/* Read request message */
+	result = rte_ring_sc_dequeue(ring_req, &msg);
+	if (result != 0)
+		return;
+
+	ring_resp = params->ring_resp;
+	p = params->p;
+	port_out_id = params->port_out_id;
+	routing_table_id = params->routing_table_id;
+	arp_table_id = params->arp_table_id;
+
+	/* Handle request */
+	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
+	switch (req->type) {
+	case APP_MSG_REQ_PING:
+	{
+		result = 0;
+		break;
+	}
+
+	case APP_MSG_REQ_RT_ADD:
+	{
+		struct app_routing_table_entry entry = {
+			.head = {
+				.action = RTE_PIPELINE_ACTION_TABLE,
+				{.table_id = arp_table_id},
+			},
+			.nh_ip = req->routing_add.nh_ip,
+			.nh_iface = port_out_id[req->routing_add.port],
+		};
+
+		struct rte_table_lpm_key key = {
+			.ip = req->routing_add.ip,
+			.depth = req->routing_add.depth,
+		};
+
+		struct rte_pipeline_table_entry *entry_ptr;
+
+		int key_found;
+
+		result = rte_pipeline_table_entry_add(p, routing_table_id, &key,
+			(struct rte_pipeline_table_entry *) &entry, &key_found,
+			&entry_ptr);
+		break;
+	}
+
+	case APP_MSG_REQ_RT_DEL:
+	{
+		struct rte_table_lpm_key key = {
+			.ip = req->routing_del.ip,
+			.depth = req->routing_del.depth,
+		};
+
+		int key_found;
+
+		result = rte_pipeline_table_entry_delete(p, routing_table_id,
+			&key, &key_found, NULL);
+		break;
+	}
+
+	case APP_MSG_REQ_ARP_ADD:
+	{
+
+		struct app_arp_table_entry entry = {
+			.head = {
+				.action = RTE_PIPELINE_ACTION_PORT,
+				{.port_id =
+					port_out_id[req->arp_add.out_iface]},
+			},
+			.nh_arp = req->arp_add.nh_arp,
+		};
+
+		struct app_arp_key arp_key = {
+			.nh_ip = req->arp_add.nh_ip,
+			.nh_iface = port_out_id[req->arp_add.out_iface],
+		};
+
+		struct rte_pipeline_table_entry *entry_ptr;
+
+		int key_found;
+
+		result = rte_pipeline_table_entry_add(p, arp_table_id, &arp_key,
+			(struct rte_pipeline_table_entry *) &entry, &key_found,
+			&entry_ptr);
+		break;
+	}
+
+	case APP_MSG_REQ_ARP_DEL:
+	{
+		struct app_arp_key arp_key = {
+			.nh_ip = req->arp_del.nh_ip,
+			.nh_iface = port_out_id[req->arp_del.out_iface],
+		};
+
+		int key_found;
+
+		result = rte_pipeline_table_entry_delete(p, arp_table_id,
+			&arp_key, &key_found, NULL);
+		break;
+	}
+
+	default:
+		rte_panic("RT Unrecognized message type (%u)\n", req->type);
+	}
+
+	/* Fill in response message */
+	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
+	resp->result = result;
+
+	/* Send response */
+	do {
+		result = rte_ring_sp_enqueue(ring_resp, msg);
+	} while (result == -ENOBUFS);
+}
diff --git a/examples/ip_pipeline/pipeline_firewall.c b/examples/ip_pipeline/pipeline_firewall.c
deleted file mode 100644
index b70260e..0000000
--- a/examples/ip_pipeline/pipeline_firewall.c
+++ /dev/null
@@ -1,313 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <rte_malloc.h>
-#include <rte_log.h>
-#include <rte_ethdev.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-#include <rte_byteorder.h>
-
-#include <rte_port_ring.h>
-#include <rte_table_acl.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-struct app_core_firewall_message_handle_params {
-	struct rte_ring *ring_req;
-	struct rte_ring *ring_resp;
-
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
-};
-
-static void
-app_message_handle(struct app_core_firewall_message_handle_params *params);
-
-enum {
-	PROTO_FIELD_IPV4,
-	SRC_FIELD_IPV4,
-	DST_FIELD_IPV4,
-	SRCP_FIELD_IPV4,
-	DSTP_FIELD_IPV4,
-	NUM_FIELDS_IPV4
-};
-
-struct rte_acl_field_def ipv4_field_formats[NUM_FIELDS_IPV4] = {
-	{
-		.type = RTE_ACL_FIELD_TYPE_BITMASK,
-		.size = sizeof(uint8_t),
-		.field_index = PROTO_FIELD_IPV4,
-		.input_index = PROTO_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) +
-			offsetof(struct ipv4_hdr, next_proto_id),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = SRC_FIELD_IPV4,
-		.input_index = SRC_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) +
-			offsetof(struct ipv4_hdr, src_addr),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = DST_FIELD_IPV4,
-		.input_index = DST_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) +
-			offsetof(struct ipv4_hdr, dst_addr),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = SRCP_FIELD_IPV4,
-		.input_index = SRCP_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = DSTP_FIELD_IPV4,
-		.input_index = SRCP_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) +
-			sizeof(uint16_t),
-	},
-};
-
-void
-app_main_loop_pipeline_firewall(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id;
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-	struct app_core_firewall_message_handle_params mh_params;
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_FW))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing firewall\n", core_id);
-
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("Unable to configure the pipeline\n");
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("Unable to configure input port for "
-				"ring %d\n", i);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("Unable to configure output port for "
-				"ring %d\n", i);
-	}
-
-	/* Table configuration */
-	{
-		struct rte_table_acl_params table_acl_params = {
-			.name = "test", /* unique identifier for acl contexts */
-			.n_rules = app.max_firewall_rules,
-			.n_rule_fields = DIM(ipv4_field_formats),
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_acl_ops,
-			.arg_create = &table_acl_params,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		memcpy(table_acl_params.field_format, ipv4_field_formats,
-			sizeof(ipv4_field_formats));
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id))
-			rte_panic("Unable to configure the ACL table\n");
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id))
-			rte_panic("Unable to connect input port %u to "
-				"table %u\n", port_in_id[i],  table_id);
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("Pipeline consistency check failed\n");
-
-	/* Message handling */
-	mh_params.ring_req = app_get_ring_req(
-		app_get_first_core_id(APP_CORE_FW));
-	mh_params.ring_resp = app_get_ring_resp(
-		app_get_first_core_id(APP_CORE_FW));
-	mh_params.p = p;
-	mh_params.port_out_id = port_out_id;
-	mh_params.table_id = table_id;
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0) {
-			rte_pipeline_flush(p);
-			app_message_handle(&mh_params);
-		}
-	}
-}
-
-void
-app_message_handle(struct app_core_firewall_message_handle_params *params)
-{
-	struct rte_ring *ring_req = params->ring_req;
-	struct rte_ring *ring_resp;
-	struct rte_mbuf *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
-	int result;
-
-	/* Read request message */
-	result = rte_ring_sc_dequeue(ring_req, (void **) &msg);
-	if (result != 0)
-		return;
-
-	ring_resp = params->ring_resp;
-	p = params->p;
-	port_out_id = params->port_out_id;
-	table_id = params->table_id;
-
-	/* Handle request */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data(msg);
-	switch (req->type) {
-	case APP_MSG_REQ_PING:
-	{
-		result = 0;
-		break;
-	}
-
-	case APP_MSG_REQ_FW_ADD:
-	{
-		struct rte_pipeline_table_entry entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[req->firewall_add.port]},
-		};
-
-		struct rte_pipeline_table_entry *entry_ptr;
-
-		int key_found;
-
-		result = rte_pipeline_table_entry_add(p, table_id,
-			&req->firewall_add.add_params, &entry, &key_found,
-			&entry_ptr);
-		break;
-	}
-
-	case APP_MSG_REQ_FW_DEL:
-	{
-		int key_found;
-
-		result = rte_pipeline_table_entry_delete(p, table_id,
-			&req->firewall_del.delete_params, &key_found, NULL);
-		break;
-	}
-
-	default:
-		rte_panic("FW unrecognized message type (%u)\n", req->type);
-	}
-
-	/* Fill in response message */
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data(msg);
-	resp->result = result;
-
-	/* Send response */
-	do {
-		result = rte_ring_sp_enqueue(ring_resp, (void *) msg);
-	} while (result == -ENOBUFS);
-}
diff --git a/examples/ip_pipeline/pipeline_flow_classification.c b/examples/ip_pipeline/pipeline_flow_classification.c
deleted file mode 100644
index cc0cbf1..0000000
--- a/examples/ip_pipeline/pipeline_flow_classification.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <rte_malloc.h>
-#include <rte_log.h>
-#include <rte_ethdev.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-#include <rte_byteorder.h>
-
-#include <rte_port_ring.h>
-#include <rte_table_hash.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-struct app_core_fc_message_handle_params {
-	struct rte_ring *ring_req;
-	struct rte_ring *ring_resp;
-
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
-};
-
-static void
-app_message_handle(struct app_core_fc_message_handle_params *params);
-
-static int app_flow_classification_table_init(
-	struct rte_pipeline *p,
-	uint32_t *port_out_id,
-	uint32_t table_id)
-{
-	struct app_flow_key flow_key;
-	uint32_t i;
-
-	/* Add entries to tables */
-	for (i = 0; i < (1 << 24); i++) {
-		struct rte_pipeline_table_entry entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i & (app.n_ports - 1)]},
-		};
-		struct rte_pipeline_table_entry *entry_ptr;
-		int key_found, status;
-
-		flow_key.ttl = 0;
-		flow_key.proto = 6; /* TCP */
-		flow_key.header_checksum = 0;
-		flow_key.ip_src = 0;
-		flow_key.ip_dst = rte_bswap32(i);
-		flow_key.port_src = 0;
-		flow_key.port_dst = 0;
-
-		status = rte_pipeline_table_entry_add(p, table_id,
-			(void *) &flow_key, &entry, &key_found, &entry_ptr);
-		if (status < 0)
-			rte_panic("Unable to add entry to table %u (%d)\n",
-				table_id, status);
-	}
-
-	return 0;
-}
-
-void
-app_main_loop_pipeline_flow_classification(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id;
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-	struct app_core_fc_message_handle_params mh_params;
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_FC))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing flow classification "
-		"(pipeline with hash table, 16-byte key, LRU)\n", core_id);
-
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("Unable to configure the pipeline\n");
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("Unable to configure input port for "
-				"ring %d\n", i);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("Unable to configure output port for "
-				"ring %d\n", i);
-	}
-
-	/* Table configuration */
-	{
-		struct rte_table_hash_key16_lru_params table_hash_params = {
-			.n_entries = 1 << 24,
-			.signature_offset = __builtin_offsetof(
-				struct app_pkt_metadata, signature),
-			.key_offset = __builtin_offsetof(
-				struct app_pkt_metadata, flow_key),
-			.f_hash = test_hash,
-			.seed = 0,
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_hash_key16_lru_ops,
-			.arg_create = &table_hash_params,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id))
-			rte_panic("Unable to configure the hash table\n");
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id))
-			rte_panic("Unable to connect input port %u to "
-				"table %u\n", port_in_id[i],  table_id);
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("Pipeline consistency check failed\n");
-
-	/* Message handling */
-	mh_params.ring_req = app_get_ring_req(
-		app_get_first_core_id(APP_CORE_FC));
-	mh_params.ring_resp = app_get_ring_resp(
-		app_get_first_core_id(APP_CORE_FC));
-	mh_params.p = p;
-	mh_params.port_out_id = port_out_id;
-	mh_params.table_id = table_id;
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0) {
-			rte_pipeline_flush(p);
-			app_message_handle(&mh_params);
-		}
-	}
-}
-
-void
-app_message_handle(struct app_core_fc_message_handle_params *params)
-{
-	struct rte_ring *ring_req = params->ring_req;
-	struct rte_ring *ring_resp;
-	void *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
-	int result;
-
-	/* Read request message */
-	result = rte_ring_sc_dequeue(ring_req, &msg);
-	if (result != 0)
-		return;
-
-	ring_resp = params->ring_resp;
-	p = params->p;
-	port_out_id = params->port_out_id;
-	table_id = params->table_id;
-
-	/* Handle request */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	switch (req->type) {
-	case APP_MSG_REQ_PING:
-	{
-		result = 0;
-		break;
-	}
-
-	case APP_MSG_REQ_FC_ADD_ALL:
-	{
-		result = app_flow_classification_table_init(p, port_out_id,
-			table_id);
-		break;
-	}
-
-	case APP_MSG_REQ_FC_ADD:
-	{
-		struct rte_pipeline_table_entry entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[req->flow_classif_add.port]},
-		};
-
-		struct rte_pipeline_table_entry *entry_ptr;
-
-		int key_found;
-
-		result = rte_pipeline_table_entry_add(p, table_id,
-			req->flow_classif_add.key_raw, &entry, &key_found,
-			&entry_ptr);
-		break;
-	}
-
-	case APP_MSG_REQ_FC_DEL:
-	{
-		int key_found;
-
-		result = rte_pipeline_table_entry_delete(p, table_id,
-			req->flow_classif_add.key_raw, &key_found, NULL);
-		break;
-	}
-
-	default:
-		rte_panic("FC Unrecognized message type (%u)\n", req->type);
-	}
-
-	/* Fill in response message */
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	resp->result = result;
-
-	/* Send response */
-	do {
-		result = rte_ring_sp_enqueue(ring_resp, msg);
-	} while (result == -ENOBUFS);
-}
diff --git a/examples/ip_pipeline/pipeline_ipv4_frag.c b/examples/ip_pipeline/pipeline_ipv4_frag.c
deleted file mode 100644
index e799206..0000000
--- a/examples/ip_pipeline/pipeline_ipv4_frag.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_malloc.h>
-#include <rte_ethdev.h>
-#include <rte_mbuf.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-
-#include <rte_port_ethdev.h>
-#include <rte_port_ring.h>
-#include <rte_port_frag.h>
-#include <rte_table_stub.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-void
-app_main_loop_pipeline_ipv4_frag(void) {
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id[APP_MAX_PORTS];
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) ||
-		(core_params->core_type != APP_CORE_IPV4_FRAG))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing IPv4 fragmentation\n", core_id);
-
-	/* Pipeline configuration */
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("%s: Unable to configure the pipeline\n", __func__);
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_ipv4_frag_params
-			port_frag_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-			.mtu = 1500,
-			.metadata_size = sizeof(struct app_pkt_metadata),
-			.pool_direct = app.pool,
-			.pool_indirect = app.indirect_pool,
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ipv4_frag_ops,
-			.arg_create = (void *) &port_frag_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("%s: Unable to configure input port %i\n",
-				__func__, i);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("%s: Unable to configure output port %i\n",
-				__func__, i);
-	}
-
-	/* Table configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_stub_ops,
-			.arg_create = NULL,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id[i]))
-			rte_panic("%s: Unable to configure table %u\n",
-				__func__, table_id[i]);
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id[i]))
-			rte_panic("%s: Unable to connect input port %u to "
-				"table %u\n", __func__, port_in_id[i],
-				table_id[i]);
-
-	/* Add entries to tables */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_entry default_entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i]},
-		};
-
-		struct rte_pipeline_table_entry *default_entry_ptr;
-
-		if (rte_pipeline_table_default_entry_add(p, table_id[i],
-			&default_entry, &default_entry_ptr))
-			rte_panic("%s: Unable to add default entry to "
-				"table %u\n", __func__, table_id[i]);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("%s: Pipeline consistency check failed\n", __func__);
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0)
-			rte_pipeline_flush(p);
-	}
-}
diff --git a/examples/ip_pipeline/pipeline_ipv4_ras.c b/examples/ip_pipeline/pipeline_ipv4_ras.c
deleted file mode 100644
index 2d6611c..0000000
--- a/examples/ip_pipeline/pipeline_ipv4_ras.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_malloc.h>
-#include <rte_ethdev.h>
-#include <rte_mbuf.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-
-#include <rte_port_ethdev.h>
-#include <rte_port_ring.h>
-#include <rte_port_ras.h>
-#include <rte_table_stub.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-void
-app_main_loop_pipeline_ipv4_ras(void) {
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id[APP_MAX_PORTS];
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) ||
-		(core_params->core_type != APP_CORE_IPV4_RAS)) {
-		rte_panic("Core %u misconfiguration\n", core_id);
-	}
-
-	RTE_LOG(INFO, USER1, "Core %u is doing IPv4 reassembly\n", core_id);
-
-	/* Pipeline configuration */
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("%s: Unable to configure the pipeline\n", __func__);
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("%s: Unable to configure input port %i\n",
-				__func__, i);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_ipv4_ras_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ipv4_ras_ops,
-			.arg_create = (void *) &port_ring_ipv4_ras_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("%s: Unable to configure output port %i\n",
-				__func__, i);
-	}
-
-	/* Table configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_stub_ops,
-			.arg_create = NULL,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id[i]))
-			rte_panic("%s: Unable to configure table %u\n",
-				__func__, table_id[i]);
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id[i]))
-			rte_panic("%s: Unable to connect input port %u to "
-				"table %u\n", __func__, port_in_id[i],
-				table_id[i]);
-
-	/* Add entries to tables */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_entry default_entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i]},
-		};
-
-		struct rte_pipeline_table_entry *default_entry_ptr;
-
-		if (rte_pipeline_table_default_entry_add(p, table_id[i],
-			&default_entry,
-			&default_entry_ptr))
-			rte_panic("%s: Unable to add default entry to "
-				"table %u\n", __func__, table_id[i]);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("%s: Pipeline consistency check failed\n", __func__);
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0)
-			rte_pipeline_flush(p);
-	}
-}
diff --git a/examples/ip_pipeline/pipeline_passthrough.c b/examples/ip_pipeline/pipeline_passthrough.c
deleted file mode 100644
index 948b2c1..0000000
--- a/examples/ip_pipeline/pipeline_passthrough.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <rte_malloc.h>
-#include <rte_log.h>
-
-#include <rte_port_ring.h>
-#include <rte_table_stub.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-void
-app_main_loop_pipeline_passthrough(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id[APP_MAX_PORTS];
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_PT))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing pass-through\n", core_id);
-
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("%s: Unable to configure the pipeline\n", __func__);
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i])) {
-			rte_panic("%s: Unable to configure input port for "
-				"ring %d\n", __func__, i);
-		}
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i])) {
-			rte_panic("%s: Unable to configure output port for "
-				"ring %d\n", __func__, i);
-		}
-	}
-
-	/* Table configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_stub_ops,
-			.arg_create = NULL,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id[i]))
-			rte_panic("%s: Unable to configure table %u\n",
-				__func__, i);
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++) {
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id[i])) {
-			rte_panic("%s: Unable to connect input port %u to "
-				"table %u\n", __func__, port_in_id[i],
-				table_id[i]);
-		}
-	}
-
-	/* Add entries to tables */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_entry default_entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i]},
-		};
-
-		struct rte_pipeline_table_entry *default_entry_ptr;
-
-		if (rte_pipeline_table_default_entry_add(p, table_id[i],
-			&default_entry, &default_entry_ptr))
-			rte_panic("%s: Unable to add default entry to "
-				"table %u\n", __func__, table_id[i]);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("%s: Pipeline consistency check failed\n", __func__);
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0)
-			rte_pipeline_flush(p);
-	}
-}
-
-void
-app_main_loop_passthrough(void) {
-	struct app_mbuf_array *m;
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_PT))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing pass-through (no pipeline)\n",
-		core_id);
-
-	m = rte_malloc_socket(NULL, sizeof(struct app_mbuf_array),
-		RTE_CACHE_LINE_SIZE, rte_socket_id());
-	if (m == NULL)
-		rte_panic("%s: cannot allocate buffer space\n", __func__);
-
-	for (i = 0; ; i = ((i + 1) & (app.n_ports - 1))) {
-		int ret;
-
-		ret = rte_ring_sc_dequeue_bulk(
-			app.rings[core_params->swq_in[i]],
-			(void **) m->array,
-			app.bsz_swq_rd);
-
-		if (ret == -ENOENT)
-			continue;
-
-		do {
-			ret = rte_ring_sp_enqueue_bulk(
-				app.rings[core_params->swq_out[i]],
-				(void **) m->array,
-				app.bsz_swq_wr);
-		} while (ret < 0);
-	}
-}
diff --git a/examples/ip_pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline_routing.c
deleted file mode 100644
index b1ce624..0000000
--- a/examples/ip_pipeline/pipeline_routing.c
+++ /dev/null
@@ -1,474 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <rte_malloc.h>
-#include <rte_log.h>
-#include <rte_ethdev.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-#include <rte_byteorder.h>
-
-#include <rte_port_ring.h>
-#include <rte_table_lpm.h>
-#include <rte_table_hash.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-#include <unistd.h>
-
-struct app_routing_table_entry {
-	struct rte_pipeline_table_entry head;
-	uint32_t nh_ip;
-	uint32_t nh_iface;
-};
-
-struct app_arp_table_entry {
-	struct rte_pipeline_table_entry head;
-	struct ether_addr nh_arp;
-};
-
-static inline void
-app_routing_table_write_metadata(
-	struct rte_mbuf *pkt,
-	struct app_routing_table_entry *entry)
-{
-	struct app_pkt_metadata *c =
-		(struct app_pkt_metadata *) RTE_MBUF_METADATA_UINT8_PTR(pkt, 0);
-
-	c->arp_key.nh_ip = entry->nh_ip;
-	c->arp_key.nh_iface = entry->nh_iface;
-}
-
-static int
-app_routing_table_ah(
-	struct rte_mbuf **pkts,
-	uint64_t *pkts_mask,
-	struct rte_pipeline_table_entry **entries,
-	__attribute__((unused)) void *arg)
-{
-	uint64_t pkts_in_mask = *pkts_mask;
-
-	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {
-		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);
-		uint32_t i;
-
-		for (i = 0; i < n_pkts; i++) {
-			struct rte_mbuf *m = pkts[i];
-			struct app_routing_table_entry *a =
-				(struct app_routing_table_entry *) entries[i];
-
-			app_routing_table_write_metadata(m, a);
-		}
-	} else
-		for ( ; pkts_in_mask; ) {
-			struct rte_mbuf *m;
-			struct app_routing_table_entry *a;
-			uint64_t pkt_mask;
-			uint32_t packet_index;
-
-			packet_index = __builtin_ctzll(pkts_in_mask);
-			pkt_mask = 1LLU << packet_index;
-			pkts_in_mask &= ~pkt_mask;
-
-			m = pkts[packet_index];
-			a = (struct app_routing_table_entry *)
-				entries[packet_index];
-			app_routing_table_write_metadata(m, a);
-		}
-
-	return 0;
-}
-
-static inline void
-app_arp_table_write_metadata(
-	struct rte_mbuf *pkt,
-	struct app_arp_table_entry *entry)
-{
-	struct app_pkt_metadata *c =
-		(struct app_pkt_metadata *) RTE_MBUF_METADATA_UINT8_PTR(pkt, 0);
-	ether_addr_copy(&entry->nh_arp, &c->nh_arp);
-}
-
-static int
-app_arp_table_ah(
-	struct rte_mbuf **pkts,
-	uint64_t *pkts_mask,
-	struct rte_pipeline_table_entry **entries,
-	__attribute__((unused)) void *arg)
-{
-	uint64_t pkts_in_mask = *pkts_mask;
-
-	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {
-		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);
-		uint32_t i;
-
-		for (i = 0; i < n_pkts; i++) {
-			struct rte_mbuf *m = pkts[i];
-			struct app_arp_table_entry *a =
-				(struct app_arp_table_entry *) entries[i];
-
-			app_arp_table_write_metadata(m, a);
-		}
-	} else {
-		for ( ; pkts_in_mask; ) {
-			struct rte_mbuf *m;
-			struct app_arp_table_entry *a;
-			uint64_t pkt_mask;
-			uint32_t packet_index;
-
-			packet_index = __builtin_ctzll(pkts_in_mask);
-			pkt_mask = 1LLU << packet_index;
-			pkts_in_mask &= ~pkt_mask;
-
-			m = pkts[packet_index];
-			a = (struct app_arp_table_entry *)
-				entries[packet_index];
-			app_arp_table_write_metadata(m, a);
-		}
-	}
-
-	return 0;
-}
-
-static uint64_t app_arp_table_hash(
-	void *key,
-	__attribute__((unused)) uint32_t key_size,
-	__attribute__((unused)) uint64_t seed)
-{
-	uint32_t *k = (uint32_t *) key;
-
-	return k[1];
-}
-
-struct app_core_routing_message_handle_params {
-	struct rte_ring *ring_req;
-	struct rte_ring *ring_resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t routing_table_id;
-	uint32_t arp_table_id;
-};
-
-static void
-app_message_handle(struct app_core_routing_message_handle_params *params);
-
-void
-app_main_loop_pipeline_routing(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t routing_table_id, arp_table_id;
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-	struct app_core_routing_message_handle_params mh_params;
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_RT))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing routing\n", core_id);
-
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("Unable to configure the pipeline\n");
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("Unable to configure input port for "
-				"ring %d\n", i);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("Unable to configure output port for "
-				"ring %d\n", i);
-	}
-
-	/* Routing table configuration */
-	{
-		struct rte_table_lpm_params table_lpm_params = {
-			.n_rules = app.max_routing_rules,
-			.entry_unique_size =
-				sizeof(struct app_routing_table_entry),
-			.offset = __builtin_offsetof(struct app_pkt_metadata,
-				flow_key.ip_dst),
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_lpm_ops,
-			.arg_create = &table_lpm_params,
-			.f_action_hit = app_routing_table_ah,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size =
-				sizeof(struct app_routing_table_entry) -
-				sizeof(struct rte_pipeline_table_entry),
-		};
-
-		if (rte_pipeline_table_create(p, &table_params,
-			&routing_table_id))
-			rte_panic("Unable to configure the LPM table\n");
-	}
-
-	/* ARP table configuration */
-	{
-		struct rte_table_hash_key8_lru_params table_arp_params = {
-			.n_entries = app.max_arp_rules,
-			.f_hash = app_arp_table_hash,
-			.seed = 0,
-			.signature_offset = 0, /* Unused */
-			.key_offset = __builtin_offsetof(
-				struct app_pkt_metadata, arp_key),
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_hash_key8_lru_dosig_ops,
-			.arg_create = &table_arp_params,
-			.f_action_hit = app_arp_table_ah,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = sizeof(struct app_arp_table_entry) -
-				sizeof(struct rte_pipeline_table_entry),
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &arp_table_id))
-			rte_panic("Unable to configure the ARP table\n");
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++) {
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			routing_table_id))
-			rte_panic("Unable to connect input port %u to "
-				"table %u\n", port_in_id[i],  routing_table_id);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("Pipeline consistency check failed\n");
-
-	/* Message handling */
-	mh_params.ring_req =
-		app_get_ring_req(app_get_first_core_id(APP_CORE_RT));
-	mh_params.ring_resp =
-		app_get_ring_resp(app_get_first_core_id(APP_CORE_RT));
-	mh_params.p = p;
-	mh_params.port_out_id = port_out_id;
-	mh_params.routing_table_id = routing_table_id;
-	mh_params.arp_table_id = arp_table_id;
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0) {
-			rte_pipeline_flush(p);
-			app_message_handle(&mh_params);
-		}
-	}
-}
-
-void
-app_message_handle(struct app_core_routing_message_handle_params *params)
-{
-	struct rte_ring *ring_req = params->ring_req;
-	struct rte_ring *ring_resp;
-	void *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t routing_table_id, arp_table_id;
-	int result;
-
-	/* Read request message */
-	result = rte_ring_sc_dequeue(ring_req, &msg);
-	if (result != 0)
-		return;
-
-	ring_resp = params->ring_resp;
-	p = params->p;
-	port_out_id = params->port_out_id;
-	routing_table_id = params->routing_table_id;
-	arp_table_id = params->arp_table_id;
-
-	/* Handle request */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	switch (req->type) {
-	case APP_MSG_REQ_PING:
-	{
-		result = 0;
-		break;
-	}
-
-	case APP_MSG_REQ_RT_ADD:
-	{
-		struct app_routing_table_entry entry = {
-			.head = {
-				.action = RTE_PIPELINE_ACTION_TABLE,
-				{.table_id = arp_table_id},
-			},
-			.nh_ip = req->routing_add.nh_ip,
-			.nh_iface = port_out_id[req->routing_add.port],
-		};
-
-		struct rte_table_lpm_key key = {
-			.ip = req->routing_add.ip,
-			.depth = req->routing_add.depth,
-		};
-
-		struct rte_pipeline_table_entry *entry_ptr;
-
-		int key_found;
-
-		result = rte_pipeline_table_entry_add(p, routing_table_id, &key,
-			(struct rte_pipeline_table_entry *) &entry, &key_found,
-			&entry_ptr);
-		break;
-	}
-
-	case APP_MSG_REQ_RT_DEL:
-	{
-		struct rte_table_lpm_key key = {
-			.ip = req->routing_del.ip,
-			.depth = req->routing_del.depth,
-		};
-
-		int key_found;
-
-		result = rte_pipeline_table_entry_delete(p, routing_table_id,
-			&key, &key_found, NULL);
-		break;
-	}
-
-	case APP_MSG_REQ_ARP_ADD:
-	{
-
-		struct app_arp_table_entry entry = {
-			.head = {
-				.action = RTE_PIPELINE_ACTION_PORT,
-				{.port_id =
-					port_out_id[req->arp_add.out_iface]},
-			},
-			.nh_arp = req->arp_add.nh_arp,
-		};
-
-		struct app_arp_key arp_key = {
-			.nh_ip = req->arp_add.nh_ip,
-			.nh_iface = port_out_id[req->arp_add.out_iface],
-		};
-
-		struct rte_pipeline_table_entry *entry_ptr;
-
-		int key_found;
-
-		result = rte_pipeline_table_entry_add(p, arp_table_id, &arp_key,
-			(struct rte_pipeline_table_entry *) &entry, &key_found,
-			&entry_ptr);
-		break;
-	}
-
-	case APP_MSG_REQ_ARP_DEL:
-	{
-		struct app_arp_key arp_key = {
-			.nh_ip = req->arp_del.nh_ip,
-			.nh_iface = port_out_id[req->arp_del.out_iface],
-		};
-
-		int key_found;
-
-		result = rte_pipeline_table_entry_delete(p, arp_table_id,
-			&arp_key, &key_found, NULL);
-		break;
-	}
-
-	default:
-		rte_panic("RT Unrecognized message type (%u)\n", req->type);
-	}
-
-	/* Fill in response message */
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	resp->result = result;
-
-	/* Send response */
-	do {
-		result = rte_ring_sp_enqueue(ring_resp, msg);
-	} while (result == -ENOBUFS);
-}
diff --git a/examples/ip_pipeline/pipeline_rx.c b/examples/ip_pipeline/pipeline_rx.c
deleted file mode 100644
index 383f1a9..0000000
--- a/examples/ip_pipeline/pipeline_rx.c
+++ /dev/null
@@ -1,385 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_malloc.h>
-#include <rte_ethdev.h>
-#include <rte_mbuf.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-#include <rte_jhash.h>
-
-#include <rte_port_ethdev.h>
-#include <rte_port_ring.h>
-#include <rte_table_stub.h>
-#include <rte_pipeline.h>
-
-
-#include "main.h"
-
-struct app_core_rx_message_handle_params {
-	struct rte_ring *ring_req;
-	struct rte_ring *ring_resp;
-
-	struct rte_pipeline *p;
-	uint32_t *port_in_id;
-};
-
-static void
-app_message_handle(struct app_core_rx_message_handle_params *params);
-
-static int
-app_pipeline_rx_port_in_action_handler(struct rte_mbuf **pkts, uint32_t n,
-	uint64_t *pkts_mask, void *arg);
-
-void
-app_main_loop_pipeline_rx(void) {
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id[APP_MAX_PORTS];
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-	struct app_core_rx_message_handle_params mh_params;
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_RX))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing RX\n", core_id);
-
-	/* Pipeline configuration */
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("%s: Unable to configure the pipeline\n", __func__);
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ethdev_reader_params port_ethdev_params = {
-			.port_id = app.ports[i],
-			.queue_id = 0,
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ethdev_reader_ops,
-			.arg_create = (void *) &port_ethdev_params,
-			.f_action = app_pipeline_rx_port_in_action_handler,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_hwq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("%s: Unable to configure input port for "
-				"port %d\n", __func__, app.ports[i]);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("%s: Unable to configure output port for "
-				"ring RX %i\n", __func__, i);
-	}
-
-	/* Table configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_stub_ops,
-			.arg_create = NULL,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id[i]))
-			rte_panic("%s: Unable to configure table %u\n",
-				__func__, table_id[i]);
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id[i]))
-			rte_panic("%s: Unable to connect input port %u to "
-				"table %u\n", __func__, port_in_id[i],
-				table_id[i]);
-
-	/* Add entries to tables */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_entry default_entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i]},
-		};
-
-		struct rte_pipeline_table_entry *default_entry_ptr;
-
-		if (rte_pipeline_table_default_entry_add(p, table_id[i],
-			&default_entry, &default_entry_ptr))
-			rte_panic("%s: Unable to add default entry to "
-				"table %u\n", __func__, table_id[i]);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("%s: Pipeline consistency check failed\n", __func__);
-
-	/* Message handling */
-	mh_params.ring_req =
-		app_get_ring_req(app_get_first_core_id(APP_CORE_RX));
-	mh_params.ring_resp =
-		app_get_ring_resp(app_get_first_core_id(APP_CORE_RX));
-	mh_params.p = p;
-	mh_params.port_in_id = port_in_id;
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0) {
-			rte_pipeline_flush(p);
-			app_message_handle(&mh_params);
-		}
-	}
-}
-
-uint64_t test_hash(
-	void *key,
-	__attribute__((unused)) uint32_t key_size,
-	__attribute__((unused)) uint64_t seed)
-{
-	struct app_flow_key *flow_key = (struct app_flow_key *) key;
-	uint32_t ip_dst = rte_be_to_cpu_32(flow_key->ip_dst);
-	uint64_t signature = (ip_dst & 0x00FFFFFFLLU) >> 2;
-
-	return signature;
-}
-
-uint32_t
-rte_jhash2_16(uint32_t *k, uint32_t initval)
-{
-	uint32_t a, b, c;
-
-	a = b = RTE_JHASH_GOLDEN_RATIO;
-	c = initval;
-
-	a += k[0];
-	b += k[1];
-	c += k[2];
-	__rte_jhash_mix(a, b, c);
-
-	c += 16; /* length in bytes */
-	a += k[3]; /* Remaining word */
-
-	__rte_jhash_mix(a, b, c);
-
-	return c;
-}
-
-static inline void
-app_pkt_metadata_fill(struct rte_mbuf *m)
-{
-	uint8_t *m_data = rte_pktmbuf_mtod(m, uint8_t *);
-	struct app_pkt_metadata *c =
-		(struct app_pkt_metadata *) RTE_MBUF_METADATA_UINT8_PTR(m, 0);
-	struct ipv4_hdr *ip_hdr =
-		(struct ipv4_hdr *) &m_data[sizeof(struct ether_hdr)];
-	uint64_t *ipv4_hdr_slab = (uint64_t *) ip_hdr;
-
-	/* TTL and Header Checksum are set to 0 */
-	c->flow_key.slab0 = ipv4_hdr_slab[1] & 0xFFFFFFFF0000FF00LLU;
-	c->flow_key.slab1 = ipv4_hdr_slab[2];
-	c->signature = test_hash((void *) &c->flow_key, 0, 0);
-
-	/* Pop Ethernet header */
-	if (app.ether_hdr_pop_push) {
-		rte_pktmbuf_adj(m, (uint16_t)sizeof(struct ether_hdr));
-		m->l2_len = 0;
-		m->l3_len = sizeof(struct ipv4_hdr);
-	}
-}
-
-int
-app_pipeline_rx_port_in_action_handler(
-	struct rte_mbuf **pkts,
-	uint32_t n,
-	uint64_t *pkts_mask,
-	__rte_unused void *arg)
-{
-	uint32_t i;
-
-	for (i = 0; i < n; i++) {
-		struct rte_mbuf *m = pkts[i];
-
-		app_pkt_metadata_fill(m);
-	}
-
-	*pkts_mask = (~0LLU) >> (64 - n);
-
-	return 0;
-}
-
-void
-app_main_loop_rx(void) {
-	struct app_mbuf_array *ma;
-	uint32_t i, j;
-	int ret;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_RX))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing RX (no pipeline)\n", core_id);
-
-	ma = rte_malloc_socket(NULL, sizeof(struct app_mbuf_array),
-		RTE_CACHE_LINE_SIZE, rte_socket_id());
-	if (ma == NULL)
-		rte_panic("%s: cannot allocate buffer space\n", __func__);
-
-	for (i = 0; ; i = ((i + 1) & (app.n_ports - 1))) {
-		uint32_t n_mbufs;
-
-		n_mbufs = rte_eth_rx_burst(
-			app.ports[i],
-			0,
-			ma->array,
-			app.bsz_hwq_rd);
-
-		if (n_mbufs == 0)
-			continue;
-
-		for (j = 0; j < n_mbufs; j++) {
-			struct rte_mbuf *m = ma->array[j];
-
-			app_pkt_metadata_fill(m);
-		}
-
-		do {
-			ret = rte_ring_sp_enqueue_bulk(
-				app.rings[core_params->swq_out[i]],
-				(void **) ma->array,
-				n_mbufs);
-		} while (ret < 0);
-	}
-}
-
-void
-app_message_handle(struct app_core_rx_message_handle_params *params)
-{
-	struct rte_ring *ring_req = params->ring_req;
-	struct rte_ring *ring_resp;
-	void *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	struct rte_pipeline *p;
-	uint32_t *port_in_id;
-	int result;
-
-	/* Read request message */
-	result = rte_ring_sc_dequeue(ring_req, &msg);
-	if (result != 0)
-		return;
-
-	ring_resp = params->ring_resp;
-	p = params->p;
-	port_in_id = params->port_in_id;
-
-	/* Handle request */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	switch (req->type) {
-	case APP_MSG_REQ_PING:
-	{
-		result = 0;
-		break;
-	}
-
-	case APP_MSG_REQ_RX_PORT_ENABLE:
-	{
-		result = rte_pipeline_port_in_enable(p,
-			port_in_id[req->rx_up.port]);
-		break;
-	}
-
-	case APP_MSG_REQ_RX_PORT_DISABLE:
-	{
-		result = rte_pipeline_port_in_disable(p,
-			port_in_id[req->rx_down.port]);
-		break;
-	}
-
-	default:
-		rte_panic("RX Unrecognized message type (%u)\n", req->type);
-	}
-
-	/* Fill in response message */
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	resp->result = result;
-
-	/* Send response */
-	do {
-		result = rte_ring_sp_enqueue(ring_resp, msg);
-	} while (result == -ENOBUFS);
-}
diff --git a/examples/ip_pipeline/pipeline_tx.c b/examples/ip_pipeline/pipeline_tx.c
deleted file mode 100644
index 0077c12..0000000
--- a/examples/ip_pipeline/pipeline_tx.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_malloc.h>
-#include <rte_ethdev.h>
-#include <rte_mbuf.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-
-#include <rte_port_ethdev.h>
-#include <rte_port_ring.h>
-#include <rte_table_stub.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-static struct ether_addr local_ether_addr = {
-	.addr_bytes = {0, 1, 2, 3, 4, 5},
-};
-
-static inline void
-app_pkt_metadata_flush(struct rte_mbuf *pkt)
-{
-	struct app_pkt_metadata *pkt_meta = (struct app_pkt_metadata *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkt, 0);
-	struct ether_hdr *ether_hdr = (struct ether_hdr *)
-		rte_pktmbuf_prepend(pkt, (uint16_t) sizeof(struct ether_hdr));
-
-	ether_addr_copy(&pkt_meta->nh_arp, &ether_hdr->d_addr);
-	ether_addr_copy(&local_ether_addr, &ether_hdr->s_addr);
-	ether_hdr->ether_type = rte_bswap16(ETHER_TYPE_IPv4);
-	pkt->l2_len = sizeof(struct ether_hdr);
-}
-
-static int
-app_pipeline_tx_port_in_action_handler(
-	struct rte_mbuf **pkts,
-	uint32_t n,
-	uint64_t *pkts_mask,
-	__rte_unused void *arg)
-{
-	uint32_t i;
-
-	for (i = 0; i < n; i++) {
-		struct rte_mbuf *m = pkts[i];
-
-		app_pkt_metadata_flush(m);
-	}
-
-	*pkts_mask = (~0LLU) >> (64 - n);
-
-	return 0;
-}
-
-void
-app_main_loop_pipeline_tx(void) {
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id[APP_MAX_PORTS];
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_TX))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing TX\n", core_id);
-
-	/* Pipeline configuration */
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("%s: Unable to configure the pipeline\n", __func__);
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = (app.ether_hdr_pop_push) ?
-				app_pipeline_tx_port_in_action_handler : NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i])) {
-			rte_panic("%s: Unable to configure input port for "
-				"ring TX %i\n", __func__, i);
-		}
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ethdev_writer_params port_ethdev_params = {
-			.port_id = app.ports[i],
-			.queue_id = 0,
-			.tx_burst_sz = app.bsz_hwq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ethdev_writer_ops,
-			.arg_create = (void *) &port_ethdev_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i])) {
-			rte_panic("%s: Unable to configure output port for "
-				"port %d\n", __func__, app.ports[i]);
-		}
-	}
-
-	/* Table configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_stub_ops,
-			.arg_create = NULL,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id[i])) {
-			rte_panic("%s: Unable to configure table %u\n",
-				__func__, table_id[i]);
-		}
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id[i]))
-			rte_panic("%s: Unable to connect input port %u to "
-				"table %u\n", __func__, port_in_id[i],
-				table_id[i]);
-
-	/* Add entries to tables */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_entry default_entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i]},
-		};
-
-		struct rte_pipeline_table_entry *default_entry_ptr;
-
-		if (rte_pipeline_table_default_entry_add(p, table_id[i],
-			&default_entry, &default_entry_ptr))
-			rte_panic("%s: Unable to add default entry to "
-				"table %u\n", __func__, table_id[i]);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("%s: Pipeline consistency check failed\n", __func__);
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0)
-			rte_pipeline_flush(p);
-	}
-}
-
-void
-app_main_loop_tx(void) {
-	struct app_mbuf_array *m[APP_MAX_PORTS];
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_TX))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing TX (no pipeline)\n", core_id);
-
-	for (i = 0; i < APP_MAX_PORTS; i++) {
-		m[i] = rte_malloc_socket(NULL, sizeof(struct app_mbuf_array),
-			RTE_CACHE_LINE_SIZE, rte_socket_id());
-		if (m[i] == NULL)
-			rte_panic("%s: Cannot allocate buffer space\n",
-				__func__);
-	}
-
-	for (i = 0; ; i = ((i + 1) & (app.n_ports - 1))) {
-		uint32_t n_mbufs, n_pkts;
-		int ret;
-
-		n_mbufs = m[i]->n_mbufs;
-
-		ret = rte_ring_sc_dequeue_bulk(
-			app.rings[core_params->swq_in[i]],
-			(void **) &m[i]->array[n_mbufs],
-			app.bsz_swq_rd);
-
-		if (ret == -ENOENT)
-			continue;
-
-		n_mbufs += app.bsz_swq_rd;
-
-		if (n_mbufs < app.bsz_hwq_wr) {
-			m[i]->n_mbufs = n_mbufs;
-			continue;
-		}
-
-		n_pkts = rte_eth_tx_burst(
-			app.ports[i],
-			0,
-			m[i]->array,
-			n_mbufs);
-
-		if (n_pkts < n_mbufs) {
-			uint32_t k;
-
-			for (k = n_pkts; k < n_mbufs; k++) {
-				struct rte_mbuf *pkt_to_free;
-
-				pkt_to_free = m[i]->array[k];
-				rte_pktmbuf_free(pkt_to_free);
-			}
-		}
-
-		m[i]->n_mbufs = 0;
-	}
-}
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (3 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 04/11] ip_pipeline: moved pipelines to separate folder Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-08 22:33   ` Thomas Monjalon
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 06/11] ip_pipeline: added application thread Maciej Gajdzica
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

From: Jasvinder Singh <jasvinder.singh@intel.com>

Master pipeline is responsible for command line handling and
communicationg with all other pipelines via message queues. Removed
cmdline.c file, as its functionality will be split over multiple
pipeline files.

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
---
 examples/ip_pipeline/Makefile                      |    5 +
 examples/ip_pipeline/cmdline.c                     | 1976 --------------------
 examples/ip_pipeline/init.c                        |    5 +
 examples/ip_pipeline/pipeline/pipeline_common_be.c |  206 ++
 examples/ip_pipeline/pipeline/pipeline_common_be.h |  163 ++
 examples/ip_pipeline/pipeline/pipeline_common_fe.c | 1333 +++++++++++++
 examples/ip_pipeline/pipeline/pipeline_common_fe.h |  228 +++
 examples/ip_pipeline/pipeline/pipeline_master.c    |   47 +
 examples/ip_pipeline/pipeline/pipeline_master.h    |   41 +
 examples/ip_pipeline/pipeline/pipeline_master_be.c |  150 ++
 examples/ip_pipeline/pipeline/pipeline_master_be.h |   41 +
 11 files changed, 2219 insertions(+), 1976 deletions(-)
 delete mode 100644 examples/ip_pipeline/cmdline.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_be.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_fe.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_common_fe.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_master_be.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 213e879..9ce80a8 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -55,6 +55,11 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_check.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
 
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_master_be.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_master.c
+
 CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS) -Wno-error=unused-function -Wno-error=unused-variable
diff --git a/examples/ip_pipeline/cmdline.c b/examples/ip_pipeline/cmdline.c
deleted file mode 100644
index 3173fd0..0000000
--- a/examples/ip_pipeline/cmdline.c
+++ /dev/null
@@ -1,1976 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <termios.h>
-#include <inttypes.h>
-#include <string.h>
-#include <netinet/in.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <rte_ether.h>
-#include <rte_byteorder.h>
-#include <rte_ring.h>
-#include <rte_mbuf.h>
-#include <rte_malloc.h>
-#include <rte_string_fns.h>
-#include <cmdline_rdline.h>
-#include <cmdline_parse.h>
-#include <cmdline_parse_num.h>
-#include <cmdline_parse_string.h>
-#include <cmdline_parse_ipaddr.h>
-#include <cmdline_parse_etheraddr.h>
-#include <cmdline_socket.h>
-#include <cmdline.h>
-
-#include "main.h"
-
-#define IS_RULE_PRESENT(res, rule_key, table, type)			\
-do {									\
-	struct app_rule *it;						\
-									\
-	(res) = NULL;							\
-	TAILQ_FOREACH(it, &table, entries) {				\
-		if (memcmp(&rule_key, &it->type.key, sizeof(rule_key)) == 0) {\
-			(res) = it;					\
-			break;						\
-		}							\
-	}								\
-} while (0)
-
-/* Rules */
-static void
-app_init_rule_tables(void);
-
-TAILQ_HEAD(linked_list, app_rule) arp_table, routing_table, firewall_table,
-	flow_table;
-
-uint32_t n_arp_rules;
-uint32_t n_routing_rules;
-uint32_t n_firewall_rules;
-uint32_t n_flow_rules;
-
-struct app_arp_rule {
-	struct {
-		uint8_t out_iface;
-		uint32_t nh_ip;
-	} key;
-
-	struct ether_addr nh_arp;
-};
-
-struct app_routing_rule {
-	struct {
-		uint32_t ip;
-		uint8_t depth;
-	} key;
-
-	uint8_t port;
-	uint32_t nh_ip;
-};
-
-struct app_firewall_rule {
-	struct {
-		uint32_t src_ip;
-		uint32_t src_ip_mask;
-		uint32_t dst_ip;
-		uint32_t dst_ip_mask;
-		uint16_t src_port_from;
-		uint16_t src_port_to;
-		uint16_t dst_port_from;
-		uint16_t dst_port_to;
-		uint8_t proto;
-		uint8_t proto_mask;
-	} key;
-
-	int32_t priority;
-	uint8_t port;
-};
-
-struct app_flow_rule {
-	struct {
-		uint32_t src_ip;
-		uint32_t dst_ip;
-		uint16_t src_port;
-		uint16_t dst_port;
-		uint8_t proto;
-	} key;
-
-	uint8_t port;
-};
-
-struct app_rule {
-	union {
-		struct app_arp_rule arp;
-		struct app_routing_rule routing;
-		struct app_firewall_rule firewall;
-		struct app_flow_rule flow;
-	};
-
-	TAILQ_ENTRY(app_rule) entries;
-};
-
-/* Initialization */
-static void
-app_init_rule_tables(void)
-{
-	TAILQ_INIT(&arp_table);
-	TAILQ_INIT(&routing_table);
-	TAILQ_INIT(&firewall_table);
-	TAILQ_INIT(&flow_table);
-
-	n_arp_rules = 0;
-	n_routing_rules = 0;
-	n_firewall_rules = 0;
-	n_flow_rules = 0;
-}
-
-/* Printing */
-static void
-print_arp_rule(struct app_arp_rule rule)
-{
-	printf("(Iface = %u, Address = %u.%u.%u.%u) => "
-		"HWaddress = %02x:%02x:%02x:%02x:%02x:%02x\n",
-		rule.key.out_iface,
-		(rule.key.nh_ip >> 24) & 0xFF,
-		(rule.key.nh_ip >> 16) & 0xFF,
-		(rule.key.nh_ip >> 8) & 0xFF,
-		rule.key.nh_ip & 0xFF,
-
-		rule.nh_arp.addr_bytes[0],
-		rule.nh_arp.addr_bytes[1],
-		rule.nh_arp.addr_bytes[2],
-		rule.nh_arp.addr_bytes[3],
-		rule.nh_arp.addr_bytes[4],
-		rule.nh_arp.addr_bytes[5]);
-}
-
-static void
-print_routing_rule(struct app_routing_rule rule)
-{
-	printf("IP Prefix = %u.%u.%u.%u/%u => "
-		"(Iface = %u, Gateway = %u.%u.%u.%u)\n",
-		(rule.key.ip >> 24) & 0xFF,
-		(rule.key.ip >> 16) & 0xFF,
-		(rule.key.ip >> 8) & 0xFF,
-		rule.key.ip & 0xFF,
-
-		rule.key.depth,
-		rule.port,
-
-		(rule.nh_ip >> 24) & 0xFF,
-		(rule.nh_ip >> 16) & 0xFF,
-		(rule.nh_ip >> 8) & 0xFF,
-		rule.nh_ip & 0xFF);
-}
-
-#ifdef RTE_LIBRTE_ACL
-
-static void
-print_firewall_rule(struct app_firewall_rule rule)
-{
-	printf("Priority %d: (IP Src = %u.%u.%u.%u/%u, "
-		"IP Dst = %u.%u.%u.%u/%u, "
-		"Port Src = %u-%u, Port Dst = %u-%u, Proto = %u (%u)) => "
-		"Port = %u\n",
-		rule.priority,
-
-		(rule.key.src_ip >> 24) & 0xFF,
-		(rule.key.src_ip >> 16) & 0xFF,
-		(rule.key.src_ip >> 8) & 0xFF,
-		rule.key.src_ip & 0xFF,
-		rule.key.src_ip_mask,
-
-		(rule.key.dst_ip >> 24) & 0xFF,
-		(rule.key.dst_ip >> 16) & 0xFF,
-		(rule.key.dst_ip >> 8) & 0xFF,
-		rule.key.dst_ip & 0xFF,
-		rule.key.dst_ip_mask,
-
-		rule.key.src_port_from,
-		rule.key.src_port_to,
-		rule.key.dst_port_from,
-		rule.key.dst_port_to,
-		rule.key.proto,
-		rule.key.proto_mask,
-		rule.port);
-}
-
-#endif
-
-static void
-print_flow_rule(struct app_flow_rule rule)
-{
-	printf("(IP Src = %u.%u.%u.%u, IP Dst = %u.%u.%u.%u, Port Src = %u, "
-		"Port Dst = %u, Proto = %u) => Port = %u\n",
-		(rule.key.src_ip >> 24) & 0xFF,
-		(rule.key.src_ip >> 16) & 0xFF,
-		(rule.key.src_ip >> 8) & 0xFF,
-		rule.key.src_ip & 0xFF,
-
-		(rule.key.dst_ip >> 24) & 0xFF,
-		(rule.key.dst_ip >> 16) & 0xFF,
-		(rule.key.dst_ip >> 8) & 0xFF,
-		rule.key.dst_ip  & 0xFF,
-
-		rule.key.src_port,
-		rule.key.dst_port,
-		(uint32_t) rule.key.proto,
-		rule.port);
-}
-
-/* Commands */
-
-/* *** Run file (script) *** */
-struct cmd_run_file_result {
-	cmdline_fixed_string_t run_string;
-	char file_path[100];
-};
-
-static void
-cmd_run_file_parsed(
-	void *parsed_result,
-	struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_run_file_result *params = parsed_result;
-	struct cmdline *file_cl;
-	int fd;
-
-	fd = open(params->file_path, O_RDONLY, 0);
-	if (fd < 0) {
-		printf("Illegal value for file path (%s)\n", params->file_path);
-		return;
-	}
-
-	file_cl = cmdline_new(cl->ctx, "", fd, 1);
-	cmdline_interact(file_cl);
-	close(fd);
-}
-
-cmdline_parse_token_string_t cmd_run_file_run_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_run_file_result, run_string, "run");
-
-cmdline_parse_token_string_t cmd_run_file_file_path =
-	TOKEN_STRING_INITIALIZER(struct cmd_run_file_result, file_path, NULL);
-
-cmdline_parse_inst_t cmd_run_file = {
-	.f = cmd_run_file_parsed,
-	.data = NULL,
-	.help_str = "Run commands from file",
-	.tokens = {
-		(void *)&cmd_run_file_run_string,
-		(void *)&cmd_run_file_file_path,
-		NULL,
-	},
-};
-
-/* *** Link - Enable *** */
-struct cmd_link_enable_result {
-	cmdline_fixed_string_t link_string;
-	uint8_t port;
-	cmdline_fixed_string_t up_string;
-};
-
-static void
-cmd_link_enable_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_link_enable_result *params = parsed_result;
-	void *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_RX);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("RX core not preformed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if (params->port >= app.n_ports) {
-		printf("Illegal value for port parameter (%u)\n", params->port);
-		return;
-	}
-
-	printf("Enabling port %d\n", params->port);
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	req->type = APP_MSG_REQ_RX_PORT_ENABLE;
-	req->rx_up.port = params->port;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request LINK_UP failed (%u)\n", resp->result);
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free(msg);
-}
-
-cmdline_parse_token_string_t cmd_link_enable_link_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_link_enable_result, link_string,
-	"link");
-
-cmdline_parse_token_num_t cmd_link_enable_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_link_enable_result, port, UINT8);
-
-cmdline_parse_token_string_t cmd_link_enable_up_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_link_enable_result, up_string,
-	"up");
-
-cmdline_parse_inst_t cmd_link_enable = {
-	.f = cmd_link_enable_parsed,
-	.data = NULL,
-	.help_str = "Link down",
-	.tokens = {
-		(void *)&cmd_link_enable_link_string,
-		(void *)&cmd_link_enable_port,
-		(void *)&cmd_link_enable_up_string,
-		NULL,
-	},
-};
-
-/* *** Link - Disable *** */
-struct cmd_link_disable_result {
-	cmdline_fixed_string_t link_string;
-	uint8_t port;
-	cmdline_fixed_string_t down_string;
-};
-
-static void
-cmd_link_disable_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_link_disable_result *params = parsed_result;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_RX);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("RX not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if (params->port >= app.n_ports) {
-		printf("Illegal value for port parameter (%u)\n", params->port);
-		return;
-	}
-
-	printf("Disabling port %d\n", params->port);
-
-	/* Allocate message buffer */
-	msg = rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	req->type = APP_MSG_REQ_RX_PORT_DISABLE;
-	req->rx_down.port = params->port;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request LINK_DOWN failed (%u)\n", resp->result);
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *)msg);
-}
-
-cmdline_parse_token_string_t cmd_link_disable_link_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_link_disable_result, link_string,
-	"link");
-
-cmdline_parse_token_num_t cmd_link_disable_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_link_disable_result, port, UINT8);
-
-cmdline_parse_token_string_t cmd_link_disable_down_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_link_disable_result, down_string,
-	"down");
-
-cmdline_parse_inst_t cmd_link_disable = {
-	.f = cmd_link_disable_parsed,
-	.data = NULL,
-	.help_str = "Link up",
-	.tokens = {
-		(void *)&cmd_link_disable_link_string,
-		(void *)&cmd_link_disable_port,
-		(void *)&cmd_link_disable_down_string,
-		NULL,
-	},
-};
-
-
-/* *** ARP - Add *** */
-struct cmd_arp_add_result {
-	cmdline_fixed_string_t arp_string;
-	cmdline_fixed_string_t add_string;
-	uint8_t out_iface;
-	cmdline_ipaddr_t nh_ip;
-	struct ether_addr nh_arp;
-
-};
-
-static void
-cmd_arp_add_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_arp_add_result *params = parsed_result;
-	struct app_rule rule, *old_rule;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_RT);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("ARP not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if (params->out_iface >= app.n_ports) {
-		printf("Illegal value for output interface parameter (%u)\n",
-			params->out_iface);
-		return;
-	}
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.arp.key.out_iface = params->out_iface;
-	rule.arp.key.nh_ip =
-		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
-	rule.arp.nh_arp = params->nh_arp;
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.arp.key, arp_table, arp);
-	if ((old_rule == NULL) && (n_arp_rules == app.max_arp_rules)) {
-		printf("ARP table is full.\n");
-		return;
-	}
-
-	printf("Adding ARP entry: ");
-	print_arp_rule(rule.arp);
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	req->type = APP_MSG_REQ_ARP_ADD;
-	req->arp_add.out_iface = rule.arp.key.out_iface;
-	req->arp_add.nh_ip = rule.arp.key.nh_ip;
-	req->arp_add.nh_arp = rule.arp.nh_arp;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request ARP_ADD failed (%u)\n", resp->result);
-	else {
-		if (old_rule == NULL) {
-			struct app_rule *new_rule = (struct app_rule *)
-				rte_zmalloc_socket("CLI",
-				sizeof(struct app_rule),
-				RTE_CACHE_LINE_SIZE,
-				rte_socket_id());
-
-			if (new_rule == NULL)
-				rte_panic("Unable to allocate new rule\n");
-
-			memcpy(new_rule, &rule, sizeof(rule));
-			TAILQ_INSERT_TAIL(&arp_table, new_rule, entries);
-			n_arp_rules++;
-		} else
-			old_rule->arp.nh_arp = rule.arp.nh_arp;
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *) msg);
-}
-
-cmdline_parse_token_string_t cmd_arp_add_arp_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_result, arp_string, "arp");
-
-cmdline_parse_token_string_t cmd_arp_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_result, add_string, "add");
-
-cmdline_parse_token_num_t cmd_arp_add_out_iface =
-	TOKEN_NUM_INITIALIZER(struct cmd_arp_add_result, out_iface, UINT8);
-
-cmdline_parse_token_ipaddr_t cmd_arp_add_nh_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_arp_add_result, nh_ip);
-
-cmdline_parse_token_etheraddr_t cmd_arp_add_nh_arp =
-	TOKEN_ETHERADDR_INITIALIZER(struct cmd_arp_add_result, nh_arp);
-
-cmdline_parse_inst_t cmd_arp_add = {
-	.f = cmd_arp_add_parsed,
-	.data = NULL,
-	.help_str = "ARP add",
-	.tokens = {
-		(void *)&cmd_arp_add_arp_string,
-		(void *)&cmd_arp_add_add_string,
-		(void *)&cmd_arp_add_out_iface,
-		(void *)&cmd_arp_add_nh_ip,
-		(void *)&cmd_arp_add_nh_arp,
-		NULL,
-	},
-	};
-
-/* *** ARP - Del *** */
-struct cmd_arp_del_result {
-	cmdline_fixed_string_t arp_string;
-	cmdline_fixed_string_t del_string;
-	uint8_t out_iface;
-	cmdline_ipaddr_t nh_ip;
-};
-
-static void
-cmd_arp_del_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_arp_del_result *params = parsed_result;
-	struct app_rule rule, *old_rule;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_RT);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("ARP not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if (params->out_iface > app.n_ports) {
-		printf("Illegal value for output interface parameter (%u)\n",
-			params->out_iface);
-		return;
-	}
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.arp.key.out_iface = params->out_iface;
-	rule.arp.key.nh_ip =
-		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.arp.key, arp_table, arp);
-	if (old_rule == NULL)
-		return;
-
-	printf("Deleting ARP entry: ");
-	print_arp_rule(old_rule->arp);
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	req->type = APP_MSG_REQ_ARP_DEL;
-	req->arp_del.out_iface = rule.arp.key.out_iface;
-	req->arp_del.nh_ip = rule.arp.key.nh_ip;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request ARP_DEL failed (%u)\n", resp->result);
-	else {
-		TAILQ_REMOVE(&arp_table, old_rule, entries);
-		n_arp_rules--;
-		rte_free(old_rule);
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *) msg);
-}
-
-cmdline_parse_token_string_t cmd_arp_del_arp_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_result, arp_string, "arp");
-
-cmdline_parse_token_string_t cmd_arp_del_del_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_result, del_string, "del");
-
-cmdline_parse_token_num_t cmd_arp_del_out_iface =
-	TOKEN_NUM_INITIALIZER(struct cmd_arp_del_result, out_iface, UINT8);
-
-cmdline_parse_token_ipaddr_t cmd_arp_del_nh_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_arp_del_result, nh_ip);
-
-cmdline_parse_inst_t cmd_arp_del = {
-	.f = cmd_arp_del_parsed,
-	.data = NULL,
-	.help_str = "ARP delete",
-	.tokens = {
-		(void *)&cmd_arp_del_arp_string,
-		(void *)&cmd_arp_del_del_string,
-		(void *)&cmd_arp_del_out_iface,
-		(void *)&cmd_arp_del_nh_ip,
-		NULL,
-	},
-};
-
-/* *** ARP - Print *** */
-struct cmd_arp_print_result {
-	cmdline_fixed_string_t arp_string;
-	cmdline_fixed_string_t print_string;
-};
-
-static void
-cmd_arp_print_parsed(
-	__attribute__((unused)) void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct app_rule *it;
-
-	TAILQ_FOREACH(it, &arp_table, entries) {
-		print_arp_rule(it->arp);
-	}
-}
-
-cmdline_parse_token_string_t cmd_arp_print_arp_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_arp_print_result, arp_string,
-	"arp");
-
-cmdline_parse_token_string_t cmd_arp_print_print_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_arp_print_result, print_string,
-	"ls");
-
-cmdline_parse_inst_t cmd_arp_print = {
-	.f = cmd_arp_print_parsed,
-	.data = NULL,
-	.help_str = "ARP list",
-	.tokens = {
-		(void *)&cmd_arp_print_arp_string,
-		(void *)&cmd_arp_print_print_string,
-		NULL,
-	},
-};
-
-/* *** Routing - Add *** */
-struct cmd_route_add_result {
-	cmdline_fixed_string_t route_string;
-	cmdline_fixed_string_t add_string;
-	cmdline_ipaddr_t ip;
-	uint8_t depth;
-	uint8_t port;
-	cmdline_ipaddr_t nh_ip;
-};
-
-static void
-cmd_route_add_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_route_add_result *params = parsed_result;
-	struct app_rule rule, *old_rule;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_RT);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("Routing not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if ((params->depth == 0) || (params->depth > 32)) {
-		printf("Illegal value for depth parameter (%u)\n",
-			params->depth);
-		return;
-	}
-
-	if (params->port >= app.n_ports) {
-		printf("Illegal value for port parameter (%u)\n", params->port);
-		return;
-	}
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.routing.key.ip = rte_bswap32((uint32_t)
-		params->ip.addr.ipv4.s_addr);
-	rule.routing.key.depth = params->depth;
-	rule.routing.port = params->port;
-	rule.routing.nh_ip =
-		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.routing.key, routing_table, routing);
-	if ((old_rule == NULL) && (n_routing_rules == app.max_routing_rules)) {
-		printf("Routing table is full.\n");
-		return;
-	}
-
-	printf("Adding route: ");
-	print_routing_rule(rule.routing);
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	req->type = APP_MSG_REQ_RT_ADD;
-	req->routing_add.ip = rule.routing.key.ip;
-	req->routing_add.depth = rule.routing.key.depth;
-	req->routing_add.port = rule.routing.port;
-	req->routing_add.nh_ip = rule.routing.nh_ip;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request ROUTE_ADD failed (%u)\n", resp->result);
-	else {
-		if (old_rule == NULL) {
-			struct app_rule *new_rule = (struct app_rule *)
-				rte_zmalloc_socket("CLI",
-				sizeof(struct app_rule),
-				RTE_CACHE_LINE_SIZE,
-				rte_socket_id());
-
-			if (new_rule == NULL)
-				rte_panic("Unable to allocate new rule\n");
-
-			memcpy(new_rule, &rule, sizeof(rule));
-			TAILQ_INSERT_TAIL(&routing_table, new_rule, entries);
-			n_routing_rules++;
-		} else {
-			old_rule->routing.port = rule.routing.port;
-			old_rule->routing.nh_ip = rule.routing.nh_ip;
-		}
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *) msg);
-}
-
-cmdline_parse_token_string_t cmd_route_add_route_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, route_string,
-	"route");
-
-cmdline_parse_token_string_t cmd_route_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, add_string,
-	"add");
-
-cmdline_parse_token_ipaddr_t cmd_route_add_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_route_add_result, ip);
-
-cmdline_parse_token_num_t cmd_route_add_depth =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, depth, UINT8);
-
-cmdline_parse_token_num_t cmd_route_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, port, UINT8);
-
-cmdline_parse_token_ipaddr_t cmd_route_add_nh_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_route_add_result, nh_ip);
-
-cmdline_parse_inst_t cmd_route_add = {
-	.f = cmd_route_add_parsed,
-	.data = NULL,
-	.help_str = "Route add",
-	.tokens = {
-		(void *)&cmd_route_add_route_string,
-		(void *)&cmd_route_add_add_string,
-		(void *)&cmd_route_add_ip,
-		(void *)&cmd_route_add_depth,
-		(void *)&cmd_route_add_port,
-		(void *)&cmd_route_add_nh_ip,
-		NULL,
-	},
-};
-
-/* *** Routing - Del *** */
-struct cmd_route_del_result {
-	cmdline_fixed_string_t route_string;
-	cmdline_fixed_string_t del_string;
-	cmdline_ipaddr_t ip;
-	uint8_t depth;
-};
-
-static void
-cmd_route_del_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_route_del_result *params = parsed_result;
-	struct app_rule rule, *old_rule;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_RT);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("Routing not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if ((params->depth == 0) || (params->depth > 32)) {
-		printf("Illegal value for depth parameter (%u)\n",
-			params->depth);
-		return;
-	}
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.routing.key.ip = rte_bswap32((uint32_t)
-		params->ip.addr.ipv4.s_addr);
-	rule.routing.key.depth = params->depth;
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.routing.key, routing_table, routing);
-	if (old_rule == NULL)
-		return;
-
-	printf("Deleting route: ");
-	print_routing_rule(old_rule->routing);
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	req->type = APP_MSG_REQ_RT_DEL;
-	req->routing_del.ip = rule.routing.key.ip;
-	req->routing_del.depth = rule.routing.key.depth;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request ROUTE_DEL failed %u)\n", resp->result);
-	else {
-		TAILQ_REMOVE(&routing_table, old_rule, entries);
-		rte_free(old_rule);
-		n_routing_rules--;
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *)msg);
-}
-
-cmdline_parse_token_string_t cmd_route_del_route_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_del_result, route_string,
-	"route");
-
-cmdline_parse_token_string_t cmd_route_del_del_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_del_result, del_string,
-	"del");
-
-cmdline_parse_token_ipaddr_t cmd_route_del_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_route_del_result, ip);
-
-cmdline_parse_token_num_t cmd_route_del_depth =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_del_result, depth, UINT8);
-
-cmdline_parse_inst_t cmd_route_del = {
-	.f = cmd_route_del_parsed,
-	.data = NULL,
-	.help_str = "Route delete",
-	.tokens = {
-		(void *)&cmd_route_del_route_string,
-		(void *)&cmd_route_del_del_string,
-		(void *)&cmd_route_del_ip,
-		(void *)&cmd_route_del_depth,
-		NULL,
-	},
-};
-
-/* *** Routing - Print *** */
-struct cmd_routing_print_result {
-	cmdline_fixed_string_t routing_string;
-	cmdline_fixed_string_t print_string;
-};
-
-static void
-cmd_routing_print_parsed(
-	__attribute__((unused)) void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct app_rule *it;
-
-	TAILQ_FOREACH(it, &routing_table, entries) {
-		print_routing_rule(it->routing);
-	}
-}
-
-cmdline_parse_token_string_t cmd_routing_print_routing_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_routing_print_result,
-	routing_string, "route");
-
-cmdline_parse_token_string_t cmd_routing_print_print_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_routing_print_result, print_string,
-	"ls");
-
-cmdline_parse_inst_t cmd_routing_print = {
-	.f = cmd_routing_print_parsed,
-	.data = NULL,
-	.help_str = "Route list",
-	.tokens = {
-		(void *)&cmd_routing_print_routing_string,
-		(void *)&cmd_routing_print_print_string,
-		NULL,
-	},
-};
-
-#ifdef RTE_LIBRTE_ACL
-
-/* *** Firewall - Add *** */
-struct cmd_firewall_add_result {
-	cmdline_fixed_string_t firewall_string;
-	cmdline_fixed_string_t add_string;
-	int32_t priority;
-	cmdline_ipaddr_t src_ip;
-	uint32_t src_ip_mask;
-	cmdline_ipaddr_t dst_ip;
-	uint32_t dst_ip_mask;
-	uint16_t src_port_from;
-	uint16_t src_port_to;
-	uint16_t dst_port_from;
-	uint16_t dst_port_to;
-	uint8_t proto;
-	uint8_t proto_mask;
-	uint8_t port;
-};
-
-static void
-cmd_firewall_add_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_firewall_add_result *params = parsed_result;
-	struct app_rule rule, *old_rule, *new_rule = NULL;
-	struct rte_mbuf *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_FW);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("Firewall not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if (params->port >= app.n_ports) {
-		printf("Illegal value for port parameter (%u)\n", params->port);
-		return;
-	}
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.firewall.priority = params->priority;
-	rule.firewall.key.src_ip =
-		rte_bswap32((uint32_t)params->src_ip.addr.ipv4.s_addr);
-	rule.firewall.key.src_ip_mask = params->src_ip_mask;
-	rule.firewall.key.dst_ip =
-		rte_bswap32((uint32_t)params->dst_ip.addr.ipv4.s_addr);
-	rule.firewall.key.dst_ip_mask = params->dst_ip_mask;
-	rule.firewall.key.src_port_from = params->src_port_from;
-	rule.firewall.key.src_port_to = params->src_port_to;
-	rule.firewall.key.dst_port_from = params->dst_port_from;
-	rule.firewall.key.dst_port_to = params->dst_port_to;
-	rule.firewall.key.proto = params->proto;
-	rule.firewall.key.proto_mask = params->proto_mask;
-	rule.firewall.port = params->port;
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.firewall.key, firewall_table, firewall);
-	if ((old_rule == NULL) &&
-		(n_firewall_rules == app.max_firewall_rules)) {
-		printf("Firewall table is full.\n");
-		return;
-	}
-
-	printf("Adding firewall rule: ");
-	print_firewall_rule(rule.firewall);
-
-	/* Allocate message buffer */
-	msg = rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* if we need a new rule structure, allocate it before we go further */
-	if (old_rule == NULL) {
-		new_rule = rte_zmalloc_socket("CLI", sizeof(struct app_rule),
-				RTE_CACHE_LINE_SIZE, rte_socket_id());
-		if (new_rule == NULL) {
-			printf("Cannot allocate memory for new rule\n");
-			rte_ctrlmbuf_free(msg);
-			return;
-		}
-	}
-
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data(msg);
-	req->type = APP_MSG_REQ_FW_ADD;
-	req->firewall_add.add_params.priority = rule.firewall.priority;
-	req->firewall_add.add_params.field_value[1].value.u32 =
-		rule.firewall.key.src_ip;
-	req->firewall_add.add_params.field_value[1].mask_range.u32 =
-		rule.firewall.key.src_ip_mask;
-	req->firewall_add.add_params.field_value[2].value.u32 =
-		rule.firewall.key.dst_ip;
-	req->firewall_add.add_params.field_value[2].mask_range.u32 =
-		rule.firewall.key.dst_ip_mask;
-	req->firewall_add.add_params.field_value[3].value.u16 =
-		rule.firewall.key.src_port_from;
-	req->firewall_add.add_params.field_value[3].mask_range.u16 =
-		rule.firewall.key.src_port_to;
-	req->firewall_add.add_params.field_value[4].value.u16 =
-		rule.firewall.key.dst_port_from;
-	req->firewall_add.add_params.field_value[4].mask_range.u16 =
-		rule.firewall.key.dst_port_to;
-	req->firewall_add.add_params.field_value[0].value.u8 =
-		rule.firewall.key.proto;
-	req->firewall_add.add_params.field_value[0].mask_range.u8 =
-		rule.firewall.key.proto_mask;
-	req->firewall_add.port = rule.firewall.port;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, (void *) msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, (void **) &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data(msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request FIREWALL_ADD failed (%u)\n", resp->result);
-	else {
-		if (old_rule == NULL) {
-			memcpy(new_rule, &rule, sizeof(rule));
-			TAILQ_INSERT_TAIL(&firewall_table, new_rule, entries);
-			n_firewall_rules++;
-		} else {
-			old_rule->firewall.priority = rule.firewall.priority;
-			old_rule->firewall.port = rule.firewall.port;
-		}
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free(msg);
-}
-
-cmdline_parse_token_string_t cmd_firewall_add_firewall_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_result,
-	firewall_string, "firewall");
-
-cmdline_parse_token_string_t cmd_firewall_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_result, add_string,
-	"add");
-
-cmdline_parse_token_num_t cmd_firewall_add_priority =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, priority, INT32);
-
-cmdline_parse_token_ipaddr_t cmd_firewall_add_src_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_firewall_add_result, src_ip);
-cmdline_parse_token_num_t cmd_firewall_add_src_ip_mask =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, src_ip_mask,
-	UINT32);
-
-cmdline_parse_token_ipaddr_t cmd_firewall_add_dst_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_firewall_add_result, dst_ip);
-cmdline_parse_token_num_t cmd_firewall_add_dst_ip_mask =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, dst_ip_mask,
-	UINT32);
-
-cmdline_parse_token_num_t cmd_firewall_add_src_port_from =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, src_port_from,
-	UINT16);
-cmdline_parse_token_num_t cmd_firewall_add_src_port_to =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, src_port_to,
-	UINT16);
-
-cmdline_parse_token_num_t cmd_firewall_add_dst_port_from =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, dst_port_from,
-	UINT16);
-cmdline_parse_token_num_t cmd_firewall_add_dst_port_to =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, dst_port_to,
-	UINT16);
-
-cmdline_parse_token_num_t cmd_firewall_add_proto =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, proto, UINT8);
-cmdline_parse_token_num_t cmd_firewall_add_proto_mask =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, proto_mask,
-	UINT8);
-cmdline_parse_token_num_t cmd_firewall_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_result, port, UINT8);
-
-cmdline_parse_inst_t cmd_firewall_add = {
-	.f = cmd_firewall_add_parsed,
-	.data = NULL,
-	.help_str = "Firewall rule add",
-	.tokens = {
-		(void *)&cmd_firewall_add_firewall_string,
-		(void *)&cmd_firewall_add_add_string,
-		(void *)&cmd_firewall_add_priority,
-		(void *)&cmd_firewall_add_src_ip,
-		(void *)&cmd_firewall_add_src_ip_mask,
-		(void *)&cmd_firewall_add_dst_ip,
-		(void *)&cmd_firewall_add_dst_ip_mask,
-		(void *)&cmd_firewall_add_src_port_from,
-		(void *)&cmd_firewall_add_src_port_to,
-		(void *)&cmd_firewall_add_dst_port_from,
-		(void *)&cmd_firewall_add_dst_port_to,
-		(void *)&cmd_firewall_add_proto,
-		(void *)&cmd_firewall_add_proto_mask,
-		(void *)&cmd_firewall_add_port,
-		NULL,
-	},
-};
-
-/* *** firewall - Del *** */
-struct cmd_firewall_del_result {
-	cmdline_fixed_string_t firewall_string;
-	cmdline_fixed_string_t del_string;
-	cmdline_ipaddr_t src_ip;
-	uint32_t src_ip_mask;
-	cmdline_ipaddr_t dst_ip;
-	uint32_t dst_ip_mask;
-	uint16_t src_port_from;
-	uint16_t src_port_to;
-	uint16_t dst_port_from;
-	uint16_t dst_port_to;
-	uint8_t proto;
-	uint8_t proto_mask;
-};
-
-static void
-cmd_firewall_del_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_firewall_del_result *params = parsed_result;
-	struct app_rule rule, *old_rule;
-	struct rte_mbuf *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_FW);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("Firewall not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.firewall.key.src_ip =
-		rte_bswap32((uint32_t) params->src_ip.addr.ipv4.s_addr);
-	rule.firewall.key.src_ip_mask = params->src_ip_mask;
-	rule.firewall.key.dst_ip =
-		rte_bswap32((uint32_t) params->dst_ip.addr.ipv4.s_addr);
-	rule.firewall.key.dst_ip_mask = params->dst_ip_mask;
-	rule.firewall.key.src_port_from = params->src_port_from;
-	rule.firewall.key.src_port_to = params->src_port_to;
-	rule.firewall.key.dst_port_from = params->dst_port_from;
-	rule.firewall.key.dst_port_to = params->dst_port_to;
-	rule.firewall.key.proto = params->proto;
-	rule.firewall.key.proto_mask = params->proto_mask;
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.firewall.key, firewall_table, firewall);
-	if (old_rule == NULL)
-		return;
-
-	printf("Deleting firewall rule: ");
-	print_firewall_rule(old_rule->firewall);
-
-	/* Allocate message buffer */
-	msg = rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data(msg);
-	memset(&req->firewall_del, 0, sizeof(req->firewall_del));
-	req->type = APP_MSG_REQ_FW_DEL;
-	req->firewall_del.delete_params.field_value[1].value.u32 =
-		rule.firewall.key.src_ip;
-	req->firewall_del.delete_params.field_value[1].mask_range.u32 =
-		rule.firewall.key.src_ip_mask;
-	req->firewall_del.delete_params.field_value[2].value.u32 =
-		rule.firewall.key.dst_ip;
-	req->firewall_del.delete_params.field_value[2].mask_range.u32 =
-		rule.firewall.key.dst_ip_mask;
-	req->firewall_del.delete_params.field_value[3].value.u16 =
-		rule.firewall.key.src_port_from;
-	req->firewall_del.delete_params.field_value[3].mask_range.u16 =
-		rule.firewall.key.src_port_to;
-	req->firewall_del.delete_params.field_value[4].value.u16 =
-		rule.firewall.key.dst_port_from;
-	req->firewall_del.delete_params.field_value[4].mask_range.u16 =
-		rule.firewall.key.dst_port_to;
-	req->firewall_del.delete_params.field_value[0].value.u8 =
-		rule.firewall.key.proto;
-	req->firewall_del.delete_params.field_value[0].mask_range.u8 =
-		rule.firewall.key.proto_mask;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, (void *) msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, (void **) &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data(msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request FIREWALL_DEL failed %u)\n", resp->result);
-	else {
-		TAILQ_REMOVE(&firewall_table, old_rule, entries);
-		rte_free(old_rule);
-		n_firewall_rules--;
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free(msg);
-}
-
-cmdline_parse_token_string_t cmd_firewall_del_firewall_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_result,
-	firewall_string, "firewall");
-
-cmdline_parse_token_string_t cmd_firewall_del_del_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_result, del_string,
-	"del");
-
-cmdline_parse_token_ipaddr_t cmd_firewall_del_src_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_firewall_del_result, src_ip);
-cmdline_parse_token_num_t cmd_firewall_del_src_ip_mask =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, src_ip_mask,
-	UINT32);
-
-cmdline_parse_token_ipaddr_t cmd_firewall_del_dst_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_firewall_del_result, dst_ip);
-cmdline_parse_token_num_t cmd_firewall_del_dst_ip_mask =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, dst_ip_mask,
-	UINT32);
-
-cmdline_parse_token_num_t cmd_firewall_del_src_port_from =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, src_port_from,
-	UINT16);
-cmdline_parse_token_num_t cmd_firewall_del_src_port_to =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, src_port_to,
-	UINT16);
-
-cmdline_parse_token_num_t cmd_firewall_del_dst_port_from =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, dst_port_from,
-	UINT16);
-cmdline_parse_token_num_t cmd_firewall_del_dst_port_to =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, dst_port_to,
-	UINT16);
-
-cmdline_parse_token_num_t cmd_firewall_del_proto =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, proto, UINT8);
-cmdline_parse_token_num_t cmd_firewall_del_proto_mask =
-	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_result, proto_mask,
-	UINT8);
-
-cmdline_parse_inst_t cmd_firewall_del = {
-	.f = cmd_firewall_del_parsed,
-	.data = NULL,
-	.help_str = "Firewall rule delete",
-	.tokens = {
-		(void *)&cmd_firewall_del_firewall_string,
-		(void *)&cmd_firewall_del_del_string,
-		(void *)&cmd_firewall_del_src_ip,
-		(void *)&cmd_firewall_del_src_ip_mask,
-		(void *)&cmd_firewall_del_dst_ip,
-		(void *)&cmd_firewall_del_dst_ip_mask,
-		(void *)&cmd_firewall_del_src_port_from,
-		(void *)&cmd_firewall_del_src_port_to,
-		(void *)&cmd_firewall_del_dst_port_from,
-		(void *)&cmd_firewall_del_dst_port_to,
-		(void *)&cmd_firewall_del_proto,
-		(void *)&cmd_firewall_del_proto_mask,
-		NULL,
-	},
-};
-
-/* *** Firewall - Print *** */
-struct cmd_firewall_print_result {
-	cmdline_fixed_string_t firewall_string;
-	cmdline_fixed_string_t print_string;
-};
-
-static void
-cmd_firewall_print_parsed(
-	__attribute__((unused)) void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct app_rule *it;
-
-	TAILQ_FOREACH(it, &firewall_table, entries) {
-		print_firewall_rule(it->firewall);
-	}
-}
-
-cmdline_parse_token_string_t cmd_firewall_print_firewall_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_firewall_print_result,
-	firewall_string, "firewall");
-
-cmdline_parse_token_string_t cmd_firewall_print_print_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_firewall_print_result, print_string,
-	"ls");
-
-cmdline_parse_inst_t cmd_firewall_print = {
-	.f = cmd_firewall_print_parsed,
-	.data = NULL,
-	.help_str = "Firewall rules list",
-	.tokens = {
-		(void *)&cmd_firewall_print_firewall_string,
-		(void *)&cmd_firewall_print_print_string,
-		NULL,
-	},
-};
-
-#endif
-
-/* *** Flow Classification - Add All *** */
-struct cmd_flow_add_all_result {
-	cmdline_fixed_string_t flow_string;
-	cmdline_fixed_string_t add_string;
-	cmdline_fixed_string_t all_string;
-};
-
-static void
-cmd_flow_add_all_parsed(
-	__attribute__((unused)) void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	struct rte_ring *ring_req =
-		app_get_ring_req(app_get_first_core_id(APP_CORE_FC));
-	struct rte_ring *ring_resp =
-		app_get_ring_resp(app_get_first_core_id(APP_CORE_FC));
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	memset(req, 0, sizeof(struct app_msg_req));
-
-	req->type = APP_MSG_REQ_FC_ADD_ALL;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request FLOW_ADD_ALL failed (%u)\n", resp->result);
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *)msg);
-}
-
-cmdline_parse_token_string_t cmd_flow_add_all_flow_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_add_all_result, flow_string,
-	"flow");
-
-cmdline_parse_token_string_t cmd_flow_add_all_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_add_all_result, add_string,
-	"add");
-
-cmdline_parse_token_string_t cmd_flow_add_all_all_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_add_all_result, all_string,
-	"all");
-
-cmdline_parse_inst_t cmd_flow_add_all = {
-	.f = cmd_flow_add_all_parsed,
-	.data = NULL,
-	.help_str = "Flow table initialization based on hard-coded rule",
-	.tokens = {
-		(void *)&cmd_flow_add_all_flow_string,
-		(void *)&cmd_flow_add_all_add_string,
-		(void *)&cmd_flow_add_all_all_string,
-		NULL,
-	},
-};
-
-/* *** Flow Classification - Add *** */
-struct cmd_flow_add_result {
-	cmdline_fixed_string_t flow_string;
-	cmdline_fixed_string_t add_string;
-	cmdline_ipaddr_t src_ip;
-	cmdline_ipaddr_t dst_ip;
-	uint16_t src_port;
-	uint16_t dst_port;
-	uint8_t proto;
-	uint8_t port;
-};
-
-static void
-cmd_flow_add_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_flow_add_result *params = parsed_result;
-	struct app_rule rule, *old_rule;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_FC);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("Flow classification not performed by any CPU core\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Check params */
-	if (params->port >= app.n_ports) {
-		printf("Illegal value for port parameter (%u)\n", params->port);
-		return;
-	}
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.flow.key.src_ip =
-		rte_bswap32((uint32_t)params->src_ip.addr.ipv4.s_addr);
-	rule.flow.key.dst_ip =
-		rte_bswap32((uint32_t)params->dst_ip.addr.ipv4.s_addr);
-	rule.flow.key.src_port = params->src_port;
-	rule.flow.key.dst_port = params->dst_port;
-	rule.flow.key.proto = params->proto;
-	rule.flow.port = params->port;
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.flow.key, flow_table, flow);
-	if ((old_rule == NULL) && (n_flow_rules == app.max_flow_rules)) {
-		printf("Flow table is full.\n");
-		return;
-	}
-
-	printf("Adding flow: ");
-	print_flow_rule(rule.flow);
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	memset(req, 0, sizeof(struct app_msg_req));
-
-	req->type = APP_MSG_REQ_FC_ADD;
-	req->flow_classif_add.key.ip_src = rte_bswap32(rule.flow.key.src_ip);
-	req->flow_classif_add.key.ip_dst = rte_bswap32(rule.flow.key.dst_ip);
-	req->flow_classif_add.key.port_src =
-		rte_bswap16(rule.flow.key.src_port);
-	req->flow_classif_add.key.port_dst =
-		rte_bswap16(rule.flow.key.dst_port);
-	req->flow_classif_add.key.proto = rule.flow.key.proto;
-	req->flow_classif_add.port = rule.flow.port;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request FLOW_ADD failed (%u)\n", resp->result);
-	else {
-		if (old_rule == NULL) {
-			struct app_rule *new_rule = (struct app_rule *)
-				rte_zmalloc_socket("CLI",
-				sizeof(struct app_rule),
-				RTE_CACHE_LINE_SIZE,
-				rte_socket_id());
-
-			if (new_rule == NULL)
-				rte_panic("Unable to allocate new rule\n");
-
-			memcpy(new_rule, &rule, sizeof(rule));
-			TAILQ_INSERT_TAIL(&flow_table, new_rule, entries);
-			n_flow_rules++;
-		} else
-			old_rule->flow.port = rule.flow.port;
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *)msg);
-}
-
-cmdline_parse_token_string_t cmd_flow_add_flow_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_add_result, flow_string,
-	"flow");
-
-cmdline_parse_token_string_t cmd_flow_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_add_result, add_string, "add");
-
-cmdline_parse_token_ipaddr_t cmd_flow_add_src_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_flow_add_result, src_ip);
-
-cmdline_parse_token_ipaddr_t cmd_flow_add_dst_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_flow_add_result, dst_ip);
-
-cmdline_parse_token_num_t cmd_flow_add_src_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_flow_add_result, src_port, UINT16);
-
-cmdline_parse_token_num_t cmd_flow_add_dst_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_flow_add_result, dst_port, UINT16);
-
-cmdline_parse_token_num_t cmd_flow_add_proto =
-	TOKEN_NUM_INITIALIZER(struct cmd_flow_add_result, proto, UINT8);
-
-cmdline_parse_token_num_t cmd_flow_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_flow_add_result, port, UINT8);
-
-cmdline_parse_inst_t cmd_flow_add = {
-	.f = cmd_flow_add_parsed,
-	.data = NULL,
-	.help_str = "Flow add",
-	.tokens = {
-		(void *)&cmd_flow_add_flow_string,
-		(void *)&cmd_flow_add_add_string,
-		(void *)&cmd_flow_add_src_ip,
-		(void *)&cmd_flow_add_dst_ip,
-		(void *)&cmd_flow_add_src_port,
-		(void *)&cmd_flow_add_dst_port,
-		(void *)&cmd_flow_add_proto,
-		(void *)&cmd_flow_add_port,
-		NULL,
-	},
-};
-
-/* *** Flow Classification - Del *** */
-struct cmd_flow_del_result {
-	cmdline_fixed_string_t flow_string;
-	cmdline_fixed_string_t del_string;
-	cmdline_ipaddr_t src_ip;
-	cmdline_ipaddr_t dst_ip;
-	uint16_t src_port;
-	uint16_t dst_port;
-	uint8_t proto;
-};
-
-static void
-cmd_flow_del_parsed(
-	void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct cmd_flow_del_result *params = parsed_result;
-	struct app_rule rule, *old_rule;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	void *msg;
-	int status;
-
-	uint32_t core_id = app_get_first_core_id(APP_CORE_FC);
-
-	if (core_id == RTE_MAX_LCORE) {
-		printf("Flow classification not performed by any CPU core.\n");
-		return;
-	}
-
-	struct rte_ring *ring_req = app_get_ring_req(core_id);
-	struct rte_ring *ring_resp = app_get_ring_resp(core_id);
-
-	/* Create rule */
-	memset(&rule, 0, sizeof(rule));
-	rule.flow.key.src_ip =
-		rte_bswap32((uint32_t)params->src_ip.addr.ipv4.s_addr);
-	rule.flow.key.dst_ip =
-		rte_bswap32((uint32_t)params->dst_ip.addr.ipv4.s_addr);
-	rule.flow.key.src_port = params->src_port;
-	rule.flow.key.dst_port = params->dst_port;
-	rule.flow.key.proto = params->proto;
-
-	/* Check rule existence */
-	IS_RULE_PRESENT(old_rule, rule.flow.key, flow_table, flow);
-	if (old_rule == NULL)
-		return;
-
-	printf("Deleting flow: ");
-	print_flow_rule(old_rule->flow);
-
-	/* Allocate message buffer */
-	msg = (void *)rte_ctrlmbuf_alloc(app.msg_pool);
-	if (msg == NULL)
-		rte_panic("Unable to allocate new message\n");
-
-	/* Fill request message */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	memset(req, 0, sizeof(struct app_msg_req));
-
-	req->type = APP_MSG_REQ_FC_DEL;
-	req->flow_classif_del.key.ip_src = rte_bswap32(rule.flow.key.src_ip);
-	req->flow_classif_del.key.ip_dst = rte_bswap32(rule.flow.key.dst_ip);
-	req->flow_classif_del.key.port_src =
-		rte_bswap32(rule.flow.key.src_port);
-	req->flow_classif_del.key.port_dst =
-		rte_bswap32(rule.flow.key.dst_port);
-	req->flow_classif_del.key.proto = rule.flow.key.proto;
-
-	/* Send request */
-	do {
-		status = rte_ring_sp_enqueue(ring_req, msg);
-	} while (status == -ENOBUFS);
-
-	/* Wait for response */
-	do {
-		status = rte_ring_sc_dequeue(ring_resp, &msg);
-	} while (status != 0);
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-
-	/* Check response */
-	if (resp->result != 0)
-		printf("Request FLOW_DEL failed (%u)\n", resp->result);
-	else {
-		TAILQ_REMOVE(&flow_table, old_rule, entries);
-		rte_free(old_rule);
-		n_flow_rules--;
-	}
-
-	/* Free message buffer */
-	rte_ctrlmbuf_free((struct rte_mbuf *)msg);
-}
-
-cmdline_parse_token_string_t cmd_flow_del_flow_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_del_result, flow_string,
-	"flow");
-
-cmdline_parse_token_string_t cmd_flow_del_del_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_del_result, del_string, "del");
-
-cmdline_parse_token_ipaddr_t cmd_flow_del_src_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_flow_del_result, src_ip);
-
-cmdline_parse_token_ipaddr_t cmd_flow_del_dst_ip =
-	TOKEN_IPADDR_INITIALIZER(struct cmd_flow_del_result, dst_ip);
-
-cmdline_parse_token_num_t cmd_flow_del_src_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_flow_del_result, src_port, UINT16);
-
-cmdline_parse_token_num_t cmd_flow_del_dst_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_flow_del_result, dst_port, UINT16);
-
-cmdline_parse_token_num_t cmd_flow_del_proto =
-	TOKEN_NUM_INITIALIZER(struct cmd_flow_del_result, proto, UINT8);
-
-cmdline_parse_inst_t cmd_flow_del = {
-	.f = cmd_flow_del_parsed,
-	.data = NULL,
-	.help_str = "Flow delete",
-	.tokens = {
-		(void *)&cmd_flow_del_flow_string,
-		(void *)&cmd_flow_del_del_string,
-		(void *)&cmd_flow_del_src_ip,
-		(void *)&cmd_flow_del_dst_ip,
-		(void *)&cmd_flow_del_src_port,
-		(void *)&cmd_flow_del_dst_port,
-		(void *)&cmd_flow_del_proto,
-		NULL,
-	},
-};
-
-/* *** Flow Classification - Print *** */
-struct cmd_flow_print_result {
-	cmdline_fixed_string_t flow_string;
-	cmdline_fixed_string_t print_string;
-};
-
-static void
-cmd_flow_print_parsed(
-	__attribute__((unused)) void *parsed_result,
-	__attribute__((unused)) struct cmdline *cl,
-	__attribute__((unused)) void *data)
-{
-	struct app_rule *it;
-
-	TAILQ_FOREACH(it, &flow_table, entries) {
-		print_flow_rule(it->flow);
-	}
-}
-
-cmdline_parse_token_string_t cmd_flow_print_flow_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_print_result, flow_string,
-	"flow");
-
-cmdline_parse_token_string_t cmd_flow_print_print_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_flow_print_result, print_string,
-	"ls");
-
-cmdline_parse_inst_t cmd_flow_print = {
-	.f = cmd_flow_print_parsed,
-	.data = NULL,
-	.help_str = "Flow list",
-	.tokens = {
-		(void *)&cmd_flow_print_flow_string,
-		(void *)&cmd_flow_print_print_string,
-		NULL,
-	},
-};
-
-/* *** QUIT *** */
-struct cmd_quit_result {
-	cmdline_fixed_string_t quit;
-};
-
-static void cmd_quit_parsed(__attribute__((unused)) void *parsed_result,
-		struct cmdline *cl,
-		__attribute__((unused)) void *data)
-{
-	cmdline_quit(cl);
-}
-
-cmdline_parse_token_string_t cmd_quit_quit =
-		TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
-
-cmdline_parse_inst_t cmd_quit = {
-	.f = cmd_quit_parsed,
-	.data = NULL,
-	.help_str = "Exit application",
-	.tokens = {
-		(void *)&cmd_quit_quit,
-		NULL,
-	},
-};
-
-/* List of commands */
-cmdline_parse_ctx_t main_ctx[] = {
-	(cmdline_parse_inst_t *)&cmd_flow_add,
-	(cmdline_parse_inst_t *)&cmd_flow_del,
-	(cmdline_parse_inst_t *)&cmd_flow_add_all,
-	(cmdline_parse_inst_t *)&cmd_flow_print,
-#ifdef RTE_LIBRTE_ACL
-	(cmdline_parse_inst_t *)&cmd_firewall_add,
-	(cmdline_parse_inst_t *)&cmd_firewall_del,
-	(cmdline_parse_inst_t *)&cmd_firewall_print,
-#endif
-	(cmdline_parse_inst_t *)&cmd_route_add,
-	(cmdline_parse_inst_t *)&cmd_route_del,
-	(cmdline_parse_inst_t *)&cmd_routing_print,
-	(cmdline_parse_inst_t *)&cmd_arp_add,
-	(cmdline_parse_inst_t *)&cmd_arp_del,
-	(cmdline_parse_inst_t *)&cmd_arp_print,
-	(cmdline_parse_inst_t *)&cmd_run_file,
-	(cmdline_parse_inst_t *)&cmd_link_enable,
-	(cmdline_parse_inst_t *)&cmd_link_disable,
-	(cmdline_parse_inst_t *)&cmd_quit,
-	NULL,
-};
-
-/* Main loop */
-void
-app_main_loop_cmdline(void)
-{
-	struct cmdline *cl;
-	uint32_t core_id = rte_lcore_id();
-
-	RTE_LOG(INFO, USER1, "Core %u is running the command line interface\n",
-		core_id);
-
-	n_arp_rules = 0;
-	n_routing_rules = 0;
-	n_firewall_rules = 0;
-	n_flow_rules = 0;
-
-	app_init_rule_tables();
-
-	cl = cmdline_stdin_new(main_ctx, "pipeline> ");
-	if (cl == NULL)
-		return;
-	cmdline_interact(cl);
-	cmdline_stdin_exit(cl);
-}
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 01e13d2..036b316 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -44,6 +44,8 @@
 
 #include "app.h"
 #include "pipeline.h"
+#include "pipeline_common_fe.h"
+#include "pipeline_master.h"
 
 #define APP_NAME_SIZE	32
 
@@ -1281,6 +1283,9 @@ int app_init(struct app_params *app)
 	app_init_tm(app);
 	app_init_msgq(app);
 
+	app_pipeline_common_cmd_push(app);
+	app_pipeline_type_register(app, &pipeline_master);
+
 	app_init_pipelines(app);
 	app_init_threads(app);
 
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_be.c b/examples/ip_pipeline/pipeline/pipeline_common_be.c
new file mode 100644
index 0000000..50dcb69
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_common_be.c
@@ -0,0 +1,206 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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_ring.h>
+#include <rte_malloc.h>
+
+#include "pipeline_common_be.h"
+
+void *
+pipeline_msg_req_ping_handler(__rte_unused struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_msg_rsp *rsp = msg;
+
+	rsp->status = 0; /* OK */
+
+	return rsp;
+}
+
+void *
+pipeline_msg_req_stats_port_in_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_stats_msg_req *req = msg;
+	struct pipeline_stats_port_in_msg_rsp *rsp = msg;
+	uint32_t port_id;
+
+	/* Check request */
+	if (req->id >= p->n_ports_in) {
+		rsp->status = -1;
+		return rsp;
+	}
+	port_id = p->port_in_id[req->id];
+
+	/* Process request */
+	rsp->status = rte_pipeline_port_in_stats_read(p->p,
+		port_id,
+		&rsp->stats,
+		1);
+
+	return rsp;
+}
+
+void *
+pipeline_msg_req_stats_port_out_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_stats_msg_req *req = msg;
+	struct pipeline_stats_port_out_msg_rsp *rsp = msg;
+	uint32_t port_id;
+
+	/* Check request */
+	if (req->id >= p->n_ports_out) {
+		rsp->status = -1;
+		return rsp;
+	}
+	port_id = p->port_out_id[req->id];
+
+	/* Process request */
+	rsp->status = rte_pipeline_port_out_stats_read(p->p,
+		port_id,
+		&rsp->stats,
+		1);
+
+	return rsp;
+}
+
+void *
+pipeline_msg_req_stats_table_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_stats_msg_req *req = msg;
+	struct pipeline_stats_table_msg_rsp *rsp = msg;
+	uint32_t table_id;
+
+	/* Check request */
+	if (req->id >= p->n_tables) {
+		rsp->status = -1;
+		return rsp;
+	}
+	table_id = p->table_id[req->id];
+
+	/* Process request */
+	rsp->status = rte_pipeline_table_stats_read(p->p,
+		table_id,
+		&rsp->stats,
+		1);
+
+	return rsp;
+}
+
+void *
+pipeline_msg_req_port_in_enable_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_port_in_msg_req *req = msg;
+	struct pipeline_msg_rsp *rsp = msg;
+	uint32_t port_id;
+
+	/* Check request */
+	if (req->port_id >= p->n_ports_in) {
+		rsp->status = -1;
+		return rsp;
+	}
+	port_id = p->port_in_id[req->port_id];
+
+	/* Process request */
+	rsp->status = rte_pipeline_port_in_enable(p->p,
+		port_id);
+
+	return rsp;
+}
+
+void *
+pipeline_msg_req_port_in_disable_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_port_in_msg_req *req = msg;
+	struct pipeline_msg_rsp *rsp = msg;
+	uint32_t port_id;
+
+	/* Check request */
+	if (req->port_id >= p->n_ports_in) {
+		rsp->status = -1;
+		return rsp;
+	}
+	port_id = p->port_in_id[req->port_id];
+
+	/* Process request */
+	rsp->status = rte_pipeline_port_in_disable(p->p,
+		port_id);
+
+	return rsp;
+}
+
+void *
+pipeline_msg_req_invalid_handler(__rte_unused struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_msg_rsp *rsp = msg;
+
+	rsp->status = -1; /* Error */
+
+	return rsp;
+}
+
+int
+pipeline_msg_req_handle(struct pipeline *p)
+{
+	uint32_t msgq_id;
+
+	for (msgq_id = 0; msgq_id < p->n_msgq; msgq_id++) {
+		for ( ; ; ) {
+			struct pipeline_msg_req *req;
+			pipeline_msg_req_handler f_handle;
+
+			req = pipeline_msg_recv(p, msgq_id);
+			if (req == NULL)
+				break;
+
+			f_handle = (req->type < PIPELINE_MSG_REQS) ?
+				p->handlers[req->type] :
+				pipeline_msg_req_invalid_handler;
+
+			if (f_handle == NULL)
+				f_handle = pipeline_msg_req_invalid_handler;
+
+			pipeline_msg_send(p,
+				msgq_id,
+				f_handle(p, (void *) req));
+		}
+	}
+
+	return 0;
+}
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_be.h b/examples/ip_pipeline/pipeline/pipeline_common_be.h
new file mode 100644
index 0000000..07fdca0
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_common_be.h
@@ -0,0 +1,163 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_COMMON_BE_H__
+#define __INCLUDE_PIPELINE_COMMON_BE_H__
+
+#include <rte_common.h>
+#include <rte_ring.h>
+#include <rte_pipeline.h>
+
+#include "pipeline_be.h"
+
+struct pipeline;
+
+enum pipeline_msg_req_type {
+	PIPELINE_MSG_REQ_PING = 0,
+	PIPELINE_MSG_REQ_STATS_PORT_IN,
+	PIPELINE_MSG_REQ_STATS_PORT_OUT,
+	PIPELINE_MSG_REQ_STATS_TABLE,
+	PIPELINE_MSG_REQ_PORT_IN_ENABLE,
+	PIPELINE_MSG_REQ_PORT_IN_DISABLE,
+	PIPELINE_MSG_REQ_CUSTOM,
+	PIPELINE_MSG_REQS
+};
+
+typedef void *(*pipeline_msg_req_handler)(struct pipeline *p, void *msg);
+
+struct pipeline {
+	struct rte_pipeline *p;
+	uint32_t port_in_id[PIPELINE_MAX_PORT_IN];
+	uint32_t port_out_id[PIPELINE_MAX_PORT_OUT];
+	uint32_t table_id[PIPELINE_MAX_TABLES];
+	struct rte_ring *msgq_in[PIPELINE_MAX_MSGQ_IN];
+	struct rte_ring *msgq_out[PIPELINE_MAX_MSGQ_OUT];
+
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+	uint32_t n_tables;
+	uint32_t n_msgq;
+
+	pipeline_msg_req_handler handlers[PIPELINE_MSG_REQS];
+	char name[PIPELINE_NAME_SIZE];
+	uint32_t log_level;
+};
+
+enum pipeline_log_level {
+	PIPELINE_LOG_LEVEL_HIGH = 1,
+	PIPELINE_LOG_LEVEL_LOW,
+	PIPELINE_LOG_LEVELS
+};
+
+#define PLOG(p, level, fmt, ...)					\
+do {									\
+	if (p->log_level >= PIPELINE_LOG_LEVEL_ ## level)		\
+		fprintf(stdout, "[%s] " fmt "\n", p->name, ## __VA_ARGS__);\
+} while (0)
+
+static inline void *
+pipeline_msg_recv(struct pipeline *p,
+	uint32_t msgq_id)
+{
+	struct rte_ring *r = p->msgq_in[msgq_id];
+	void *msg;
+	int status = rte_ring_sc_dequeue(r, &msg);
+
+	if (status != 0)
+		return NULL;
+
+	return msg;
+}
+
+static inline void
+pipeline_msg_send(struct pipeline *p,
+	uint32_t msgq_id,
+	void *msg)
+{
+	struct rte_ring *r = p->msgq_out[msgq_id];
+	int status;
+
+	do {
+		status = rte_ring_sp_enqueue(r, msg);
+	} while (status == -ENOBUFS);
+}
+
+struct pipeline_msg_req {
+	enum pipeline_msg_req_type type;
+};
+
+struct pipeline_stats_msg_req {
+	enum pipeline_msg_req_type type;
+	uint32_t id;
+};
+
+struct pipeline_port_in_msg_req {
+	enum pipeline_msg_req_type type;
+	uint32_t port_id;
+};
+
+struct pipeline_custom_msg_req {
+	enum pipeline_msg_req_type type;
+	uint32_t subtype;
+};
+
+struct pipeline_msg_rsp {
+	int status;
+};
+
+struct pipeline_stats_port_in_msg_rsp {
+	int status;
+	struct rte_pipeline_port_in_stats stats;
+};
+
+struct pipeline_stats_port_out_msg_rsp {
+	int status;
+	struct rte_pipeline_port_out_stats stats;
+};
+
+struct pipeline_stats_table_msg_rsp {
+	int status;
+	struct rte_pipeline_table_stats stats;
+};
+
+void *pipeline_msg_req_ping_handler(struct pipeline *p, void *msg);
+void *pipeline_msg_req_stats_port_in_handler(struct pipeline *p, void *msg);
+void *pipeline_msg_req_stats_port_out_handler(struct pipeline *p, void *msg);
+void *pipeline_msg_req_stats_table_handler(struct pipeline *p, void *msg);
+void *pipeline_msg_req_port_in_enable_handler(struct pipeline *p, void *msg);
+void *pipeline_msg_req_port_in_disable_handler(struct pipeline *p, void *msg);
+void *pipeline_msg_req_invalid_handler(struct pipeline *p, void *msg);
+
+int pipeline_msg_req_handle(struct pipeline *p);
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
new file mode 100644
index 0000000..fcda0ce
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
@@ -0,0 +1,1333 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_ring.h>
+#include <rte_malloc.h>
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_etheraddr.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include "pipeline_common_fe.h"
+
+int
+app_pipeline_ping(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct app_pipeline_params *p;
+	struct pipeline_msg_req *req;
+	struct pipeline_msg_rsp *rsp;
+	int status = 0;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
+	if (p == NULL)
+		return -1;
+
+	/* Message buffer allocation */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	/* Fill in request */
+	req->type = PIPELINE_MSG_REQ_PING;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Check response */
+	status = rsp->status;
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+
+	return status;
+}
+
+int
+app_pipeline_stats_port_in(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id,
+	struct rte_pipeline_port_in_stats *stats)
+{
+	struct app_pipeline_params *p;
+	struct pipeline_stats_msg_req *req;
+	struct pipeline_stats_port_in_msg_rsp *rsp;
+	int status = 0;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(stats == NULL))
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
+	if ((p == NULL) ||
+		(port_id >= p->n_pktq_in))
+		return -1;
+
+	/* Message buffer allocation */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	/* Fill in request */
+	req->type = PIPELINE_MSG_REQ_STATS_PORT_IN;
+	req->id = port_id;
+
+	/* Send request and wait for response */
+	rsp = (struct pipeline_stats_port_in_msg_rsp *)
+		app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Check response */
+	status = rsp->status;
+	if (status == 0)
+		memcpy(stats, &rsp->stats, sizeof(rsp->stats));
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+
+	return status;
+}
+
+int
+app_pipeline_stats_port_out(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id,
+	struct rte_pipeline_port_out_stats *stats)
+{
+	struct app_pipeline_params *p;
+	struct pipeline_stats_msg_req *req;
+	struct pipeline_stats_port_out_msg_rsp *rsp;
+	int status = 0;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(pipeline_id >= app->n_pipelines) ||
+		(stats == NULL))
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
+	if ((p == NULL) ||
+		(port_id >= p->n_pktq_out))
+		return -1;
+
+	/* Message buffer allocation */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	/* Fill in request */
+	req->type = PIPELINE_MSG_REQ_STATS_PORT_OUT;
+	req->id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Check response */
+	status = rsp->status;
+	if (status == 0)
+		memcpy(stats, &rsp->stats, sizeof(rsp->stats));
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+
+	return status;
+}
+
+int
+app_pipeline_stats_table(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t table_id,
+	struct rte_pipeline_table_stats *stats)
+{
+	struct app_pipeline_params *p;
+	struct pipeline_stats_msg_req *req;
+	struct pipeline_stats_table_msg_rsp *rsp;
+	int status = 0;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(stats == NULL))
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
+	if (p == NULL)
+		return -1;
+
+	/* Message buffer allocation */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	/* Fill in request */
+	req->type = PIPELINE_MSG_REQ_STATS_TABLE;
+	req->id = table_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Check response */
+	status = rsp->status;
+	if (status == 0)
+		memcpy(stats, &rsp->stats, sizeof(rsp->stats));
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+
+	return status;
+}
+
+int
+app_pipeline_port_in_enable(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id)
+{
+	struct app_pipeline_params *p;
+	struct pipeline_port_in_msg_req *req;
+	struct pipeline_msg_rsp *rsp;
+	int status = 0;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
+	if ((p == NULL) ||
+		(port_id >= p->n_pktq_in))
+		return -1;
+
+	/* Message buffer allocation */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	/* Fill in request */
+	req->type = PIPELINE_MSG_REQ_PORT_IN_ENABLE;
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Check response */
+	status = rsp->status;
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+
+	return status;
+}
+
+int
+app_pipeline_port_in_disable(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id)
+{
+	struct app_pipeline_params *p;
+	struct pipeline_port_in_msg_req *req;
+	struct pipeline_msg_rsp *rsp;
+	int status = 0;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", pipeline_id, p);
+	if ((p == NULL) ||
+		(port_id >= p->n_pktq_in))
+		return -1;
+
+	/* Message buffer allocation */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	/* Fill in request */
+	req->type = PIPELINE_MSG_REQ_PORT_IN_DISABLE;
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Check response */
+	status = rsp->status;
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+
+	return status;
+}
+
+int
+app_link_config(struct app_params *app,
+	uint32_t link_id,
+	uint32_t ip,
+	uint32_t depth)
+{
+	struct app_link_params *p;
+	uint32_t i, netmask, host, bcast;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
+	if (p == NULL) {
+		APP_LOG(app, HIGH, "LINK%" PRIu32 " is not a valid link",
+			link_id);
+		return -1;
+	}
+
+	if (p->state) {
+		APP_LOG(app, HIGH, "%s is UP, please bring it DOWN first",
+			p->name);
+		return -1;
+	}
+
+	netmask = (~0) << (32 - depth);
+	host = ip & netmask;
+	bcast = host | (~netmask);
+
+	if ((ip == 0) ||
+		(ip == UINT32_MAX) ||
+		(ip == host) ||
+		(ip == bcast)) {
+		APP_LOG(app, HIGH, "Illegal IP address");
+		return -1;
+	}
+
+	for (i = 0; i < app->n_links; i++) {
+		struct app_link_params *link = &app->link_params[i];
+
+		if (strcmp(p->name, link->name) == 0)
+			continue;
+
+		if (link->ip == ip) {
+			APP_LOG(app, HIGH,
+				"%s is already assigned this IP address",
+				p->name);
+			return -1;
+		}
+	}
+
+	if ((depth == 0) || (depth > 32)) {
+		APP_LOG(app, HIGH, "Illegal value for depth parameter "
+			"(%" PRIu32 ")",
+			depth);
+		return -1;
+	}
+
+	/* Save link parameters */
+	p->ip = ip;
+	p->depth = depth;
+
+	return 0;
+}
+
+int
+app_link_up(struct app_params *app,
+	uint32_t link_id)
+{
+	struct app_link_params *p;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
+	if (p == NULL) {
+		APP_LOG(app, HIGH, "LINK%" PRIu32 " is not a valid link",
+			link_id);
+		return -1;
+	}
+
+	/* Check link state */
+	if (p->state) {
+		APP_LOG(app, HIGH, "%s is already UP", p->name);
+		return 0;
+	}
+
+	/* Check that IP address is valid */
+	if (p->ip == 0) {
+		APP_LOG(app, HIGH, "%s IP address is not set", p->name);
+		return 0;
+	}
+
+	app_link_up_internal(app, p);
+
+	return 0;
+}
+
+int
+app_link_down(struct app_params *app,
+	uint32_t link_id)
+{
+	struct app_link_params *p;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
+	if (p == NULL) {
+		APP_LOG(app, HIGH, "LINK%" PRIu32 " is not a valid link",
+			link_id);
+		return -1;
+	}
+
+	/* Check link state */
+	if (p->state == 0) {
+		APP_LOG(app, HIGH, "%s is already DOWN", p->name);
+		return 0;
+	}
+
+	app_link_down_internal(app, p);
+
+	return 0;
+}
+
+/*
+ * ping
+ */
+
+struct cmd_ping_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t ping_string;
+};
+
+static void
+cmd_ping_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_ping_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_ping(app,	params->pipeline_id);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_ping_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_ping_result, p_string, "p");
+
+cmdline_parse_token_num_t cmd_ping_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_ping_result, pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_ping_ping_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_ping_result, ping_string, "ping");
+
+cmdline_parse_inst_t cmd_ping = {
+	.f = cmd_ping_parsed,
+	.data = NULL,
+	.help_str = "Pipeline ping",
+	.tokens = {
+		(void *) &cmd_ping_p_string,
+		(void *) &cmd_ping_pipeline_id,
+		(void *) &cmd_ping_ping_string,
+		NULL,
+	},
+};
+
+/*
+ * stats port in
+ */
+
+struct cmd_stats_port_in_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t stats_string;
+	cmdline_fixed_string_t port_string;
+	cmdline_fixed_string_t in_string;
+	uint32_t port_in_id;
+
+};
+static void
+cmd_stats_port_in_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_stats_port_in_result *params = parsed_result;
+	struct app_params *app = data;
+	struct rte_pipeline_port_in_stats stats;
+	int status;
+
+	status = app_pipeline_stats_port_in(app,
+			params->pipeline_id,
+			params->port_in_id,
+			&stats);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+
+	/* Display stats */
+	printf("Pipeline %" PRIu32 " - stats for input port %" PRIu32 ":\n"
+		"\tPkts in: %" PRIu64 "\n"
+		"\tPkts dropped by AH: %" PRIu64 "\n"
+		"\tPkts dropped by other: %" PRIu64 "\n",
+		params->pipeline_id,
+		params->port_in_id,
+		stats.stats.n_pkts_in,
+		stats.n_pkts_dropped_by_ah,
+		stats.stats.n_pkts_drop);
+}
+
+cmdline_parse_token_string_t cmd_stats_port_in_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_stats_port_in_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_stats_port_in_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_stats_port_in_stats_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, stats_string,
+		"stats");
+
+cmdline_parse_token_string_t cmd_stats_port_in_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, port_string,
+		"port");
+
+cmdline_parse_token_string_t cmd_stats_port_in_in_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_in_result, in_string,
+		"in");
+
+	cmdline_parse_token_num_t cmd_stats_port_in_port_in_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_stats_port_in_result, port_in_id,
+		UINT32);
+
+cmdline_parse_inst_t cmd_stats_port_in = {
+	.f = cmd_stats_port_in_parsed,
+	.data = NULL,
+	.help_str = "Pipeline input port stats",
+	.tokens = {
+		(void *) &cmd_stats_port_in_p_string,
+		(void *) &cmd_stats_port_in_pipeline_id,
+		(void *) &cmd_stats_port_in_stats_string,
+		(void *) &cmd_stats_port_in_port_string,
+		(void *) &cmd_stats_port_in_in_string,
+		(void *) &cmd_stats_port_in_port_in_id,
+		NULL,
+	},
+};
+
+/*
+ * stats port out
+ */
+
+struct cmd_stats_port_out_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t stats_string;
+	cmdline_fixed_string_t port_string;
+	cmdline_fixed_string_t out_string;
+	uint32_t port_out_id;
+};
+
+static void
+cmd_stats_port_out_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+
+	struct cmd_stats_port_out_result *params = parsed_result;
+	struct app_params *app = data;
+	struct rte_pipeline_port_out_stats stats;
+	int status;
+
+	status = app_pipeline_stats_port_out(app,
+			params->pipeline_id,
+			params->port_out_id,
+			&stats);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+
+	/* Display stats */
+	printf("Pipeline %" PRIu32 " - stats for output port %" PRIu32 ":\n"
+		"\tPkts in: %" PRIu64 "\n"
+		"\tPkts dropped by AH: %" PRIu64 "\n"
+		"\tPkts dropped by other: %" PRIu64 "\n",
+		params->pipeline_id,
+		params->port_out_id,
+		stats.stats.n_pkts_in,
+		stats.n_pkts_dropped_by_ah,
+		stats.stats.n_pkts_drop);
+}
+
+cmdline_parse_token_string_t cmd_stats_port_out_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, p_string,
+	"p");
+
+cmdline_parse_token_num_t cmd_stats_port_out_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_stats_port_out_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_stats_port_out_stats_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, stats_string,
+		"stats");
+
+cmdline_parse_token_string_t cmd_stats_port_out_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, port_string,
+		"port");
+
+cmdline_parse_token_string_t cmd_stats_port_out_out_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_port_out_result, out_string,
+		"out");
+
+cmdline_parse_token_num_t cmd_stats_port_out_port_out_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_stats_port_out_result, port_out_id,
+		UINT32);
+
+cmdline_parse_inst_t cmd_stats_port_out = {
+	.f = cmd_stats_port_out_parsed,
+	.data = NULL,
+	.help_str = "Pipeline output port stats",
+	.tokens = {
+		(void *) &cmd_stats_port_out_p_string,
+		(void *) &cmd_stats_port_out_pipeline_id,
+		(void *) &cmd_stats_port_out_stats_string,
+		(void *) &cmd_stats_port_out_port_string,
+		(void *) &cmd_stats_port_out_out_string,
+		(void *) &cmd_stats_port_out_port_out_id,
+		NULL,
+	},
+};
+
+/*
+ * stats table
+ */
+
+struct cmd_stats_table_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t stats_string;
+	cmdline_fixed_string_t table_string;
+	uint32_t table_id;
+};
+
+static void
+cmd_stats_table_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_stats_table_result *params = parsed_result;
+	struct app_params *app = data;
+	struct rte_pipeline_table_stats stats;
+	int status;
+
+	status = app_pipeline_stats_table(app,
+			params->pipeline_id,
+			params->table_id,
+			&stats);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+
+	/* Display stats */
+	printf("Pipeline %" PRIu32 " - stats for table %" PRIu32 ":\n"
+		"\tPkts in: %" PRIu64 "\n"
+		"\tPkts in with lookup miss: %" PRIu64 "\n"
+		"\tPkts in with lookup hit dropped by AH: %" PRIu64 "\n"
+		"\tPkts in with lookup hit dropped by others: %" PRIu64 "\n"
+		"\tPkts in with lookup miss dropped by AH: %" PRIu64 "\n"
+		"\tPkts in with lookup miss dropped by others: %" PRIu64 "\n",
+		params->pipeline_id,
+		params->table_id,
+		stats.stats.n_pkts_in,
+		stats.stats.n_pkts_lookup_miss,
+		stats.n_pkts_dropped_by_lkp_hit_ah,
+		stats.n_pkts_dropped_lkp_hit,
+		stats.n_pkts_dropped_by_lkp_miss_ah,
+		stats.n_pkts_dropped_lkp_miss);
+}
+
+cmdline_parse_token_string_t cmd_stats_table_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_table_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_stats_table_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_stats_table_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_stats_table_stats_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_table_result, stats_string,
+		"stats");
+
+cmdline_parse_token_string_t cmd_stats_table_table_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_stats_table_result, table_string,
+		"table");
+
+cmdline_parse_token_num_t cmd_stats_table_table_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_stats_table_result, table_id, UINT32);
+
+cmdline_parse_inst_t cmd_stats_table = {
+	.f = cmd_stats_table_parsed,
+	.data = NULL,
+	.help_str = "Pipeline table stats",
+	.tokens = {
+		(void *) &cmd_stats_table_p_string,
+		(void *) &cmd_stats_table_pipeline_id,
+		(void *) &cmd_stats_table_stats_string,
+		(void *) &cmd_stats_table_table_string,
+		(void *) &cmd_stats_table_table_id,
+		NULL,
+	},
+};
+
+/*
+ * port in enable
+ */
+
+struct cmd_port_in_enable_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t port_string;
+	cmdline_fixed_string_t in_string;
+	uint32_t port_in_id;
+	cmdline_fixed_string_t enable_string;
+};
+
+static void
+cmd_port_in_enable_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_port_in_enable_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_port_in_enable(app,
+			params->pipeline_id,
+			params->port_in_id);
+
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_port_in_enable_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_port_in_enable_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_port_in_enable_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_port_in_enable_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result, port_string,
+	"port");
+
+cmdline_parse_token_string_t cmd_port_in_enable_in_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result, in_string,
+		"in");
+
+cmdline_parse_token_num_t cmd_port_in_enable_port_in_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_port_in_enable_result, port_in_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_port_in_enable_enable_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_enable_result,
+		enable_string, "enable");
+
+cmdline_parse_inst_t cmd_port_in_enable = {
+	.f = cmd_port_in_enable_parsed,
+	.data = NULL,
+	.help_str = "Pipeline input port enable",
+	.tokens = {
+		(void *) &cmd_port_in_enable_p_string,
+		(void *) &cmd_port_in_enable_pipeline_id,
+		(void *) &cmd_port_in_enable_port_string,
+		(void *) &cmd_port_in_enable_in_string,
+		(void *) &cmd_port_in_enable_port_in_id,
+		(void *) &cmd_port_in_enable_enable_string,
+		NULL,
+	},
+};
+
+/*
+ * port in disable
+ */
+
+struct cmd_port_in_disable_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t port_string;
+	cmdline_fixed_string_t in_string;
+	uint32_t port_in_id;
+	cmdline_fixed_string_t disable_string;
+};
+
+static void
+cmd_port_in_disable_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_port_in_disable_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_port_in_disable(app,
+			params->pipeline_id,
+			params->port_in_id);
+
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_port_in_disable_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_port_in_disable_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_port_in_disable_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_port_in_disable_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result, port_string,
+		"port");
+
+cmdline_parse_token_string_t cmd_port_in_disable_in_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result, in_string,
+		"in");
+
+cmdline_parse_token_num_t cmd_port_in_disable_port_in_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_port_in_disable_result, port_in_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_port_in_disable_disable_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_port_in_disable_result,
+		disable_string, "disable");
+
+cmdline_parse_inst_t cmd_port_in_disable = {
+	.f = cmd_port_in_disable_parsed,
+	.data = NULL,
+	.help_str = "Pipeline input port disable",
+	.tokens = {
+		(void *) &cmd_port_in_disable_p_string,
+		(void *) &cmd_port_in_disable_pipeline_id,
+		(void *) &cmd_port_in_disable_port_string,
+		(void *) &cmd_port_in_disable_in_string,
+		(void *) &cmd_port_in_disable_port_in_id,
+		(void *) &cmd_port_in_disable_disable_string,
+		NULL,
+	},
+};
+
+/*
+ * link config
+ */
+
+static void
+print_link_info(struct app_link_params *p)
+{
+	struct rte_eth_stats stats;
+	struct ether_addr *mac_addr;
+	uint32_t netmask = (~0) << (32 - p->depth);
+	uint32_t host = p->ip & netmask;
+	uint32_t bcast = host | (~netmask);
+
+	memset(&stats, 0, sizeof(stats));
+	rte_eth_stats_get(p->pmd_id, &stats);
+
+	mac_addr = (struct ether_addr *) &p->mac_addr;
+
+	printf("%s: flags=<%s>\n",
+		p->name,
+		(p->state) ? "UP" : "DOWN");
+
+	if (p->ip)
+		printf("\tinet %" PRIu32 ".%" PRIu32
+			".%" PRIu32 ".%" PRIu32
+			" netmask %" PRIu32 ".%" PRIu32
+			".%" PRIu32 ".%" PRIu32 " "
+			"broadcast %" PRIu32 ".%" PRIu32
+			".%" PRIu32 ".%" PRIu32 "\n",
+			(p->ip >> 24) & 0xFF,
+			(p->ip >> 16) & 0xFF,
+			(p->ip >> 8) & 0xFF,
+			p->ip & 0xFF,
+			(netmask >> 24) & 0xFF,
+			(netmask >> 16) & 0xFF,
+			(netmask >> 8) & 0xFF,
+			netmask & 0xFF,
+			(bcast >> 24) & 0xFF,
+			(bcast >> 16) & 0xFF,
+			(bcast >> 8) & 0xFF,
+			bcast & 0xFF);
+
+	printf("\tether %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
+		":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
+		mac_addr->addr_bytes[0],
+		mac_addr->addr_bytes[1],
+		mac_addr->addr_bytes[2],
+		mac_addr->addr_bytes[3],
+		mac_addr->addr_bytes[4],
+		mac_addr->addr_bytes[5]);
+
+	printf("\tRX packets %" PRIu64
+		"  bytes %" PRIu64
+		"\n",
+		stats.ipackets,
+		stats.ibytes);
+
+	printf("\tRX mcast %" PRIu64
+		"  fdirmatch %" PRIu64
+		"  fdirmiss %" PRIu64
+		"  lb-packets %" PRIu64
+		"  lb-bytes %" PRIu64
+		"  xon %" PRIu64
+		"  xoff %" PRIu64 "\n",
+		stats.imcasts,
+		stats.fdirmatch,
+		stats.fdirmiss,
+		stats.ilbpackets,
+		stats.ilbbytes,
+		stats.rx_pause_xon,
+		stats.rx_pause_xoff);
+
+	printf("\tRX errors %" PRIu64
+		"  missed %" PRIu64
+		"  badcrc %" PRIu64
+		"  badlen %" PRIu64
+		"  no-mbuf %" PRIu64
+		"\n",
+		stats.ierrors,
+		stats.imissed,
+		stats.ibadcrc,
+		stats.ibadlen,
+		stats.rx_nombuf);
+
+	printf("\tTX packets %" PRIu64
+		"  bytes %" PRIu64 "\n",
+		stats.opackets,
+		stats.obytes);
+
+	printf("\tTX lb-packets %" PRIu64
+		"  lb-bytes %" PRIu64
+		"  xon %" PRIu64
+		"  xoff %" PRIu64
+		"\n",
+		stats.olbpackets,
+		stats.olbbytes,
+		stats.tx_pause_xon,
+		stats.tx_pause_xoff);
+
+	printf("\tTX errors %" PRIu64
+		"\n",
+		stats.oerrors);
+
+	printf("\n");
+}
+
+struct cmd_link_config_result {
+	cmdline_fixed_string_t link_string;
+	uint32_t link_id;
+	cmdline_fixed_string_t config_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+};
+
+static void
+cmd_link_config_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	 void *data)
+{
+	struct cmd_link_config_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	uint32_t link_id = params->link_id;
+	uint32_t ip  = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	uint32_t depth = params->depth;
+
+	status = app_link_config(app, link_id, ip, depth);
+	if (status)
+		printf("Command failed\n");
+	else {
+		struct app_link_params *p;
+
+		APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
+		print_link_info(p);
+	}
+}
+
+cmdline_parse_token_string_t cmd_link_config_link_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_config_result, link_string,
+		"link");
+
+cmdline_parse_token_num_t cmd_link_config_link_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_link_config_result, link_id, UINT32);
+
+cmdline_parse_token_string_t cmd_link_config_config_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_config_result, config_string,
+		"config");
+
+cmdline_parse_token_ipaddr_t cmd_link_config_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_link_config_result, ip);
+
+cmdline_parse_token_num_t cmd_link_config_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_link_config_result, depth, UINT32);
+
+cmdline_parse_inst_t cmd_link_config = {
+	.f = cmd_link_config_parsed,
+	.data = NULL,
+	.help_str = "Link configuration",
+	.tokens = {
+		(void *)&cmd_link_config_link_string,
+		(void *)&cmd_link_config_link_id,
+		(void *)&cmd_link_config_config_string,
+		(void *)&cmd_link_config_ip,
+		(void *)&cmd_link_config_depth,
+		NULL,
+	},
+};
+
+/*
+ * link up
+ */
+
+struct cmd_link_up_result {
+	cmdline_fixed_string_t link_string;
+	uint32_t link_id;
+	cmdline_fixed_string_t up_string;
+};
+
+static void
+cmd_link_up_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_link_up_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_link_up(app, params->link_id);
+	if (status != 0)
+		printf("Command failed\n");
+	else {
+		struct app_link_params *p;
+
+		APP_PARAM_FIND_BY_ID(app->link_params, "LINK", params->link_id,
+			p);
+		print_link_info(p);
+	}
+}
+
+cmdline_parse_token_string_t cmd_link_up_link_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_up_result, link_string,
+		"link");
+
+cmdline_parse_token_num_t cmd_link_up_link_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_link_up_result, link_id, UINT32);
+
+cmdline_parse_token_string_t cmd_link_up_up_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_up_result, up_string, "up");
+
+cmdline_parse_inst_t cmd_link_up = {
+	.f = cmd_link_up_parsed,
+	.data = NULL,
+	.help_str = "Link UP",
+	.tokens = {
+		(void *)&cmd_link_up_link_string,
+		(void *)&cmd_link_up_link_id,
+		(void *)&cmd_link_up_up_string,
+		NULL,
+	},
+};
+
+/*
+ * link down
+ */
+
+struct cmd_link_down_result {
+	cmdline_fixed_string_t link_string;
+	uint32_t link_id;
+	cmdline_fixed_string_t down_string;
+};
+
+static void
+cmd_link_down_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_link_down_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_link_down(app, params->link_id);
+	if (status != 0)
+		printf("Command failed\n");
+	else {
+		struct app_link_params *p;
+
+		APP_PARAM_FIND_BY_ID(app->link_params, "LINK", params->link_id,
+			p);
+		print_link_info(p);
+	}
+}
+
+cmdline_parse_token_string_t cmd_link_down_link_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_down_result, link_string,
+		"link");
+
+cmdline_parse_token_num_t cmd_link_down_link_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_link_down_result, link_id, UINT32);
+
+cmdline_parse_token_string_t cmd_link_down_down_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_down_result, down_string,
+		"down");
+
+cmdline_parse_inst_t cmd_link_down = {
+	.f = cmd_link_down_parsed,
+	.data = NULL,
+	.help_str = "Link DOWN",
+	.tokens = {
+		(void *) &cmd_link_down_link_string,
+		(void *) &cmd_link_down_link_id,
+		(void *) &cmd_link_down_down_string,
+		NULL,
+	},
+};
+
+/*
+ * link ls
+ */
+
+struct cmd_link_ls_result {
+	cmdline_fixed_string_t link_string;
+	cmdline_fixed_string_t ls_string;
+};
+
+static void
+cmd_link_ls_parsed(
+	__attribute__((unused)) void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	 void *data)
+{
+	struct app_params *app = data;
+	uint32_t link_id;
+
+	for (link_id = 0; link_id < app->n_links; link_id++) {
+		struct app_link_params *p;
+
+		APP_PARAM_FIND_BY_ID(app->link_params, "LINK", link_id, p);
+		print_link_info(p);
+	}
+}
+
+cmdline_parse_token_string_t cmd_link_ls_link_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_ls_result, link_string,
+		"link");
+
+cmdline_parse_token_string_t cmd_link_ls_ls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_ls_result, ls_string, "ls");
+
+cmdline_parse_inst_t cmd_link_ls = {
+	.f = cmd_link_ls_parsed,
+	.data = NULL,
+	.help_str = "Link list",
+	.tokens = {
+		(void *)&cmd_link_ls_link_string,
+		(void *)&cmd_link_ls_ls_string,
+		NULL,
+	},
+};
+
+/*
+ * quit
+ */
+
+struct cmd_quit_result {
+	cmdline_fixed_string_t quit;
+};
+
+static void
+cmd_quit_parsed(
+	__rte_unused void *parsed_result,
+	struct cmdline *cl,
+	__rte_unused void *data)
+{
+	cmdline_quit(cl);
+}
+
+static cmdline_parse_token_string_t cmd_quit_quit =
+	TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
+
+static cmdline_parse_inst_t cmd_quit = {
+	.f = cmd_quit_parsed,
+	.data = NULL,
+	.help_str = "Quit",
+	.tokens = {
+		(void *) &cmd_quit_quit,
+		NULL,
+	},
+};
+
+/*
+ * run
+ */
+
+static void
+app_run_file(
+	cmdline_parse_ctx_t *ctx,
+	const char *file_name)
+{
+	struct cmdline *file_cl;
+	int fd;
+
+	fd = open(file_name, O_RDONLY);
+	if (fd < 0) {
+		printf("Cannot open file \"%s\"\n", file_name);
+		return;
+	}
+
+	file_cl = cmdline_new(ctx, "", fd, 1);
+	cmdline_interact(file_cl);
+	close(fd);
+}
+
+struct cmd_run_file_result {
+	cmdline_fixed_string_t run_string;
+	char file_name[APP_FILE_NAME_SIZE];
+};
+
+static void
+cmd_run_parsed(
+	void *parsed_result,
+	struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_run_file_result *params = parsed_result;
+
+	app_run_file(cl->ctx, params->file_name);
+}
+
+cmdline_parse_token_string_t cmd_run_run_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_run_file_result, run_string,
+		"run");
+
+cmdline_parse_token_string_t cmd_run_file_name =
+	TOKEN_STRING_INITIALIZER(struct cmd_run_file_result, file_name, NULL);
+
+cmdline_parse_inst_t cmd_run = {
+	.f = cmd_run_parsed,
+	.data = NULL,
+	.help_str = "Run CLI script file",
+	.tokens = {
+		(void *) &cmd_run_run_string,
+		(void *) &cmd_run_file_name,
+		NULL,
+	},
+};
+
+static cmdline_parse_ctx_t pipeline_common_cmds[] = {
+	(cmdline_parse_inst_t *) &cmd_quit,
+	(cmdline_parse_inst_t *) &cmd_run,
+
+	(cmdline_parse_inst_t *) &cmd_link_config,
+	(cmdline_parse_inst_t *) &cmd_link_up,
+	(cmdline_parse_inst_t *) &cmd_link_down,
+	(cmdline_parse_inst_t *) &cmd_link_ls,
+
+	(cmdline_parse_inst_t *) &cmd_ping,
+	(cmdline_parse_inst_t *) &cmd_stats_port_in,
+	(cmdline_parse_inst_t *) &cmd_stats_port_out,
+	(cmdline_parse_inst_t *) &cmd_stats_table,
+	(cmdline_parse_inst_t *) &cmd_port_in_enable,
+	(cmdline_parse_inst_t *) &cmd_port_in_disable,
+	NULL,
+};
+
+int
+app_pipeline_common_cmd_push(struct app_params *app)
+{
+	uint32_t n_cmds, i;
+
+	/* Check for available slots in the application commands array */
+	n_cmds = RTE_DIM(pipeline_common_cmds) - 1;
+	if (n_cmds > APP_MAX_CMDS - app->n_cmds)
+		return -ENOMEM;
+
+	/* Push pipeline commands into the application */
+	memcpy(&app->cmds[app->n_cmds],
+		pipeline_common_cmds,
+		n_cmds * sizeof(cmdline_parse_ctx_t *));
+
+	for (i = 0; i < n_cmds; i++)
+		app->cmds[app->n_cmds + i]->data = app;
+
+	app->n_cmds += n_cmds;
+	app->cmds[app->n_cmds] = NULL;
+
+	return 0;
+}
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.h b/examples/ip_pipeline/pipeline/pipeline_common_fe.h
new file mode 100644
index 0000000..693848d
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.h
@@ -0,0 +1,228 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_COMMON_FE_H__
+#define __INCLUDE_PIPELINE_COMMON_FE_H__
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_malloc.h>
+#include <cmdline_parse.h>
+
+#include "pipeline_common_be.h"
+#include "pipeline.h"
+#include "app.h"
+
+#ifndef MSG_TIMEOUT_DEFAULT
+#define MSG_TIMEOUT_DEFAULT                      1000
+#endif
+
+static inline struct app_pipeline_data *
+app_pipeline_data(struct app_params *app, uint32_t id)
+{
+	struct app_pipeline_params *params;
+
+	APP_PARAM_FIND_BY_ID(app->pipeline_params, "PIPELINE", id, params);
+	if (params == NULL)
+		return NULL;
+
+	return &app->pipeline_data[params - app->pipeline_params];
+}
+
+static inline void *
+app_pipeline_data_fe(struct app_params *app, uint32_t id)
+{
+	struct app_pipeline_data *pipeline_data;
+
+	pipeline_data = app_pipeline_data(app, id);
+	if (pipeline_data == NULL)
+		return NULL;
+
+	return pipeline_data->fe;
+}
+
+static inline struct rte_ring *
+app_pipeline_msgq_in_get(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct app_msgq_params *p;
+
+	APP_PARAM_FIND_BY_ID(app->msgq_params,
+		"MSGQ-REQ-PIPELINE",
+		pipeline_id,
+		p);
+	if (p == NULL)
+		return NULL;
+
+	return app->msgq[p - app->msgq_params];
+}
+
+static inline struct rte_ring *
+app_pipeline_msgq_out_get(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct app_msgq_params *p;
+
+	APP_PARAM_FIND_BY_ID(app->msgq_params,
+		"MSGQ-RSP-PIPELINE",
+		pipeline_id,
+		p);
+	if (p == NULL)
+		return NULL;
+
+	return app->msgq[p - app->msgq_params];
+}
+
+static inline void *
+app_msg_alloc(__rte_unused struct app_params *app)
+{
+	return rte_malloc(NULL, 2048, RTE_CACHE_LINE_SIZE);
+}
+
+static inline void
+app_msg_free(__rte_unused struct app_params *app,
+	void *msg)
+{
+	rte_free(msg);
+}
+
+static inline void
+app_msg_send(struct app_params *app,
+	uint32_t pipeline_id,
+	void *msg)
+{
+	struct rte_ring *r = app_pipeline_msgq_in_get(app, pipeline_id);
+	int status;
+
+	do {
+		status = rte_ring_sp_enqueue(r, msg);
+	} while (status == -ENOBUFS);
+}
+
+static inline void *
+app_msg_recv(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct rte_ring *r = app_pipeline_msgq_out_get(app, pipeline_id);
+	void *msg;
+	int status = rte_ring_sc_dequeue(r, &msg);
+
+	if (status != 0)
+		return NULL;
+
+	return msg;
+}
+
+static inline void *
+app_msg_send_recv(struct app_params *app,
+	uint32_t pipeline_id,
+	void *msg,
+	uint32_t timeout_ms)
+{
+	struct rte_ring *r_req = app_pipeline_msgq_in_get(app, pipeline_id);
+	struct rte_ring *r_rsp = app_pipeline_msgq_out_get(app, pipeline_id);
+	uint64_t hz = rte_get_tsc_hz();
+	void *msg_recv;
+	uint64_t deadline;
+	int status;
+
+	/* send */
+	do {
+		status = rte_ring_sp_enqueue(r_req, (void *) msg);
+	} while (status == -ENOBUFS);
+
+	/* recv */
+	deadline = (timeout_ms) ?
+		(rte_rdtsc() + ((hz * timeout_ms) / 1000)) :
+		UINT64_MAX;
+
+	do {
+		if (rte_rdtsc() > deadline)
+			return NULL;
+
+		status = rte_ring_sc_dequeue(r_rsp, &msg_recv);
+	} while (status != 0);
+
+	return msg_recv;
+}
+
+int
+app_pipeline_ping(struct app_params *app,
+	uint32_t pipeline_id);
+
+int
+app_pipeline_stats_port_in(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id,
+	struct rte_pipeline_port_in_stats *stats);
+
+int
+app_pipeline_stats_port_out(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id,
+	struct rte_pipeline_port_out_stats *stats);
+
+int
+app_pipeline_stats_table(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t table_id,
+	struct rte_pipeline_table_stats *stats);
+
+int
+app_pipeline_port_in_enable(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id);
+
+int
+app_pipeline_port_in_disable(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id);
+
+int
+app_link_config(struct app_params *app,
+	uint32_t link_id,
+	uint32_t ip,
+	uint32_t depth);
+
+int
+app_link_up(struct app_params *app,
+	uint32_t link_id);
+
+int
+app_link_down(struct app_params *app,
+	uint32_t link_id);
+
+int
+app_pipeline_common_cmd_push(struct app_params *app);
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_master.c b/examples/ip_pipeline/pipeline/pipeline_master.c
new file mode 100644
index 0000000..1ccdad1
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_master.c
@@ -0,0 +1,47 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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 "pipeline_master.h"
+#include "pipeline_master_be.h"
+
+static struct pipeline_fe_ops pipeline_master_fe_ops = {
+	.f_init = NULL,
+	.f_free = NULL,
+	.cmds = NULL,
+};
+
+struct pipeline_type pipeline_master = {
+	.name = "MASTER",
+	.be_ops = &pipeline_master_be_ops,
+	.fe_ops = &pipeline_master_fe_ops,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_master.h b/examples/ip_pipeline/pipeline/pipeline_master.h
new file mode 100644
index 0000000..3fe3030
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_master.h
@@ -0,0 +1,41 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_MASTER_H__
+#define __INCLUDE_PIPELINE_MASTER_H__
+
+#include "pipeline.h"
+
+extern struct pipeline_type pipeline_master;
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.c b/examples/ip_pipeline/pipeline/pipeline_master_be.c
new file mode 100644
index 0000000..ac0cbbc
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_master_be.c
@@ -0,0 +1,150 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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 <fcntl.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include "app.h"
+#include "pipeline_master_be.h"
+
+struct pipeline_master {
+	struct app_params *app;
+	struct cmdline *cl;
+	int script_file_done;
+} __rte_cache_aligned;
+
+static void*
+pipeline_init(__rte_unused struct pipeline_params *params, void *arg)
+{
+	struct app_params *app = (struct app_params *) arg;
+	struct pipeline_master *p;
+	uint32_t size;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct pipeline_master));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		return NULL;
+
+	/* Initialization */
+	p->app = app;
+
+	p->cl = cmdline_stdin_new(app->cmds, "pipeline> ");
+	if (p->cl == NULL) {
+		rte_free(p);
+		return NULL;
+	}
+
+	p->script_file_done = 0;
+	if (app->script_file == NULL)
+		p->script_file_done = 1;
+
+	return (void *) p;
+}
+
+static int
+pipeline_free(void *pipeline)
+{
+	struct pipeline_master *p = (struct pipeline_master *) pipeline;
+
+	if (p == NULL)
+		return -EINVAL;
+
+	cmdline_stdin_exit(p->cl);
+	rte_free(p);
+
+	return 0;
+}
+
+static int
+pipeline_run(void *pipeline)
+{
+	struct pipeline_master *p = (struct pipeline_master *) pipeline;
+	int status;
+
+	if (p->script_file_done == 0) {
+		struct app_params *app = p->app;
+		int fd = open(app->script_file, O_RDONLY);
+
+		if (fd < 0)
+			printf("Cannot open CLI script file \"%s\"\n",
+				app->script_file);
+		else {
+			struct cmdline *file_cl;
+
+			printf("Running CLI script file \"%s\" ...\n",
+				app->script_file);
+			file_cl = cmdline_new(p->cl->ctx, "", fd, 1);
+			cmdline_interact(file_cl);
+			close(fd);
+		}
+
+		p->script_file_done = 1;
+	}
+
+	status = cmdline_poll(p->cl);
+	if (status < 0)
+		rte_panic("CLI poll error (%" PRId32 ")\n", status);
+	else if (status == RDLINE_EXITED) {
+		cmdline_stdin_exit(p->cl);
+		rte_exit(0, "Bye!\n");
+	}
+
+	return 0;
+}
+
+static int
+pipeline_timer(__rte_unused void *pipeline)
+{
+	return 0;
+}
+
+struct pipeline_be_ops pipeline_master_be_ops = {
+		.f_init = pipeline_init,
+		.f_free = pipeline_free,
+		.f_run = pipeline_run,
+		.f_timer = pipeline_timer,
+		.f_track = NULL,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_master_be.h b/examples/ip_pipeline/pipeline/pipeline_master_be.h
new file mode 100644
index 0000000..00b71fe
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_master_be.h
@@ -0,0 +1,41 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_MASTER_BE_H__
+#define __INCLUDE_PIPELINE_MASTER_BE_H__
+
+#include "pipeline_common_be.h"
+
+extern struct pipeline_be_ops pipeline_master_be_ops;
+
+#endif
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 06/11] ip_pipeline: added application thread
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (4 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 07/11] ip_pipeline: moved config files to separate folder Maciej Gajdzica
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

Application thread runs pipelines on assigned cores.

Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
---
 examples/ip_pipeline/Makefile |    1 +
 examples/ip_pipeline/main.c   |    6 +++
 examples/ip_pipeline/thread.c |  110 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+)
 create mode 100644 examples/ip_pipeline/thread.c

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 9ce80a8..f255338 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -53,6 +53,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse_tm.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_check.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.c
 
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
diff --git a/examples/ip_pipeline/main.c b/examples/ip_pipeline/main.c
index ef68c86..862e2f2 100644
--- a/examples/ip_pipeline/main.c
+++ b/examples/ip_pipeline/main.c
@@ -52,5 +52,11 @@ main(int argc, char **argv)
 	/* Init */
 	app_init(&app);
 
+	/* Run-time */
+	rte_eal_mp_remote_launch(
+		app_thread,
+		(void *) &app,
+		CALL_MASTER);
+
 	return 0;
 }
diff --git a/examples/ip_pipeline/thread.c b/examples/ip_pipeline/thread.c
new file mode 100644
index 0000000..b2a8656
--- /dev/null
+++ b/examples/ip_pipeline/thread.c
@@ -0,0 +1,110 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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_cycles.h>
+#include <rte_pipeline.h>
+
+#include "pipeline_common_be.h"
+#include "app.h"
+
+int app_thread(void *arg)
+{
+	struct app_params *app = (struct app_params *) arg;
+	uint32_t core_id = rte_lcore_id(), i, j;
+	struct app_thread_data *t = &app->thread_data[core_id];
+	uint32_t n_regular = RTE_MIN(t->n_regular, RTE_DIM(t->regular));
+	uint32_t n_custom = RTE_MIN(t->n_custom, RTE_DIM(t->custom));
+
+	for (i = 0; ; i++) {
+		/* Run regular pipelines */
+		for (j = 0; j < n_regular; j++) {
+			struct app_thread_pipeline_data *data = &t->regular[j];
+			struct pipeline *p = data->be;
+
+			rte_pipeline_run(p->p);
+		}
+
+		/* Run custom pipelines */
+		for (j = 0; j < n_custom; j++) {
+			struct app_thread_pipeline_data *data = &t->custom[j];
+
+			data->f_run(data->be);
+		}
+
+		/* Timer */
+		if ((i & 0xF) == 0) {
+			uint64_t time = rte_get_tsc_cycles();
+			uint64_t t_deadline = UINT64_MAX;
+
+			if (time < t->deadline)
+				continue;
+
+			/* Timer for regular pipelines */
+			for (j = 0; j < n_regular; j++) {
+				struct app_thread_pipeline_data *data =
+					&t->regular[j];
+				uint64_t p_deadline = data->deadline;
+
+				if (p_deadline <= time) {
+					data->f_timer(data->be);
+					p_deadline = time + data->timer_period;
+					data->deadline = p_deadline;
+				}
+
+				if (p_deadline < t_deadline)
+					t_deadline = p_deadline;
+			}
+
+			/* Timer for custom pipelines */
+			for (j = 0; j < n_custom; j++) {
+				struct app_thread_pipeline_data *data =
+					&t->custom[j];
+				uint64_t p_deadline = data->deadline;
+
+				if (p_deadline <= time) {
+					data->f_timer(data->be);
+					p_deadline = time + data->timer_period;
+					data->deadline = p_deadline;
+				}
+
+				if (p_deadline < t_deadline)
+					t_deadline = p_deadline;
+			}
+
+			t->deadline = t_deadline;
+		}
+	}
+
+	return 0;
+}
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 07/11] ip_pipeline: moved config files to separate folder
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (5 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 06/11] ip_pipeline: added application thread Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 08/11] ip_pipeline: added new implementation of passthrough pipeline Maciej Gajdzica
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

Created new folder for config(.cfg) and script(.sh) files.

Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
---
 examples/ip_pipeline/config/ip_pipeline.cfg |    9 +++
 examples/ip_pipeline/config/ip_pipeline.sh  |    5 ++
 examples/ip_pipeline/config/tm_profile.cfg  |  105 +++++++++++++++++++++++++++
 examples/ip_pipeline/ip_pipeline.cfg        |   56 --------------
 examples/ip_pipeline/ip_pipeline.sh         |   18 -----
 5 files changed, 119 insertions(+), 74 deletions(-)
 create mode 100644 examples/ip_pipeline/config/ip_pipeline.cfg
 create mode 100644 examples/ip_pipeline/config/ip_pipeline.sh
 create mode 100644 examples/ip_pipeline/config/tm_profile.cfg
 delete mode 100644 examples/ip_pipeline/ip_pipeline.cfg
 delete mode 100644 examples/ip_pipeline/ip_pipeline.sh

diff --git a/examples/ip_pipeline/config/ip_pipeline.cfg b/examples/ip_pipeline/config/ip_pipeline.cfg
new file mode 100644
index 0000000..095ed25
--- /dev/null
+++ b/examples/ip_pipeline/config/ip_pipeline.cfg
@@ -0,0 +1,9 @@
+[PIPELINE0]
+type = MASTER
+core = 0
+
+[PIPELINE1]
+type = PASS-THROUGH
+core = 1
+pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
+pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
diff --git a/examples/ip_pipeline/config/ip_pipeline.sh b/examples/ip_pipeline/config/ip_pipeline.sh
new file mode 100644
index 0000000..4fca259
--- /dev/null
+++ b/examples/ip_pipeline/config/ip_pipeline.sh
@@ -0,0 +1,5 @@
+#
+#run config/ip_pipeline.sh
+#
+
+p 1 ping
diff --git a/examples/ip_pipeline/config/tm_profile.cfg b/examples/ip_pipeline/config/tm_profile.cfg
new file mode 100644
index 0000000..53edb67
--- /dev/null
+++ b/examples/ip_pipeline/config/tm_profile.cfg
@@ -0,0 +1,105 @@
+;   BSD LICENSE
+;
+;   Copyright(c) 2010-2014 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.
+
+; This file enables the following hierarchical scheduler configuration for each
+; 10GbE output port:
+;	* Single subport (subport 0):
+;		- Subport rate set to 100% of port rate
+;		- Each of the 4 traffic classes has rate set to 100% of port rate
+;	* 4K pipes per subport 0 (pipes 0 .. 4095) with identical configuration:
+;		- Pipe rate set to 1/4K of port rate
+;		- Each of the 4 traffic classes has rate set to 100% of pipe rate
+;		- Within each traffic class, the byte-level WRR weights for the 4 queues
+;         are set to 1:1:1:1
+;
+; For more details, please refer to chapter "Quality of Service (QoS) Framework"
+; of Intel Data Plane Development Kit (Intel DPDK) Programmer's Guide.
+
+; Port configuration
+[port]
+frame overhead = 24 ; frame overhead = Preamble (7) + SFD (1) + FCS (4) + IFG (12)
+mtu = 1522; mtu = Q-in-Q MTU (FCS not included)
+number of subports per port = 1
+number of pipes per subport = 4096
+queue sizes = 64 64 64 64
+
+; Subport configuration
+[subport 0]
+tb rate = 1250000000           ; Bytes per second
+tb size = 1000000              ; Bytes
+
+tc 0 rate = 1250000000         ; Bytes per second
+tc 1 rate = 1250000000         ; Bytes per second
+tc 2 rate = 1250000000         ; Bytes per second
+tc 3 rate = 1250000000         ; Bytes per second
+tc period = 10                 ; Milliseconds
+
+pipe 0-4095 = 0                ; These pipes are configured with pipe profile 0
+
+; Pipe configuration
+[pipe profile 0]
+tb rate = 305175               ; Bytes per second
+tb size = 1000000              ; Bytes
+
+tc 0 rate = 305175             ; Bytes per second
+tc 1 rate = 305175             ; Bytes per second
+tc 2 rate = 305175             ; Bytes per second
+tc 3 rate = 305175             ; Bytes per second
+tc period = 40                 ; Milliseconds
+
+tc 3 oversubscription weight = 1
+
+tc 0 wrr weights = 1 1 1 1
+tc 1 wrr weights = 1 1 1 1
+tc 2 wrr weights = 1 1 1 1
+tc 3 wrr weights = 1 1 1 1
+
+; RED params per traffic class and color (Green / Yellow / Red)
+[red]
+tc 0 wred min = 48 40 32
+tc 0 wred max = 64 64 64
+tc 0 wred inv prob = 10 10 10
+tc 0 wred weight = 9 9 9
+
+tc 1 wred min = 48 40 32
+tc 1 wred max = 64 64 64
+tc 1 wred inv prob = 10 10 10
+tc 1 wred weight = 9 9 9
+
+tc 2 wred min = 48 40 32
+tc 2 wred max = 64 64 64
+tc 2 wred inv prob = 10 10 10
+tc 2 wred weight = 9 9 9
+
+tc 3 wred min = 48 40 32
+tc 3 wred max = 64 64 64
+tc 3 wred inv prob = 10 10 10
+tc 3 wred weight = 9 9 9
diff --git a/examples/ip_pipeline/ip_pipeline.cfg b/examples/ip_pipeline/ip_pipeline.cfg
deleted file mode 100644
index 428830d..0000000
--- a/examples/ip_pipeline/ip_pipeline.cfg
+++ /dev/null
@@ -1,56 +0,0 @@
-;   BSD LICENSE
-;
-;   Copyright(c) 2010-2014 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.
-
-; Core configuration
-[core 0]
-type = MASTER
-queues in  = 15 16 17 -1 -1 -1 -1 -1
-queues out = 12 13 14 -1 -1 -1 -1 -1
-
-[core 1]
-type = RX
-queues in  = -1 -1 -1 -1 -1 -1 -1 12
-queues out =  0  1  2  3 -1 -1 -1 15
-
-[core 2]
-type = FC
-queues in  =  0  1  2  3 -1 -1 -1 13
-queues out =  4  5  6  7 -1 -1 -1 16
-
-[core 3]
-type = RT
-queues in  =  4  5  6  7 -1 -1 -1 14
-queues out =  8  9 10 11 -1 -1 -1 17
-
-[core 4]
-type = TX
-queues in  =  8  9 10 11 -1 -1 -1 -1
-queues out = -1 -1 -1 -1 -1 -1 -1 -1
diff --git a/examples/ip_pipeline/ip_pipeline.sh b/examples/ip_pipeline/ip_pipeline.sh
deleted file mode 100644
index c3419ca..0000000
--- a/examples/ip_pipeline/ip_pipeline.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#Address Resolution Protocol (ARP) Table
-#arp add iface ipaddr macaddr
-arp add 0 0.0.0.1 0a:0b:0c:0d:0e:0f
-arp add 1 0.128.0.1 1a:1b:1c:1d:1e:1f
-
-#Routing Table
-#route add ipaddr prefixlen iface gateway
-route add 0.0.0.0 9 0 0.0.0.1
-route add 0.128.0.0 9 1 0.128.0.1
-
-#Flow Table
-flow add all
-#flow add 0.0.0.0 1.2.3.4 0 0 6 0
-#flow add 10.11.12.13 0.0.0.0 0 0 6 1
-
-#Firewall
-#firewall add 1 0.0.0.0 0 0.0.0.0 9 0 65535 0 65535 6 0xf 0
-#firewall add 1 0.0.0.0 0 0.128.0.0 9 0 65535 0 65535 6 0xf 1
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 08/11] ip_pipeline: added new implementation of passthrough pipeline
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (6 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 07/11] ip_pipeline: moved config files to separate folder Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 09/11] ip_pipeline: added new implementation of firewall pipeline Maciej Gajdzica
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

From: Jasvinder Singh <jasvinder.singh@intel.com>

Passthrough pipeline implementation is split to two files.
pipeline_passthrough.c file handles front-end functions (cli commands
parsing) pipeline_passthrough_ops.c contains implementation of functions
done by pipeline (back-end).

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
---
 examples/ip_pipeline/Makefile                      |    2 +
 examples/ip_pipeline/init.c                        |    2 +
 examples/ip_pipeline/pipeline/hash_func.h          |  351 +++++++++
 .../ip_pipeline/pipeline/pipeline_actions_common.h |  119 +++
 .../ip_pipeline/pipeline/pipeline_passthrough.c    |  192 +----
 .../ip_pipeline/pipeline/pipeline_passthrough.h    |   41 ++
 .../ip_pipeline/pipeline/pipeline_passthrough_be.c |  772 ++++++++++++++++++++
 .../ip_pipeline/pipeline/pipeline_passthrough_be.h |   41 ++
 8 files changed, 1341 insertions(+), 179 deletions(-)
 create mode 100644 examples/ip_pipeline/pipeline/hash_func.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_actions_common.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_passthrough_be.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index f255338..930dc61 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -60,6 +60,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_common_fe.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_master_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_master.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough_be.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough.c
 
 CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
 CFLAGS += -O3
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 036b316..0f3bf0b 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -46,6 +46,7 @@
 #include "pipeline.h"
 #include "pipeline_common_fe.h"
 #include "pipeline_master.h"
+#include "pipeline_passthrough.h"
 
 #define APP_NAME_SIZE	32
 
@@ -1285,6 +1286,7 @@ int app_init(struct app_params *app)
 
 	app_pipeline_common_cmd_push(app);
 	app_pipeline_type_register(app, &pipeline_master);
+	app_pipeline_type_register(app, &pipeline_passthrough);
 
 	app_init_pipelines(app);
 	app_init_threads(app);
diff --git a/examples/ip_pipeline/pipeline/hash_func.h b/examples/ip_pipeline/pipeline/hash_func.h
new file mode 100644
index 0000000..7846300
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/hash_func.h
@@ -0,0 +1,351 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __INCLUDE_HASH_FUNC_H__
+#define __INCLUDE_HASH_FUNC_H__
+
+static inline uint64_t
+hash_xor_key8(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0;
+
+	xor0 = seed ^ k[0];
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key16(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0;
+
+	xor0 = (k[0] ^ seed) ^ k[1];
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key24(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0;
+
+	xor0 = (k[0] ^ seed) ^ k[1];
+
+	xor0 ^= k[2];
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key32(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0, xor1;
+
+	xor0 = (k[0] ^ seed) ^ k[1];
+	xor1 = k[2] ^ k[3];
+
+	xor0 ^= xor1;
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key40(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0, xor1;
+
+	xor0 = (k[0] ^ seed) ^ k[1];
+	xor1 = k[2] ^ k[3];
+
+	xor0 ^= xor1;
+
+	xor0 ^= k[4];
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key48(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0, xor1, xor2;
+
+	xor0 = (k[0] ^ seed) ^ k[1];
+	xor1 = k[2] ^ k[3];
+	xor2 = k[4] ^ k[5];
+
+	xor0 ^= xor1;
+
+	xor0 ^= xor2;
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key56(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0, xor1, xor2;
+
+	xor0 = (k[0] ^ seed) ^ k[1];
+	xor1 = k[2] ^ k[3];
+	xor2 = k[4] ^ k[5];
+
+	xor0 ^= xor1;
+	xor2 ^= k[6];
+
+	xor0 ^= xor2;
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key64(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t xor0, xor1, xor2, xor3;
+
+	xor0 = (k[0] ^ seed) ^ k[1];
+	xor1 = k[2] ^ k[3];
+	xor2 = k[4] ^ k[5];
+	xor3 = k[6] ^ k[7];
+
+	xor0 ^= xor1;
+	xor2 ^= xor3;
+
+	xor0 ^= xor2;
+
+	return (xor0 >> 32) ^ xor0;
+}
+
+#if defined(__x86_64__)
+
+#include <x86intrin.h>
+
+static inline uint64_t
+hash_crc_key8(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t crc0;
+
+	crc0 = _mm_crc32_u64(seed, k[0]);
+
+	return crc0;
+}
+
+static inline uint64_t
+hash_crc_key16(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t k0, crc0, crc1;
+
+	k0 = k[0];
+
+	crc0 = _mm_crc32_u64(k0, seed);
+	crc1 = _mm_crc32_u64(k0 >> 32, k[1]);
+
+	crc0 ^= crc1;
+
+	return crc0;
+}
+
+static inline uint64_t
+hash_crc_key24(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t k0, k2, crc0, crc1;
+
+	k0 = k[0];
+	k2 = k[2];
+
+	crc0 = _mm_crc32_u64(k0, seed);
+	crc1 = _mm_crc32_u64(k0 >> 32, k[1]);
+
+	crc0 = _mm_crc32_u64(crc0, k2);
+
+	crc0 ^= crc1;
+
+	return crc0;
+}
+
+static inline uint64_t
+hash_crc_key32(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t k0, k2, crc0, crc1, crc2, crc3;
+
+	k0 = k[0];
+	k2 = k[2];
+
+	crc0 = _mm_crc32_u64(k0, seed);
+	crc1 = _mm_crc32_u64(k0 >> 32, k[1]);
+
+	crc2 = _mm_crc32_u64(k2, k[3]);
+	crc3 = k2 >> 32;
+
+	crc0 = _mm_crc32_u64(crc0, crc1);
+	crc1 = _mm_crc32_u64(crc2, crc3);
+
+	crc0 ^= crc1;
+
+	return crc0;
+}
+
+static inline uint64_t
+hash_crc_key40(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t k0, k2, crc0, crc1, crc2, crc3;
+
+	k0 = k[0];
+	k2 = k[2];
+
+	crc0 = _mm_crc32_u64(k0, seed);
+	crc1 = _mm_crc32_u64(k0 >> 32, k[1]);
+
+	crc2 = _mm_crc32_u64(k2, k[3]);
+	crc3 = _mm_crc32_u64(k2 >> 32, k[4]);
+
+	crc0 = _mm_crc32_u64(crc0, crc1);
+	crc1 = _mm_crc32_u64(crc2, crc3);
+
+	crc0 ^= crc1;
+
+	return crc0;
+}
+
+static inline uint64_t
+hash_crc_key48(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t k0, k2, k5, crc0, crc1, crc2, crc3;
+
+	k0 = k[0];
+	k2 = k[2];
+	k5 = k[5];
+
+	crc0 = _mm_crc32_u64(k0, seed);
+	crc1 = _mm_crc32_u64(k0 >> 32, k[1]);
+
+	crc2 = _mm_crc32_u64(k2, k[3]);
+	crc3 = _mm_crc32_u64(k2 >> 32, k[4]);
+
+	crc0 = _mm_crc32_u64(crc0, (crc1 << 32) ^ crc2);
+	crc1 = _mm_crc32_u64(crc3, k5);
+
+	crc0 ^= crc1;
+
+	return crc0;
+}
+
+static inline uint64_t
+hash_crc_key56(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5;
+
+	k0 = k[0];
+	k2 = k[2];
+	k5 = k[5];
+
+	crc0 = _mm_crc32_u64(k0, seed);
+	crc1 = _mm_crc32_u64(k0 >> 32, k[1]);
+
+	crc2 = _mm_crc32_u64(k2, k[3]);
+	crc3 = _mm_crc32_u64(k2 >> 32, k[4]);
+
+	crc4 = _mm_crc32_u64(k5, k[6]);
+	crc5 = k5 >> 32;
+
+	crc0 = _mm_crc32_u64(crc0, (crc1 << 32) ^ crc2);
+	crc1 = _mm_crc32_u64(crc3, (crc4 << 32) ^ crc5);
+
+	crc0 ^= crc1;
+
+	return crc0;
+}
+
+static inline uint64_t
+hash_crc_key64(void *key, __rte_unused uint32_t key_size, uint64_t seed)
+{
+	uint64_t *k = key;
+	uint64_t k0, k2, k5, crc0, crc1, crc2, crc3, crc4, crc5;
+
+	k0 = k[0];
+	k2 = k[2];
+	k5 = k[5];
+
+	crc0 = _mm_crc32_u64(k0, seed);
+	crc1 = _mm_crc32_u64(k0 >> 32, k[1]);
+
+	crc2 = _mm_crc32_u64(k2, k[3]);
+	crc3 = _mm_crc32_u64(k2 >> 32, k[4]);
+
+	crc4 = _mm_crc32_u64(k5, k[6]);
+	crc5 = _mm_crc32_u64(k5 >> 32, k[7]);
+
+	crc0 = _mm_crc32_u64(crc0, (crc1 << 32) ^ crc2);
+	crc1 = _mm_crc32_u64(crc3, (crc4 << 32) ^ crc5);
+
+	crc0 ^= crc1;
+
+	return crc0;
+}
+
+#define hash_default_key8			hash_crc_key8
+#define hash_default_key16			hash_crc_key16
+#define hash_default_key24			hash_crc_key24
+#define hash_default_key32			hash_crc_key32
+#define hash_default_key40			hash_crc_key40
+#define hash_default_key48			hash_crc_key48
+#define hash_default_key56			hash_crc_key56
+#define hash_default_key64			hash_crc_key64
+
+#else
+
+#define hash_default_key8			hash_xor_key8
+#define hash_default_key16			hash_xor_key16
+#define hash_default_key24			hash_xor_key24
+#define hash_default_key32			hash_xor_key32
+#define hash_default_key40			hash_xor_key40
+#define hash_default_key48			hash_xor_key48
+#define hash_default_key56			hash_xor_key56
+#define hash_default_key64			hash_xor_key64
+
+#endif
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_actions_common.h b/examples/ip_pipeline/pipeline/pipeline_actions_common.h
new file mode 100644
index 0000000..4b5d5c4
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_actions_common.h
@@ -0,0 +1,119 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __INCLUDE_PIPELINE_ACTIONS_COMMON_H__
+#define __INCLUDE_PIPELINE_ACTIONS_COMMON_H__
+
+#define PIPELINE_PORT_IN_AH(f_ah, f_pkt_work, f_pkt4_work)		\
+static int								\
+f_ah(									\
+	struct rte_mbuf **pkts,						\
+	uint32_t n_pkts,						\
+	uint64_t *pkts_mask,						\
+	void *arg)							\
+{									\
+	uint32_t i;							\
+									\
+	for (i = 0; i < (n_pkts & (~0x3LLU)); i += 4)			\
+		f_pkt4_work(&pkts[i], arg);				\
+									\
+	for ( ; i < n_pkts; i++)					\
+		f_pkt_work(pkts[i], arg);				\
+									\
+	*pkts_mask = (~0LLU) >> (64 - n_pkts);				\
+									\
+	return 0;							\
+}
+
+#define PIPELINE_TABLE_AH_HIT(f_ah, f_pkt_work, f_pkt4_work)		\
+static int								\
+f_ah(									\
+	struct rte_mbuf **pkts,						\
+	uint64_t *pkts_mask,						\
+	struct rte_pipeline_table_entry **entries,			\
+	void *arg)							\
+{									\
+	uint64_t pkts_in_mask = *pkts_mask;				\
+									\
+	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {			\
+		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);	\
+		uint32_t i;						\
+									\
+		for (i = 0; i < (n_pkts & (~0x3LLU)); i += 4)		\
+			f_pkt4_work(&pkts[i], &entries[i], arg);	\
+									\
+		for ( ; i < n_pkts; i++)				\
+			f_pkt_work(pkts[i], entries[i], arg);		\
+	} else								\
+		for ( ; pkts_in_mask; ) {				\
+			uint32_t pos = __builtin_ctzll(pkts_in_mask);	\
+			uint64_t pkt_mask = 1LLU << pos;		\
+									\
+			pkts_in_mask &= ~pkt_mask;			\
+			f_pkt_work(pkts[pos], entries[pos], arg);	\
+		}							\
+									\
+	return 0;							\
+}
+
+#define PIPELINE_TABLE_AH_MISS(f_ah, f_pkt_work, f_pkt4_work)		\
+static int								\
+f_ah(									\
+	struct rte_mbuf **pkts,						\
+	uint64_t *pkts_mask,						\
+	struct rte_pipeline_table_entry *entry,				\
+	void *arg)							\
+{									\
+	uint64_t pkts_in_mask = *pkts_mask;				\
+									\
+	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {			\
+		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);	\
+		uint32_t i;						\
+									\
+		for (i = 0; i < (n_pkts & (~0x3LLU)); i += 4)		\
+			f_pkt4_work(&pkts[i], entry, arg);		\
+									\
+		for ( ; i < n_pkts; i++)				\
+			f_pkt_work(pkts[i], entry, arg);		\
+	} else								\
+		for ( ; pkts_in_mask; ) {				\
+			uint32_t pos = __builtin_ctzll(pkts_in_mask);	\
+			uint64_t pkt_mask = 1LLU << pos;		\
+									\
+			pkts_in_mask &= ~pkt_mask;			\
+			f_pkt_work(pkts[pos], entry, arg);		\
+		}							\
+									\
+	return 0;							\
+}
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_passthrough.c b/examples/ip_pipeline/pipeline/pipeline_passthrough.c
index 948b2c1..fc2cae5 100644
--- a/examples/ip_pipeline/pipeline/pipeline_passthrough.c
+++ b/examples/ip_pipeline/pipeline/pipeline_passthrough.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -31,183 +31,17 @@
  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include "pipeline_passthrough.h"
+#include "pipeline_passthrough_be.h"
 
-#include <rte_malloc.h>
-#include <rte_log.h>
+static struct pipeline_fe_ops pipeline_passthrough_fe_ops = {
+	.f_init = NULL,
+	.f_free = NULL,
+	.cmds = NULL,
+};
 
-#include <rte_port_ring.h>
-#include <rte_table_stub.h>
-#include <rte_pipeline.h>
-
-#include "main.h"
-
-void
-app_main_loop_pipeline_passthrough(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id[APP_MAX_PORTS];
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_PT))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing pass-through\n", core_id);
-
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
-	if (p == NULL)
-		rte_panic("%s: Unable to configure the pipeline\n", __func__);
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i])) {
-			rte_panic("%s: Unable to configure input port for "
-				"ring %d\n", __func__, i);
-		}
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i])) {
-			rte_panic("%s: Unable to configure output port for "
-				"ring %d\n", __func__, i);
-		}
-	}
-
-	/* Table configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_stub_ops,
-			.arg_create = NULL,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id[i]))
-			rte_panic("%s: Unable to configure table %u\n",
-				__func__, i);
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++) {
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id[i])) {
-			rte_panic("%s: Unable to connect input port %u to "
-				"table %u\n", __func__, port_in_id[i],
-				table_id[i]);
-		}
-	}
-
-	/* Add entries to tables */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_pipeline_table_entry default_entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i]},
-		};
-
-		struct rte_pipeline_table_entry *default_entry_ptr;
-
-		if (rte_pipeline_table_default_entry_add(p, table_id[i],
-			&default_entry, &default_entry_ptr))
-			rte_panic("%s: Unable to add default entry to "
-				"table %u\n", __func__, table_id[i]);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("%s: Pipeline consistency check failed\n", __func__);
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0)
-			rte_pipeline_flush(p);
-	}
-}
-
-void
-app_main_loop_passthrough(void) {
-	struct app_mbuf_array *m;
-	uint32_t i;
-
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_PT))
-		rte_panic("Core %u misconfiguration\n", core_id);
-
-	RTE_LOG(INFO, USER1, "Core %u is doing pass-through (no pipeline)\n",
-		core_id);
-
-	m = rte_malloc_socket(NULL, sizeof(struct app_mbuf_array),
-		RTE_CACHE_LINE_SIZE, rte_socket_id());
-	if (m == NULL)
-		rte_panic("%s: cannot allocate buffer space\n", __func__);
-
-	for (i = 0; ; i = ((i + 1) & (app.n_ports - 1))) {
-		int ret;
-
-		ret = rte_ring_sc_dequeue_bulk(
-			app.rings[core_params->swq_in[i]],
-			(void **) m->array,
-			app.bsz_swq_rd);
-
-		if (ret == -ENOENT)
-			continue;
-
-		do {
-			ret = rte_ring_sp_enqueue_bulk(
-				app.rings[core_params->swq_out[i]],
-				(void **) m->array,
-				app.bsz_swq_wr);
-		} while (ret < 0);
-	}
-}
+struct pipeline_type pipeline_passthrough = {
+	.name = "PASS-THROUGH",
+	.be_ops = &pipeline_passthrough_be_ops,
+	.fe_ops = &pipeline_passthrough_fe_ops,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_passthrough.h b/examples/ip_pipeline/pipeline/pipeline_passthrough.h
new file mode 100644
index 0000000..420a876
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_passthrough.h
@@ -0,0 +1,41 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_PASSTHROUGH_H__
+#define __INCLUDE_PIPELINE_PASSTHROUGH_H__
+
+#include "pipeline.h"
+
+extern struct pipeline_type pipeline_passthrough;
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_passthrough_be.c b/examples/ip_pipeline/pipeline/pipeline_passthrough_be.c
new file mode 100644
index 0000000..83a16c2
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_passthrough_be.c
@@ -0,0 +1,772 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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 <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_byteorder.h>
+#include <rte_table_stub.h>
+#include <rte_table_hash.h>
+#include <rte_pipeline.h>
+
+#include "pipeline_passthrough_be.h"
+#include "pipeline_actions_common.h"
+#include "hash_func.h"
+
+enum flow_key_type {
+	FLOW_KEY_QINQ,
+	FLOW_KEY_IPV4_5TUPLE,
+	FLOW_KEY_IPV6_5TUPLE,
+};
+
+struct pipeline_passthrough {
+	struct pipeline p;
+
+	uint32_t key_type_valid;
+	enum flow_key_type key_type;
+	uint32_t key_offset_rd;
+	uint32_t key_offset_wr;
+	uint32_t hash_offset;
+
+	rte_table_hash_op_hash f_hash;
+	rte_pipeline_port_in_action_handler f_port_in_ah;
+} __rte_cache_aligned;
+
+static pipeline_msg_req_handler handlers[] = {
+	[PIPELINE_MSG_REQ_PING] =
+		pipeline_msg_req_ping_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_IN] =
+		pipeline_msg_req_stats_port_in_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_OUT] =
+		pipeline_msg_req_stats_port_out_handler,
+	[PIPELINE_MSG_REQ_STATS_TABLE] =
+		pipeline_msg_req_stats_table_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_ENABLE] =
+		pipeline_msg_req_port_in_enable_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_DISABLE] =
+		pipeline_msg_req_port_in_disable_handler,
+	[PIPELINE_MSG_REQ_CUSTOM] =
+		pipeline_msg_req_invalid_handler,
+};
+
+static inline void
+pkt_work_key_qinq(
+	struct rte_mbuf *pkt,
+	void *arg)
+{
+	struct pipeline_passthrough *p_pt = arg;
+	uint32_t key_offset_rd = p_pt->key_offset_rd;
+	uint32_t key_offset_wr = p_pt->key_offset_wr;
+	uint32_t hash_offset = p_pt->hash_offset;
+
+	uint64_t *key_rd = RTE_MBUF_METADATA_UINT64_PTR(pkt, key_offset_rd);
+	uint64_t *key_wr = RTE_MBUF_METADATA_UINT64_PTR(pkt, key_offset_wr);
+	uint32_t *hash = RTE_MBUF_METADATA_UINT32_PTR(pkt, hash_offset);
+
+	/* Read */
+	uint64_t key_qinq = *key_rd & rte_bswap64(0x00000FFF00000FFFLLU);
+
+	/* Compute */
+	uint32_t hash_qinq = p_pt->f_hash(&key_qinq, 8, 0);
+
+	/* Write */
+	*key_wr = key_qinq;
+	*hash = hash_qinq;
+}
+
+static inline void
+pkt4_work_key_qinq(
+	struct rte_mbuf **pkt,
+	void *arg)
+{
+	struct pipeline_passthrough *p_pt = arg;
+	uint32_t key_offset_rd = p_pt->key_offset_rd;
+	uint32_t key_offset_wr = p_pt->key_offset_wr;
+	uint32_t hash_offset = p_pt->hash_offset;
+
+	uint64_t *key_rd0 = RTE_MBUF_METADATA_UINT64_PTR(pkt[0], key_offset_rd);
+	uint64_t *key_wr0 = RTE_MBUF_METADATA_UINT64_PTR(pkt[0], key_offset_wr);
+	uint32_t *hash0 = RTE_MBUF_METADATA_UINT32_PTR(pkt[0], hash_offset);
+
+	uint64_t *key_rd1 = RTE_MBUF_METADATA_UINT64_PTR(pkt[1], key_offset_rd);
+	uint64_t *key_wr1 = RTE_MBUF_METADATA_UINT64_PTR(pkt[1], key_offset_wr);
+	uint32_t *hash1 = RTE_MBUF_METADATA_UINT32_PTR(pkt[1], hash_offset);
+
+	uint64_t *key_rd2 = RTE_MBUF_METADATA_UINT64_PTR(pkt[2], key_offset_rd);
+	uint64_t *key_wr2 = RTE_MBUF_METADATA_UINT64_PTR(pkt[2], key_offset_wr);
+	uint32_t *hash2 = RTE_MBUF_METADATA_UINT32_PTR(pkt[2], hash_offset);
+
+	uint64_t *key_rd3 = RTE_MBUF_METADATA_UINT64_PTR(pkt[3], key_offset_rd);
+	uint64_t *key_wr3 = RTE_MBUF_METADATA_UINT64_PTR(pkt[3], key_offset_wr);
+	uint32_t *hash3 = RTE_MBUF_METADATA_UINT32_PTR(pkt[3], hash_offset);
+
+	/* Read */
+	uint64_t key_qinq0 = *key_rd0 & rte_bswap64(0x00000FFF00000FFFLLU);
+	uint64_t key_qinq1 = *key_rd1 & rte_bswap64(0x00000FFF00000FFFLLU);
+	uint64_t key_qinq2 = *key_rd2 & rte_bswap64(0x00000FFF00000FFFLLU);
+	uint64_t key_qinq3 = *key_rd3 & rte_bswap64(0x00000FFF00000FFFLLU);
+
+	/* Compute */
+	uint32_t hash_qinq0 = p_pt->f_hash(&key_qinq0, 8, 0);
+	uint32_t hash_qinq1 = p_pt->f_hash(&key_qinq1, 8, 0);
+	uint32_t hash_qinq2 = p_pt->f_hash(&key_qinq2, 8, 0);
+	uint32_t hash_qinq3 = p_pt->f_hash(&key_qinq3, 8, 0);
+
+	/* Write */
+	*key_wr0 = key_qinq0;
+	*key_wr1 = key_qinq1;
+	*key_wr2 = key_qinq2;
+	*key_wr3 = key_qinq3;
+
+	*hash0 = hash_qinq0;
+	*hash1 = hash_qinq1;
+	*hash2 = hash_qinq2;
+	*hash3 = hash_qinq3;
+}
+
+PIPELINE_PORT_IN_AH(port_in_ah_key_qinq, pkt_work_key_qinq, pkt4_work_key_qinq);
+
+static inline void
+pkt_work_key_ipv4(
+	struct rte_mbuf *pkt,
+	void *arg)
+{
+	struct pipeline_passthrough *p_pt = arg;
+	uint32_t key_offset_rd = p_pt->key_offset_rd;
+	uint32_t key_offset_wr = p_pt->key_offset_wr;
+	uint32_t hash_offset = p_pt->hash_offset;
+
+	uint64_t *key_rd = RTE_MBUF_METADATA_UINT64_PTR(pkt, key_offset_rd);
+	uint64_t *key_wr = RTE_MBUF_METADATA_UINT64_PTR(pkt, key_offset_wr);
+	uint32_t *hash = RTE_MBUF_METADATA_UINT32_PTR(pkt, hash_offset);
+	uint64_t key_ipv4[2];
+	uint32_t hash_ipv4;
+
+	/* Read */
+	key_ipv4[0] = key_rd[0] & rte_bswap64(0x00FF0000FFFFFFFFLLU);
+	key_ipv4[1] = key_rd[1];
+
+	/* Compute */
+	hash_ipv4 = p_pt->f_hash(key_ipv4, 16, 0);
+
+	/* Write */
+	key_wr[0] = key_ipv4[0];
+	key_wr[1] = key_ipv4[1];
+	*hash = hash_ipv4;
+}
+
+static inline void
+pkt4_work_key_ipv4(
+	struct rte_mbuf **pkt,
+	void *arg)
+{
+	struct pipeline_passthrough *p_pt = arg;
+	uint32_t key_offset_rd = p_pt->key_offset_rd;
+	uint32_t key_offset_wr = p_pt->key_offset_wr;
+	uint32_t hash_offset = p_pt->hash_offset;
+
+	uint64_t *key_rd0 = RTE_MBUF_METADATA_UINT64_PTR(pkt[0], key_offset_rd);
+	uint64_t *key_wr0 = RTE_MBUF_METADATA_UINT64_PTR(pkt[0], key_offset_wr);
+	uint32_t *hash0 = RTE_MBUF_METADATA_UINT32_PTR(pkt[0], hash_offset);
+
+	uint64_t *key_rd1 = RTE_MBUF_METADATA_UINT64_PTR(pkt[1], key_offset_rd);
+	uint64_t *key_wr1 = RTE_MBUF_METADATA_UINT64_PTR(pkt[1], key_offset_wr);
+	uint32_t *hash1 = RTE_MBUF_METADATA_UINT32_PTR(pkt[1], hash_offset);
+
+	uint64_t *key_rd2 = RTE_MBUF_METADATA_UINT64_PTR(pkt[2], key_offset_rd);
+	uint64_t *key_wr2 = RTE_MBUF_METADATA_UINT64_PTR(pkt[2], key_offset_wr);
+	uint32_t *hash2 = RTE_MBUF_METADATA_UINT32_PTR(pkt[2], hash_offset);
+
+	uint64_t *key_rd3 = RTE_MBUF_METADATA_UINT64_PTR(pkt[3], key_offset_rd);
+	uint64_t *key_wr3 = RTE_MBUF_METADATA_UINT64_PTR(pkt[3], key_offset_wr);
+	uint32_t *hash3 = RTE_MBUF_METADATA_UINT32_PTR(pkt[3], hash_offset);
+
+	uint64_t key_ipv4_0[2];
+	uint64_t key_ipv4_1[2];
+	uint64_t key_ipv4_2[2];
+	uint64_t key_ipv4_3[2];
+
+	uint32_t hash_ipv4_0;
+	uint32_t hash_ipv4_1;
+	uint32_t hash_ipv4_2;
+	uint32_t hash_ipv4_3;
+
+	/* Read */
+	key_ipv4_0[0] = key_rd0[0] & rte_bswap64(0x00FF0000FFFFFFFFLLU);
+	key_ipv4_1[0] = key_rd1[0] & rte_bswap64(0x00FF0000FFFFFFFFLLU);
+	key_ipv4_2[0] = key_rd2[0] & rte_bswap64(0x00FF0000FFFFFFFFLLU);
+	key_ipv4_3[0] = key_rd3[0] & rte_bswap64(0x00FF0000FFFFFFFFLLU);
+
+	key_ipv4_0[1] = key_rd0[1];
+	key_ipv4_1[1] = key_rd1[1];
+	key_ipv4_2[1] = key_rd2[1];
+	key_ipv4_3[1] = key_rd3[1];
+
+	/* Compute */
+	hash_ipv4_0 = p_pt->f_hash(key_ipv4_0, 16, 0);
+	hash_ipv4_1 = p_pt->f_hash(key_ipv4_1, 16, 0);
+	hash_ipv4_2 = p_pt->f_hash(key_ipv4_2, 16, 0);
+	hash_ipv4_3 = p_pt->f_hash(key_ipv4_3, 16, 0);
+
+	/* Write */
+	key_wr0[0] = key_ipv4_0[0];
+	key_wr1[0] = key_ipv4_1[0];
+	key_wr2[0] = key_ipv4_2[0];
+	key_wr3[0] = key_ipv4_3[0];
+
+	key_wr0[1] = key_ipv4_0[1];
+	key_wr1[1] = key_ipv4_1[1];
+	key_wr2[1] = key_ipv4_2[1];
+	key_wr3[1] = key_ipv4_3[1];
+
+	*hash0 = hash_ipv4_0;
+	*hash1 = hash_ipv4_1;
+	*hash2 = hash_ipv4_2;
+	*hash3 = hash_ipv4_3;
+}
+
+PIPELINE_PORT_IN_AH(port_in_ah_key_ipv4, pkt_work_key_ipv4, pkt4_work_key_ipv4);
+
+static inline void
+pkt_work_key_ipv6(
+	struct rte_mbuf *pkt,
+	void *arg)
+{
+	struct pipeline_passthrough *p_pt = arg;
+	uint32_t key_offset_rd = p_pt->key_offset_rd;
+	uint32_t key_offset_wr = p_pt->key_offset_wr;
+	uint32_t hash_offset = p_pt->hash_offset;
+
+	uint64_t *key_rd = RTE_MBUF_METADATA_UINT64_PTR(pkt, key_offset_rd);
+	uint64_t *key_wr = RTE_MBUF_METADATA_UINT64_PTR(pkt, key_offset_wr);
+	uint32_t *hash = RTE_MBUF_METADATA_UINT32_PTR(pkt, hash_offset);
+	uint64_t key_ipv6[8];
+	uint32_t hash_ipv6;
+
+	/* Read */
+	key_ipv6[0] = key_rd[0] & rte_bswap64(0x0000FF00FFFFFFFFLLU);
+	key_ipv6[1] = key_rd[1];
+	key_ipv6[2] = key_rd[2];
+	key_ipv6[3] = key_rd[3];
+	key_ipv6[4] = key_rd[4];
+	key_ipv6[5] = 0;
+	key_ipv6[6] = 0;
+	key_ipv6[7] = 0;
+
+	/* Compute */
+	hash_ipv6 = p_pt->f_hash(key_ipv6, 64, 0);
+
+	/* Write */
+	key_wr[0] = key_ipv6[0];
+	key_wr[1] = key_ipv6[1];
+	key_wr[2] = key_ipv6[2];
+	key_wr[3] = key_ipv6[3];
+	key_wr[4] = key_ipv6[4];
+	key_wr[5] = 0;
+	key_wr[6] = 0;
+	key_wr[7] = 0;
+	*hash = hash_ipv6;
+}
+
+static inline void
+pkt4_work_key_ipv6(
+	struct rte_mbuf **pkt,
+	void *arg)
+{
+	struct pipeline_passthrough *p_pt = arg;
+	uint32_t key_offset_rd = p_pt->key_offset_rd;
+	uint32_t key_offset_wr = p_pt->key_offset_wr;
+	uint32_t hash_offset = p_pt->hash_offset;
+
+	uint64_t *key_rd0 = RTE_MBUF_METADATA_UINT64_PTR(pkt[0], key_offset_rd);
+	uint64_t *key_wr0 = RTE_MBUF_METADATA_UINT64_PTR(pkt[0], key_offset_wr);
+	uint32_t *hash0 = RTE_MBUF_METADATA_UINT32_PTR(pkt[0], hash_offset);
+
+	uint64_t *key_rd1 = RTE_MBUF_METADATA_UINT64_PTR(pkt[1], key_offset_rd);
+	uint64_t *key_wr1 = RTE_MBUF_METADATA_UINT64_PTR(pkt[1], key_offset_wr);
+	uint32_t *hash1 = RTE_MBUF_METADATA_UINT32_PTR(pkt[1], hash_offset);
+
+	uint64_t *key_rd2 = RTE_MBUF_METADATA_UINT64_PTR(pkt[2], key_offset_rd);
+	uint64_t *key_wr2 = RTE_MBUF_METADATA_UINT64_PTR(pkt[2], key_offset_wr);
+	uint32_t *hash2 = RTE_MBUF_METADATA_UINT32_PTR(pkt[2], hash_offset);
+
+	uint64_t *key_rd3 = RTE_MBUF_METADATA_UINT64_PTR(pkt[3], key_offset_rd);
+	uint64_t *key_wr3 = RTE_MBUF_METADATA_UINT64_PTR(pkt[3], key_offset_wr);
+	uint32_t *hash3 = RTE_MBUF_METADATA_UINT32_PTR(pkt[3], hash_offset);
+
+	uint64_t key_ipv6_0[8];
+	uint64_t key_ipv6_1[8];
+	uint64_t key_ipv6_2[8];
+	uint64_t key_ipv6_3[8];
+
+	uint32_t hash_ipv6_0;
+	uint32_t hash_ipv6_1;
+	uint32_t hash_ipv6_2;
+	uint32_t hash_ipv6_3;
+
+	/* Read */
+	key_ipv6_0[0] = key_rd0[0] & rte_bswap64(0x0000FF00FFFFFFFFLLU);
+	key_ipv6_1[0] = key_rd1[0] & rte_bswap64(0x0000FF00FFFFFFFFLLU);
+	key_ipv6_2[0] = key_rd2[0] & rte_bswap64(0x0000FF00FFFFFFFFLLU);
+	key_ipv6_3[0] = key_rd3[0] & rte_bswap64(0x0000FF00FFFFFFFFLLU);
+
+	key_ipv6_0[1] = key_rd0[1];
+	key_ipv6_1[1] = key_rd1[1];
+	key_ipv6_2[1] = key_rd2[1];
+	key_ipv6_3[1] = key_rd3[1];
+
+	key_ipv6_0[2] = key_rd0[2];
+	key_ipv6_1[2] = key_rd1[2];
+	key_ipv6_2[2] = key_rd2[2];
+	key_ipv6_3[2] = key_rd3[2];
+
+	key_ipv6_0[3] = key_rd0[3];
+	key_ipv6_1[3] = key_rd1[3];
+	key_ipv6_2[3] = key_rd2[3];
+	key_ipv6_3[3] = key_rd3[3];
+
+	key_ipv6_0[4] = key_rd0[4];
+	key_ipv6_1[4] = key_rd1[4];
+	key_ipv6_2[4] = key_rd2[4];
+	key_ipv6_3[4] = key_rd3[4];
+
+	key_ipv6_0[5] = 0;
+	key_ipv6_1[5] = 0;
+	key_ipv6_2[5] = 0;
+	key_ipv6_3[5] = 0;
+
+	key_ipv6_0[6] = 0;
+	key_ipv6_1[6] = 0;
+	key_ipv6_2[6] = 0;
+	key_ipv6_3[6] = 0;
+
+	key_ipv6_0[7] = 0;
+	key_ipv6_1[7] = 0;
+	key_ipv6_2[7] = 0;
+	key_ipv6_3[7] = 0;
+
+	/* Compute */
+	hash_ipv6_0 = p_pt->f_hash(key_ipv6_0, 64, 0);
+	hash_ipv6_1 = p_pt->f_hash(key_ipv6_1, 64, 0);
+	hash_ipv6_2 = p_pt->f_hash(key_ipv6_2, 64, 0);
+	hash_ipv6_3 = p_pt->f_hash(key_ipv6_3, 64, 0);
+
+	/* Write */
+	key_wr0[0] = key_ipv6_0[0];
+	key_wr1[0] = key_ipv6_1[0];
+	key_wr2[0] = key_ipv6_2[0];
+	key_wr3[0] = key_ipv6_3[0];
+
+	key_wr0[1] = key_ipv6_0[1];
+	key_wr1[1] = key_ipv6_1[1];
+	key_wr2[1] = key_ipv6_2[1];
+	key_wr3[1] = key_ipv6_3[1];
+
+	key_wr0[2] = key_ipv6_0[2];
+	key_wr1[2] = key_ipv6_1[2];
+	key_wr2[2] = key_ipv6_2[2];
+	key_wr3[2] = key_ipv6_3[2];
+
+	key_wr0[3] = key_ipv6_0[3];
+	key_wr1[3] = key_ipv6_1[3];
+	key_wr2[3] = key_ipv6_2[3];
+	key_wr3[3] = key_ipv6_3[3];
+
+	key_wr0[4] = key_ipv6_0[4];
+	key_wr1[4] = key_ipv6_1[4];
+	key_wr2[4] = key_ipv6_2[4];
+	key_wr3[4] = key_ipv6_3[4];
+
+	key_wr0[5] = 0;
+	key_wr0[5] = 0;
+	key_wr0[5] = 0;
+	key_wr0[5] = 0;
+
+	key_wr0[6] = 0;
+	key_wr0[6] = 0;
+	key_wr0[6] = 0;
+	key_wr0[6] = 0;
+
+	key_wr0[7] = 0;
+	key_wr0[7] = 0;
+	key_wr0[7] = 0;
+	key_wr0[7] = 0;
+
+	*hash0 = hash_ipv6_0;
+	*hash1 = hash_ipv6_1;
+	*hash2 = hash_ipv6_2;
+	*hash3 = hash_ipv6_3;
+}
+
+PIPELINE_PORT_IN_AH(port_in_ah_key_ipv6, pkt_work_key_ipv6, pkt4_work_key_ipv6);
+
+static int
+pipeline_passthrough_parse_args(struct pipeline_passthrough *p,
+	struct pipeline_params *params)
+{
+	uint32_t key_type_present = 0;
+	uint32_t key_offset_rd_present = 0;
+	uint32_t key_offset_wr_present = 0;
+	uint32_t hash_offset_present = 0;
+	uint32_t i;
+
+	for (i = 0; i < params->n_args; i++) {
+		char *arg_name = params->args_name[i];
+		char *arg_value = params->args_value[i];
+
+		/* key_type */
+		if (strcmp(arg_name, "key_type") == 0) {
+			if (key_type_present)
+				return -1;
+			key_type_present = 1;
+
+			if ((strcmp(arg_value, "q-in-q") == 0) ||
+				(strcmp(arg_value, "qinq") == 0))
+				p->key_type = FLOW_KEY_QINQ;
+			else if (strcmp(arg_value, "ipv4_5tuple") == 0)
+				p->key_type = FLOW_KEY_IPV4_5TUPLE;
+			else if (strcmp(arg_value, "ipv6_5tuple") == 0)
+				p->key_type = FLOW_KEY_IPV6_5TUPLE;
+			else
+				return -1;
+
+			p->key_type_valid = 1;
+
+			continue;
+		}
+
+		/* key_offset_rd */
+		if (strcmp(arg_name, "key_offset_rd") == 0) {
+			if (key_offset_rd_present)
+				return -1;
+			key_offset_rd_present = 1;
+
+			p->key_offset_rd = atoi(arg_value);
+
+			continue;
+		}
+
+		/* key_offset_wr */
+		if (strcmp(arg_name, "key_offset_wr") == 0) {
+			if (key_offset_wr_present)
+				return -1;
+			key_offset_wr_present = 1;
+
+			p->key_offset_wr = atoi(arg_value);
+
+			continue;
+		}
+
+		/* hash_offset */
+		if (strcmp(arg_name, "hash_offset") == 0) {
+			if (hash_offset_present)
+				return -1;
+			hash_offset_present = 1;
+
+			p->hash_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* any other */
+		return -1;
+	}
+
+	/* Check that mandatory arguments are present */
+	if ((key_offset_rd_present != key_type_present) ||
+		(key_offset_wr_present != key_type_present) ||
+		(hash_offset_present != key_type_present))
+		return -1;
+
+	return 0;
+}
+
+static void*
+pipeline_passthrough_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct pipeline *p;
+	struct pipeline_passthrough *p_pt;
+	uint32_t size, i;
+
+	/* Check input arguments */
+	if ((params == NULL) ||
+		(params->n_ports_in == 0) ||
+		(params->n_ports_out == 0) ||
+		(params->n_ports_in < params->n_ports_out) ||
+		(params->n_ports_in % params->n_ports_out))
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct pipeline_passthrough));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	p_pt = (struct pipeline_passthrough *) p;
+	if (p == NULL)
+		return NULL;
+
+	strcpy(p->name, params->name);
+	p->log_level = params->log_level;
+
+	PLOG(p, HIGH, "Pass-through");
+
+	/* Parse arguments */
+	if (pipeline_passthrough_parse_args(p_pt, params))
+		return NULL;
+
+	if (p_pt->key_type_valid == 0) {
+		p_pt->f_hash = NULL;
+		p_pt->f_port_in_ah = NULL;
+	} else
+		switch (p_pt->key_type) {
+		case FLOW_KEY_QINQ:
+			p_pt->f_hash = hash_default_key8;
+			p_pt->f_port_in_ah = port_in_ah_key_qinq;
+			break;
+
+		case FLOW_KEY_IPV4_5TUPLE:
+			p_pt->f_hash = hash_default_key16;
+			p_pt->f_port_in_ah = port_in_ah_key_ipv4;
+			break;
+
+		case FLOW_KEY_IPV6_5TUPLE:
+			p_pt->f_hash = hash_default_key64;
+			p_pt->f_port_in_ah = port_in_ah_key_ipv6;
+			break;
+
+		default:
+			p_pt->f_hash = NULL;
+			p_pt->f_port_in_ah = NULL;
+		}
+
+	/* Pipeline */
+	{
+		struct rte_pipeline_params pipeline_params = {
+			.name = "PASS-THROUGH",
+			.socket_id = params->socket_id,
+			.offset_port_id = 0,
+		};
+
+		p->p = rte_pipeline_create(&pipeline_params);
+		if (p->p == NULL) {
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Input ports */
+	p->n_ports_in = params->n_ports_in;
+	for (i = 0; i < p->n_ports_in; i++) {
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = pipeline_port_in_params_get_ops(
+				&params->port_in[i]),
+			.arg_create = pipeline_port_in_params_convert(
+				&params->port_in[i]),
+			.f_action = p_pt->f_port_in_ah,
+			.arg_ah = p_pt,
+			.burst_size = params->port_in[i].burst_size,
+		};
+
+		int status = rte_pipeline_port_in_create(p->p,
+			&port_params,
+			&p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Output ports */
+	p->n_ports_out = params->n_ports_out;
+	for (i = 0; i < p->n_ports_out; i++) {
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = pipeline_port_out_params_get_ops(
+				&params->port_out[i]),
+			.arg_create = pipeline_port_out_params_convert(
+				&params->port_out[i]),
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		int status = rte_pipeline_port_out_create(p->p,
+			&port_params,
+			&p->port_out_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Tables */
+	p->n_tables = p->n_ports_in;
+	for (i = 0; i < p->n_ports_in; i++) {
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_stub_ops,
+			.arg_create = NULL,
+			.f_action_hit = NULL,
+			.f_action_miss = NULL,
+			.arg_ah = NULL,
+			.action_data_size = 0,
+		};
+
+		int status = rte_pipeline_table_create(p->p,
+			&table_params,
+			&p->table_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Connecting input ports to tables */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_connect_to_table(p->p,
+			p->port_in_id[i],
+			p->table_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Add entries to tables */
+	for (i = 0; i < p->n_ports_in; i++) {
+		struct rte_pipeline_table_entry default_entry = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[
+				i / (p->n_ports_in / p->n_ports_out)]},
+		};
+
+		struct rte_pipeline_table_entry *default_entry_ptr;
+
+		int status = rte_pipeline_table_default_entry_add(p->p,
+			p->table_id[i],
+			&default_entry,
+			&default_entry_ptr);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Enable input ports */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_enable(p->p,
+			p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p->p) < 0) {
+		rte_pipeline_free(p->p);
+		rte_free(p);
+		return NULL;
+	}
+
+	/* Message queues */
+	p->n_msgq = params->n_msgq;
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_in[i] = params->msgq_in[i];
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_out[i] = params->msgq_out[i];
+
+	/* Message handlers */
+	memcpy(p->handlers, handlers, sizeof(p->handlers));
+
+	return p;
+}
+
+static int
+pipeline_passthrough_free(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	rte_pipeline_free(p->p);
+	rte_free(p);
+	return 0;
+}
+
+static int
+pipeline_passthrough_timer(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	pipeline_msg_req_handle(p);
+	rte_pipeline_flush(p->p);
+
+	return 0;
+}
+
+static int
+pipeline_passthrough_track(void *pipeline, uint32_t port_in, uint32_t *port_out)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if ((p == NULL) ||
+		(port_in >= p->n_ports_in) ||
+		(port_out == NULL))
+		return -1;
+
+	*port_out = port_in / p->n_ports_in;
+	return 0;
+}
+
+struct pipeline_be_ops pipeline_passthrough_be_ops = {
+	.f_init = pipeline_passthrough_init,
+	.f_free = pipeline_passthrough_free,
+	.f_run = NULL,
+	.f_timer = pipeline_passthrough_timer,
+	.f_track = pipeline_passthrough_track,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_passthrough_be.h b/examples/ip_pipeline/pipeline/pipeline_passthrough_be.h
new file mode 100644
index 0000000..9d5e3db
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_passthrough_be.h
@@ -0,0 +1,41 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_PASSTHROUGH_BE_H__
+#define __INCLUDE_PIPELINE_PASSTHROUGH_BE_H__
+
+#include "pipeline_common_be.h"
+
+extern struct pipeline_be_ops pipeline_passthrough_be_ops;
+
+#endif
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 09/11] ip_pipeline: added new implementation of firewall pipeline
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (7 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 08/11] ip_pipeline: added new implementation of passthrough pipeline Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 10/11] ip_pipeline: added new implementation of routing pipeline Maciej Gajdzica
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Firewall pipeline implementation is split to two files.
pipeline_firewall.c file handles front-end functions (cli commands
parsing) pipeline_firewall_ops.c contains implementation of functions
done by pipeline (back-end).

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 examples/ip_pipeline/Makefile                      |    2 +
 examples/ip_pipeline/init.c                        |    2 +
 examples/ip_pipeline/pipeline/pipeline_firewall.c  | 1164 ++++++++++++++++----
 examples/ip_pipeline/pipeline/pipeline_firewall.h  |   63 ++
 .../ip_pipeline/pipeline/pipeline_firewall_be.c    |  740 +++++++++++++
 .../ip_pipeline/pipeline/pipeline_firewall_be.h    |  138 +++
 6 files changed, 1872 insertions(+), 237 deletions(-)
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_firewall_be.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 930dc61..382fee6 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -62,6 +62,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_master_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_master.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall_be.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall.c
 
 CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
 CFLAGS += -O3
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 0f3bf0b..6ba14da 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -47,6 +47,7 @@
 #include "pipeline_common_fe.h"
 #include "pipeline_master.h"
 #include "pipeline_passthrough.h"
+#include "pipeline_firewall.h"
 
 #define APP_NAME_SIZE	32
 
@@ -1287,6 +1288,7 @@ int app_init(struct app_params *app)
 	app_pipeline_common_cmd_push(app);
 	app_pipeline_type_register(app, &pipeline_master);
 	app_pipeline_type_register(app, &pipeline_passthrough);
+	app_pipeline_type_register(app, &pipeline_firewall);
 
 	app_init_pipelines(app);
 	app_init_threads(app);
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall.c b/examples/ip_pipeline/pipeline/pipeline_firewall.c
index b70260e..f6924ab 100644
--- a/examples/ip_pipeline/pipeline/pipeline_firewall.c
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -32,282 +32,972 @@
  */
 
 #include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
 
+#include <rte_common.h>
+#include <rte_hexdump.h>
 #include <rte_malloc.h>
-#include <rte_log.h>
-#include <rte_ethdev.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-#include <rte_byteorder.h>
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_etheraddr.h>
+#include <cmdline_socket.h>
+
+#include "app.h"
+#include "pipeline_common_fe.h"
+#include "pipeline_firewall.h"
+
+struct app_pipeline_firewall_rule {
+	struct pipeline_firewall_key key;
+	int32_t priority;
+	uint32_t port_id;
+	void *entry_ptr;
+
+	TAILQ_ENTRY(app_pipeline_firewall_rule) node;
+};
+
+struct app_pipeline_firewall {
+	/* parameters */
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+
+	/* rules */
+	TAILQ_HEAD(, app_pipeline_firewall_rule) rules;
+	uint32_t n_rules;
+	uint32_t default_rule_present;
+	uint32_t default_rule_port_id;
+	void *default_rule_entry_ptr;
+};
 
-#include <rte_port_ring.h>
-#include <rte_table_acl.h>
-#include <rte_pipeline.h>
+static void
+print_firewall_ipv4_rule(struct app_pipeline_firewall_rule *rule)
+{
+	printf("Prio = %" PRId32 " (SA = %" PRIu32 ".%" PRIu32
+		".%" PRIu32 ".%" PRIu32 "/%" PRIu32 ", "
+		"DA = %" PRIu32 ".%" PRIu32
+		".%"PRIu32 ".%" PRIu32 "/%" PRIu32 ", "
+		"SP = %" PRIu32 "-%" PRIu32 ", "
+		"DP = %" PRIu32 "-%" PRIu32 ", "
+		"Proto = %" PRIu32 " / 0x%" PRIx32 ") => "
+		"Port = %" PRIu32 " (entry ptr = %p)\n",
+
+		rule->priority,
+
+		(rule->key.key.ipv4_5tuple.src_ip >> 24) & 0xFF,
+		(rule->key.key.ipv4_5tuple.src_ip >> 16) & 0xFF,
+		(rule->key.key.ipv4_5tuple.src_ip >> 8) & 0xFF,
+		rule->key.key.ipv4_5tuple.src_ip & 0xFF,
+		rule->key.key.ipv4_5tuple.src_ip_mask,
+
+		(rule->key.key.ipv4_5tuple.dst_ip >> 24) & 0xFF,
+		(rule->key.key.ipv4_5tuple.dst_ip >> 16) & 0xFF,
+		(rule->key.key.ipv4_5tuple.dst_ip >> 8) & 0xFF,
+		rule->key.key.ipv4_5tuple.dst_ip & 0xFF,
+		rule->key.key.ipv4_5tuple.dst_ip_mask,
+
+		rule->key.key.ipv4_5tuple.src_port_from,
+		rule->key.key.ipv4_5tuple.src_port_to,
+
+		rule->key.key.ipv4_5tuple.dst_port_from,
+		rule->key.key.ipv4_5tuple.dst_port_to,
+
+		rule->key.key.ipv4_5tuple.proto,
+		rule->key.key.ipv4_5tuple.proto_mask,
+
+		rule->port_id,
+		rule->entry_ptr);
+}
 
-#include "main.h"
+static struct app_pipeline_firewall_rule *
+app_pipeline_firewall_rule_find(struct app_pipeline_firewall *p,
+	struct pipeline_firewall_key *key)
+{
+	struct app_pipeline_firewall_rule *r;
 
-struct app_core_firewall_message_handle_params {
-	struct rte_ring *ring_req;
-	struct rte_ring *ring_resp;
+	TAILQ_FOREACH(r, &p->rules, node)
+		if (memcmp(key,
+			&r->key,
+			sizeof(struct pipeline_firewall_key)) == 0)
+			return r;
 
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
-};
+	return NULL;
+}
 
 static void
-app_message_handle(struct app_core_firewall_message_handle_params *params);
-
-enum {
-	PROTO_FIELD_IPV4,
-	SRC_FIELD_IPV4,
-	DST_FIELD_IPV4,
-	SRCP_FIELD_IPV4,
-	DSTP_FIELD_IPV4,
-	NUM_FIELDS_IPV4
-};
+app_pipeline_firewall_ls(
+	struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct app_pipeline_firewall *p;
+	struct app_pipeline_firewall_rule *rule;
+	uint32_t n_rules;
+	int priority;
 
-struct rte_acl_field_def ipv4_field_formats[NUM_FIELDS_IPV4] = {
-	{
-		.type = RTE_ACL_FIELD_TYPE_BITMASK,
-		.size = sizeof(uint8_t),
-		.field_index = PROTO_FIELD_IPV4,
-		.input_index = PROTO_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) +
-			offsetof(struct ipv4_hdr, next_proto_id),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = SRC_FIELD_IPV4,
-		.input_index = SRC_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) +
-			offsetof(struct ipv4_hdr, src_addr),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = DST_FIELD_IPV4,
-		.input_index = DST_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) +
-			offsetof(struct ipv4_hdr, dst_addr),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = SRCP_FIELD_IPV4,
-		.input_index = SRCP_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr),
-	},
+	/* Check input arguments */
+	if (app == NULL)
+		return;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return;
+
+	n_rules = p->n_rules;
+	for (priority = 0; n_rules; priority++)
+		TAILQ_FOREACH(rule, &p->rules, node)
+			if (rule->priority == priority) {
+				print_firewall_ipv4_rule(rule);
+				n_rules--;
+			}
+
+	if (p->default_rule_present)
+		printf("Default rule: port %" PRIu32 " (entry ptr = %p)\n",
+			p->default_rule_port_id,
+			p->default_rule_entry_ptr);
+	else
+		printf("Default rule: DROP\n");
+
+	printf("\n");
+}
+
+static void*
+app_pipeline_firewall_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct app_pipeline_firewall *p;
+	uint32_t size;
+
+	/* Check input arguments */
+	if ((params == NULL) ||
+		(params->n_ports_in == 0) ||
+		(params->n_ports_out == 0))
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct app_pipeline_firewall));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		return NULL;
+
+	/* Initialization */
+	p->n_ports_in = params->n_ports_in;
+	p->n_ports_out = params->n_ports_out;
+
+	TAILQ_INIT(&p->rules);
+	p->n_rules = 0;
+	p->default_rule_present = 0;
+	p->default_rule_port_id = 0;
+	p->default_rule_entry_ptr = NULL;
+
+	return (void *) p;
+}
+
+static int
+app_pipeline_firewall_free(void *pipeline)
+{
+	struct app_pipeline_firewall *p = pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	while (!TAILQ_EMPTY(&p->rules)) {
+		struct app_pipeline_firewall_rule *rule;
+
+		rule = TAILQ_FIRST(&p->rules);
+		TAILQ_REMOVE(&p->rules, rule, node);
+		rte_free(rule);
+	}
+
+	rte_free(p);
+	return 0;
+}
+
+static int
+app_pipeline_firewall_key_check_and_normalize(struct pipeline_firewall_key *key)
+{
+	switch (key->type) {
+	case PIPELINE_FIREWALL_IPV4_5TUPLE:
 	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = DSTP_FIELD_IPV4,
-		.input_index = SRCP_FIELD_IPV4,
-		.offset = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) +
-			sizeof(uint16_t),
-	},
-};
+		uint32_t src_ip_depth = key->key.ipv4_5tuple.src_ip_mask;
+		uint32_t dst_ip_depth = key->key.ipv4_5tuple.dst_ip_mask;
+		uint16_t src_port_from = key->key.ipv4_5tuple.src_port_from;
+		uint16_t src_port_to = key->key.ipv4_5tuple.src_port_to;
+		uint16_t dst_port_from = key->key.ipv4_5tuple.dst_port_from;
+		uint16_t dst_port_to = key->key.ipv4_5tuple.dst_port_to;
 
-void
-app_main_loop_pipeline_firewall(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
+		uint32_t src_ip_netmask = 0;
+		uint32_t dst_ip_netmask = 0;
 
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id;
-	uint32_t i;
+		if ((src_ip_depth > 32) ||
+			(dst_ip_depth > 32) ||
+			(src_port_from > src_port_to) ||
+			(dst_port_from > dst_port_to))
+			return -1;
 
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-	struct app_core_firewall_message_handle_params mh_params;
+		if (src_ip_depth)
+			src_ip_netmask = (~0) << (32 - src_ip_depth);
 
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_FW))
-		rte_panic("Core %u misconfiguration\n", core_id);
+		if (dst_ip_depth)
+			dst_ip_netmask = ((~0) << (32 - dst_ip_depth));
 
-	RTE_LOG(INFO, USER1, "Core %u is doing firewall\n", core_id);
+		key->key.ipv4_5tuple.src_ip &= src_ip_netmask;
+		key->key.ipv4_5tuple.dst_ip &= dst_ip_netmask;
+
+		return 0;
+	}
+
+	default:
+		return -1;
+	}
+}
 
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
+int
+app_pipeline_firewall_add_rule(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_firewall_key *key,
+	uint32_t priority,
+	uint32_t port_id)
+{
+	struct app_pipeline_firewall *p;
+	struct app_pipeline_firewall_rule *rule;
+	struct pipeline_firewall_add_msg_req *req;
+	struct pipeline_firewall_add_msg_rsp *rsp;
+	int new_rule;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL) ||
+		(key->type != PIPELINE_FIREWALL_IPV4_5TUPLE))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
 	if (p == NULL)
-		rte_panic("Unable to configure the pipeline\n");
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("Unable to configure input port for "
-				"ring %d\n", i);
+		return -1;
+
+	if (port_id >= p->n_ports_out)
+		return -1;
+
+	if (app_pipeline_firewall_key_check_and_normalize(key) != 0)
+		return -1;
+
+	/* Find existing rule or allocate new rule */
+	rule = app_pipeline_firewall_rule_find(p, key);
+	new_rule = (rule == NULL);
+	if (rule == NULL) {
+		rule = rte_malloc(NULL, sizeof(*rule), RTE_CACHE_LINE_SIZE);
+
+		if (rule == NULL)
+			return -1;
 	}
 
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("Unable to configure output port for "
-				"ring %d\n", i);
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL) {
+		if (new_rule)
+			rte_free(rule);
+		return -1;
 	}
 
-	/* Table configuration */
-	{
-		struct rte_table_acl_params table_acl_params = {
-			.name = "test", /* unique identifier for acl contexts */
-			.n_rules = app.max_firewall_rules,
-			.n_rule_fields = DIM(ipv4_field_formats),
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_acl_ops,
-			.arg_create = &table_acl_params,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		memcpy(table_acl_params.field_format, ipv4_field_formats,
-			sizeof(ipv4_field_formats));
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id))
-			rte_panic("Unable to configure the ACL table\n");
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FIREWALL_MSG_REQ_ADD;
+	memcpy(&req->key, key, sizeof(*key));
+	req->priority = priority;
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL) {
+		if (new_rule)
+			rte_free(rule);
+		return -1;
+	}
+
+	/* Read response and write rule */
+	if (rsp->status ||
+		(rsp->entry_ptr == NULL) ||
+		((new_rule == 0) && (rsp->key_found == 0)) ||
+		((new_rule == 1) && (rsp->key_found == 1))) {
+		app_msg_free(app, rsp);
+		if (new_rule)
+			rte_free(rule);
+		return -1;
+	}
+
+	memcpy(&rule->key, key, sizeof(*key));
+	rule->priority = priority;
+	rule->port_id = port_id;
+	rule->entry_ptr = rsp->entry_ptr;
+
+	/* Commit rule */
+	if (new_rule) {
+		TAILQ_INSERT_TAIL(&p->rules, rule, node);
+		p->n_rules++;
 	}
 
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id))
-			rte_panic("Unable to connect input port %u to "
-				"table %u\n", port_in_id[i],  table_id);
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("Pipeline consistency check failed\n");
-
-	/* Message handling */
-	mh_params.ring_req = app_get_ring_req(
-		app_get_first_core_id(APP_CORE_FW));
-	mh_params.ring_resp = app_get_ring_resp(
-		app_get_first_core_id(APP_CORE_FW));
-	mh_params.p = p;
-	mh_params.port_out_id = port_out_id;
-	mh_params.table_id = table_id;
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0) {
-			rte_pipeline_flush(p);
-			app_message_handle(&mh_params);
-		}
+	print_firewall_ipv4_rule(rule);
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+int
+app_pipeline_firewall_delete_rule(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_firewall_key *key)
+{
+	struct app_pipeline_firewall *p;
+	struct app_pipeline_firewall_rule *rule;
+	struct pipeline_firewall_del_msg_req *req;
+	struct pipeline_firewall_del_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL) ||
+		(key->type != PIPELINE_FIREWALL_IPV4_5TUPLE))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (app_pipeline_firewall_key_check_and_normalize(key) != 0)
+		return -1;
+
+	/* Find rule */
+	rule = app_pipeline_firewall_rule_find(p, key);
+	if (rule == NULL)
+		return 0;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FIREWALL_MSG_REQ_DEL;
+	memcpy(&req->key, key, sizeof(*key));
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status || !rsp->key_found) {
+		app_msg_free(app, rsp);
+		return -1;
 	}
+
+	/* Remove rule */
+	TAILQ_REMOVE(&p->rules, rule, node);
+	p->n_rules--;
+	rte_free(rule);
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
 }
 
-void
-app_message_handle(struct app_core_firewall_message_handle_params *params)
+int
+app_pipeline_firewall_add_default_rule(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id)
 {
-	struct rte_ring *ring_req = params->ring_req;
-	struct rte_ring *ring_resp;
-	struct rte_mbuf *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
-	int result;
-
-	/* Read request message */
-	result = rte_ring_sc_dequeue(ring_req, (void **) &msg);
-	if (result != 0)
-		return;
+	struct app_pipeline_firewall *p;
+	struct pipeline_firewall_add_default_msg_req *req;
+	struct pipeline_firewall_add_default_msg_rsp *rsp;
 
-	ring_resp = params->ring_resp;
-	p = params->p;
-	port_out_id = params->port_out_id;
-	table_id = params->table_id;
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
 
-	/* Handle request */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data(msg);
-	switch (req->type) {
-	case APP_MSG_REQ_PING:
-	{
-		result = 0;
-		break;
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (port_id >= p->n_ports_out)
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FIREWALL_MSG_REQ_ADD_DEFAULT;
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response and write rule */
+	if (rsp->status || (rsp->entry_ptr == NULL)) {
+		app_msg_free(app, rsp);
+		return -1;
 	}
 
-	case APP_MSG_REQ_FW_ADD:
-	{
-		struct rte_pipeline_table_entry entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[req->firewall_add.port]},
-		};
+	p->default_rule_port_id = port_id;
+	p->default_rule_entry_ptr = rsp->entry_ptr;
 
-		struct rte_pipeline_table_entry *entry_ptr;
+	/* Commit rule */
+	p->default_rule_present = 1;
 
-		int key_found;
+	/* Free response */
+	app_msg_free(app, rsp);
 
-		result = rte_pipeline_table_entry_add(p, table_id,
-			&req->firewall_add.add_params, &entry, &key_found,
-			&entry_ptr);
-		break;
+	return 0;
+}
+
+int
+app_pipeline_firewall_delete_default_rule(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct app_pipeline_firewall *p;
+	struct pipeline_firewall_del_default_msg_req *req;
+	struct pipeline_firewall_del_default_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FIREWALL_MSG_REQ_DEL_DEFAULT;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response and write rule */
+	if (rsp->status) {
+		app_msg_free(app, rsp);
+		return -1;
 	}
 
-	case APP_MSG_REQ_FW_DEL:
-	{
-		int key_found;
+	/* Commit rule */
+	p->default_rule_present = 0;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+/*
+ * p firewall add ipv4
+ */
+
+struct cmd_firewall_add_ipv4_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t firewall_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t ipv4_string;
+	int32_t priority;
+	cmdline_ipaddr_t src_ip;
+	uint32_t src_ip_mask;
+	cmdline_ipaddr_t dst_ip;
+	uint32_t dst_ip_mask;
+	uint16_t src_port_from;
+	uint16_t src_port_to;
+	uint16_t dst_port_from;
+	uint16_t dst_port_to;
+	uint8_t proto;
+	uint8_t proto_mask;
+	uint8_t port_id;
+};
+
+static void
+cmd_firewall_add_ipv4_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_firewall_add_ipv4_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_firewall_key key;
+	int status;
+
+	key.type = PIPELINE_FIREWALL_IPV4_5TUPLE;
+	key.key.ipv4_5tuple.src_ip = rte_bswap32(
+		(uint32_t) params->src_ip.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.src_ip_mask = params->src_ip_mask;
+	key.key.ipv4_5tuple.dst_ip = rte_bswap32(
+		(uint32_t) params->dst_ip.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.dst_ip_mask = params->dst_ip_mask;
+	key.key.ipv4_5tuple.src_port_from = params->src_port_from;
+	key.key.ipv4_5tuple.src_port_to = params->src_port_to;
+	key.key.ipv4_5tuple.dst_port_from = params->dst_port_from;
+	key.key.ipv4_5tuple.dst_port_to = params->dst_port_to;
+	key.key.ipv4_5tuple.proto = params->proto;
+	key.key.ipv4_5tuple.proto_mask = params->proto_mask;
+
+	status = app_pipeline_firewall_add_rule(app,
+		params->pipeline_id,
+		&key,
+		params->priority,
+		params->port_id);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+cmdline_parse_token_string_t cmd_firewall_add_ipv4_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_ipv4_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_firewall_add_ipv4_firewall_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		firewall_string, "firewall");
+
+cmdline_parse_token_string_t cmd_firewall_add_ipv4_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		add_string, "add");
+
+cmdline_parse_token_string_t cmd_firewall_add_ipv4_ipv4_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		ipv4_string, "ipv4");
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_priority =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result, priority,
+		INT32);
+
+cmdline_parse_token_ipaddr_t cmd_firewall_add_ipv4_src_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_firewall_add_ipv4_result, src_ip);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_src_ip_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result, src_ip_mask,
+		UINT32);
+
+cmdline_parse_token_ipaddr_t cmd_firewall_add_ipv4_dst_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_firewall_add_ipv4_result, dst_ip);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_dst_ip_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result, dst_ip_mask,
+		UINT32);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_src_port_from =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		src_port_from, UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_src_port_to =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		src_port_to, UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_dst_port_from =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		dst_port_from, UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_dst_port_to =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		dst_port_to, UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_proto =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		proto, UINT8);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_proto_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		proto_mask, UINT8);
+
+cmdline_parse_token_num_t cmd_firewall_add_ipv4_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_ipv4_result,
+		port_id, UINT8);
+
+cmdline_parse_inst_t cmd_firewall_add_ipv4 = {
+	.f = cmd_firewall_add_ipv4_parsed,
+	.data = NULL,
+	.help_str = "Firewall rule add",
+	.tokens = {
+		(void *) &cmd_firewall_add_ipv4_p_string,
+		(void *) &cmd_firewall_add_ipv4_pipeline_id,
+		(void *) &cmd_firewall_add_ipv4_firewall_string,
+		(void *) &cmd_firewall_add_ipv4_add_string,
+		(void *) &cmd_firewall_add_ipv4_ipv4_string,
+		(void *) &cmd_firewall_add_ipv4_priority,
+		(void *) &cmd_firewall_add_ipv4_src_ip,
+		(void *) &cmd_firewall_add_ipv4_src_ip_mask,
+		(void *) &cmd_firewall_add_ipv4_dst_ip,
+		(void *) &cmd_firewall_add_ipv4_dst_ip_mask,
+		(void *) &cmd_firewall_add_ipv4_src_port_from,
+		(void *) &cmd_firewall_add_ipv4_src_port_to,
+		(void *) &cmd_firewall_add_ipv4_dst_port_from,
+		(void *) &cmd_firewall_add_ipv4_dst_port_to,
+		(void *) &cmd_firewall_add_ipv4_proto,
+		(void *) &cmd_firewall_add_ipv4_proto_mask,
+		(void *) &cmd_firewall_add_ipv4_port_id,
+		NULL,
+	},
+};
+
+/*
+ * p firewall del ipv4
+ */
+
+struct cmd_firewall_del_ipv4_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t firewall_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t ipv4_string;
+	cmdline_ipaddr_t src_ip;
+	uint32_t src_ip_mask;
+	cmdline_ipaddr_t dst_ip;
+	uint32_t dst_ip_mask;
+	uint16_t src_port_from;
+	uint16_t src_port_to;
+	uint16_t dst_port_from;
+	uint16_t dst_port_to;
+	uint8_t proto;
+	uint8_t proto_mask;
+};
 
-		result = rte_pipeline_table_entry_delete(p, table_id,
-			&req->firewall_del.delete_params, &key_found, NULL);
-		break;
+static void
+cmd_firewall_del_ipv4_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_firewall_del_ipv4_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_firewall_key key;
+	int status;
+
+	key.type = PIPELINE_FIREWALL_IPV4_5TUPLE;
+	key.key.ipv4_5tuple.src_ip = rte_bswap32(
+		(uint32_t) params->src_ip.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.src_ip_mask = params->src_ip_mask;
+	key.key.ipv4_5tuple.dst_ip = rte_bswap32(
+		(uint32_t) params->dst_ip.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.dst_ip_mask = params->dst_ip_mask;
+	key.key.ipv4_5tuple.src_port_from = params->src_port_from;
+	key.key.ipv4_5tuple.src_port_to = params->src_port_to;
+	key.key.ipv4_5tuple.dst_port_from = params->dst_port_from;
+	key.key.ipv4_5tuple.dst_port_to = params->dst_port_to;
+	key.key.ipv4_5tuple.proto = params->proto;
+	key.key.ipv4_5tuple.proto_mask = params->proto_mask;
+
+	status = app_pipeline_firewall_delete_rule(app,
+		params->pipeline_id,
+		&key);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
 
-	default:
-		rte_panic("FW unrecognized message type (%u)\n", req->type);
+cmdline_parse_token_string_t cmd_firewall_del_ipv4_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_ipv4_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_firewall_del_ipv4_firewall_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_ipv4_result,
+		firewall_string, "firewall");
+
+cmdline_parse_token_string_t cmd_firewall_del_ipv4_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_ipv4_result,
+		del_string, "del");
+
+cmdline_parse_token_string_t cmd_firewall_del_ipv4_ipv4_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_ipv4_result,
+		ipv4_string, "ipv4");
+
+cmdline_parse_token_ipaddr_t cmd_firewall_del_ipv4_src_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_firewall_del_ipv4_result, src_ip);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_src_ip_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result, src_ip_mask,
+		UINT32);
+
+cmdline_parse_token_ipaddr_t cmd_firewall_del_ipv4_dst_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_firewall_del_ipv4_result, dst_ip);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_dst_ip_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result, dst_ip_mask,
+		UINT32);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_src_port_from =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result,
+		src_port_from, UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_src_port_to =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result, src_port_to,
+		UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_dst_port_from =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result,
+		dst_port_from, UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_dst_port_to =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result,
+		dst_port_to, UINT16);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_proto =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result,
+		proto, UINT8);
+
+cmdline_parse_token_num_t cmd_firewall_del_ipv4_proto_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_ipv4_result, proto_mask,
+		UINT8);
+
+cmdline_parse_inst_t cmd_firewall_del_ipv4 = {
+	.f = cmd_firewall_del_ipv4_parsed,
+	.data = NULL,
+	.help_str = "Firewall rule delete",
+	.tokens = {
+		(void *) &cmd_firewall_del_ipv4_p_string,
+		(void *) &cmd_firewall_del_ipv4_pipeline_id,
+		(void *) &cmd_firewall_del_ipv4_firewall_string,
+		(void *) &cmd_firewall_del_ipv4_del_string,
+		(void *) &cmd_firewall_del_ipv4_ipv4_string,
+		(void *) &cmd_firewall_del_ipv4_src_ip,
+		(void *) &cmd_firewall_del_ipv4_src_ip_mask,
+		(void *) &cmd_firewall_del_ipv4_dst_ip,
+		(void *) &cmd_firewall_del_ipv4_dst_ip_mask,
+		(void *) &cmd_firewall_del_ipv4_src_port_from,
+		(void *) &cmd_firewall_del_ipv4_src_port_to,
+		(void *) &cmd_firewall_del_ipv4_dst_port_from,
+		(void *) &cmd_firewall_del_ipv4_dst_port_to,
+		(void *) &cmd_firewall_del_ipv4_proto,
+		(void *) &cmd_firewall_del_ipv4_proto_mask,
+		NULL,
+	},
+};
+
+/*
+ * p firewall add default
+ */
+struct cmd_firewall_add_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t firewall_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t default_string;
+	uint8_t port_id;
+};
+
+static void
+cmd_firewall_add_default_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_firewall_add_default_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_firewall_add_default_rule(app,
+		params->pipeline_id,
+		params->port_id);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+cmdline_parse_token_string_t cmd_firewall_add_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_default_result,
+		p_string, "p");
+
+cmdline_parse_token_num_t cmd_firewall_add_default_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_default_result,
+		pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_firewall_add_default_firewall_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_default_result,
+	firewall_string, "firewall");
+
+cmdline_parse_token_string_t cmd_firewall_add_default_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_default_result,
+	add_string, "add");
+
+cmdline_parse_token_string_t cmd_firewall_add_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_add_default_result,
+		default_string, "default");
+
+cmdline_parse_token_num_t cmd_firewall_add_default_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_add_default_result, port_id,
+		UINT8);
+
+cmdline_parse_inst_t cmd_firewall_add_default = {
+	.f = cmd_firewall_add_default_parsed,
+	.data = NULL,
+	.help_str = "Firewall default rule add",
+	.tokens = {
+		(void *) &cmd_firewall_add_default_p_string,
+		(void *) &cmd_firewall_add_default_pipeline_id,
+		(void *) &cmd_firewall_add_default_firewall_string,
+		(void *) &cmd_firewall_add_default_add_string,
+		(void *) &cmd_firewall_add_default_default_string,
+		(void *) &cmd_firewall_add_default_port_id,
+		NULL,
+	},
+};
+
+/*
+ * p firewall del default
+ */
+struct cmd_firewall_del_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t firewall_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t default_string;
+};
+
+static void
+cmd_firewall_del_default_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_firewall_del_default_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_firewall_delete_default_rule(app,
+		params->pipeline_id);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
+
+cmdline_parse_token_string_t cmd_firewall_del_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_default_result,
+		p_string, "p");
+
+cmdline_parse_token_num_t cmd_firewall_del_default_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_del_default_result,
+		pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_firewall_del_default_firewall_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_default_result,
+	firewall_string, "firewall");
+
+cmdline_parse_token_string_t cmd_firewall_del_default_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_default_result,
+		del_string, "del");
+
+cmdline_parse_token_string_t cmd_firewall_del_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_del_default_result,
+		default_string, "default");
+
+cmdline_parse_inst_t cmd_firewall_del_default = {
+	.f = cmd_firewall_del_default_parsed,
+	.data = NULL,
+	.help_str = "Firewall default rule delete",
+	.tokens = {
+		(void *) &cmd_firewall_del_default_p_string,
+		(void *) &cmd_firewall_del_default_pipeline_id,
+		(void *) &cmd_firewall_del_default_firewall_string,
+		(void *) &cmd_firewall_del_default_del_string,
+		(void *) &cmd_firewall_del_default_default_string,
+		NULL,
+	},
+};
 
-	/* Fill in response message */
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data(msg);
-	resp->result = result;
+/*
+ * p firewall ls
+ */
+
+struct cmd_firewall_ls_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t firewall_string;
+	cmdline_fixed_string_t ls_string;
+};
+
+static void
+cmd_firewall_ls_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_firewall_ls_result *params = parsed_result;
+	struct app_params *app = data;
 
-	/* Send response */
-	do {
-		result = rte_ring_sp_enqueue(ring_resp, (void *) msg);
-	} while (result == -ENOBUFS);
+	app_pipeline_firewall_ls(app, params->pipeline_id);
 }
+
+cmdline_parse_token_string_t cmd_firewall_ls_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_ls_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_firewall_ls_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_firewall_ls_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_firewall_ls_firewall_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_ls_result,
+	firewall_string, "firewall");
+
+cmdline_parse_token_string_t cmd_firewall_ls_ls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_firewall_ls_result, ls_string,
+	"ls");
+
+cmdline_parse_inst_t cmd_firewall_ls = {
+	.f = cmd_firewall_ls_parsed,
+	.data = NULL,
+	.help_str = "Firewall rule list",
+	.tokens = {
+		(void *) &cmd_firewall_ls_p_string,
+		(void *) &cmd_firewall_ls_pipeline_id,
+		(void *) &cmd_firewall_ls_firewall_string,
+		(void *) &cmd_firewall_ls_ls_string,
+		NULL,
+	},
+};
+
+static cmdline_parse_ctx_t pipeline_cmds[] = {
+	(cmdline_parse_inst_t *) &cmd_firewall_add_ipv4,
+	(cmdline_parse_inst_t *) &cmd_firewall_del_ipv4,
+	(cmdline_parse_inst_t *) &cmd_firewall_add_default,
+	(cmdline_parse_inst_t *) &cmd_firewall_del_default,
+	(cmdline_parse_inst_t *) &cmd_firewall_ls,
+	NULL,
+};
+
+static struct pipeline_fe_ops pipeline_firewall_fe_ops = {
+	.f_init = app_pipeline_firewall_init,
+	.f_free = app_pipeline_firewall_free,
+	.cmds = pipeline_cmds,
+};
+
+struct pipeline_type pipeline_firewall = {
+	.name = "FIREWALL",
+	.be_ops = &pipeline_firewall_be_ops,
+	.fe_ops = &pipeline_firewall_fe_ops,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall.h b/examples/ip_pipeline/pipeline/pipeline_firewall.h
new file mode 100644
index 0000000..82e905d
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall.h
@@ -0,0 +1,63 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_FIREWALL_H__
+#define __INCLUDE_PIPELINE_FIREWALL_H__
+
+#include "pipeline.h"
+#include "pipeline_firewall_be.h"
+
+int
+app_pipeline_firewall_add_rule(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_firewall_key *key,
+	uint32_t priority,
+	uint32_t port_id);
+
+int
+app_pipeline_firewall_delete_rule(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_firewall_key *key);
+
+int
+app_pipeline_firewall_add_default_rule(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id);
+
+int
+app_pipeline_firewall_delete_default_rule(struct app_params *app,
+	uint32_t pipeline_id);
+
+extern struct pipeline_type pipeline_firewall;
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall_be.c b/examples/ip_pipeline/pipeline/pipeline_firewall_be.c
new file mode 100644
index 0000000..b6f305f
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall_be.c
@@ -0,0 +1,740 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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 <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+#include <rte_byteorder.h>
+#include <rte_table_acl.h>
+
+#include "pipeline_firewall_be.h"
+
+struct pipeline_firewall {
+	struct pipeline p;
+	pipeline_msg_req_handler custom_handlers[PIPELINE_FIREWALL_MSG_REQS];
+
+	uint32_t n_rules;
+	uint32_t n_rule_fields;
+	struct rte_acl_field_def *field_format;
+	uint32_t field_format_size;
+} __rte_cache_aligned;
+
+static void *
+pipeline_firewall_msg_req_custom_handler(struct pipeline *p, void *msg);
+
+static pipeline_msg_req_handler handlers[] = {
+	[PIPELINE_MSG_REQ_PING] =
+		pipeline_msg_req_ping_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_IN] =
+		pipeline_msg_req_stats_port_in_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_OUT] =
+		pipeline_msg_req_stats_port_out_handler,
+	[PIPELINE_MSG_REQ_STATS_TABLE] =
+		pipeline_msg_req_stats_table_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_ENABLE] =
+		pipeline_msg_req_port_in_enable_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_DISABLE] =
+		pipeline_msg_req_port_in_disable_handler,
+	[PIPELINE_MSG_REQ_CUSTOM] =
+		pipeline_firewall_msg_req_custom_handler,
+};
+
+static void *
+pipeline_firewall_msg_req_add_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_firewall_msg_req_del_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_firewall_msg_req_add_default_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_firewall_msg_req_del_default_handler(struct pipeline *p, void *msg);
+
+static pipeline_msg_req_handler custom_handlers[] = {
+	[PIPELINE_FIREWALL_MSG_REQ_ADD] =
+		pipeline_firewall_msg_req_add_handler,
+	[PIPELINE_FIREWALL_MSG_REQ_DEL] =
+		pipeline_firewall_msg_req_del_handler,
+	[PIPELINE_FIREWALL_MSG_REQ_ADD_DEFAULT] =
+		pipeline_firewall_msg_req_add_default_handler,
+	[PIPELINE_FIREWALL_MSG_REQ_DEL_DEFAULT] =
+		pipeline_firewall_msg_req_del_default_handler,
+};
+
+/*
+ * Firewall table
+ */
+struct firewall_table_entry {
+	struct rte_pipeline_table_entry head;
+};
+
+static struct rte_acl_field_def field_format_ipv4[] = {
+	/* Protocol */
+	[0] = {
+		.type = RTE_ACL_FIELD_TYPE_BITMASK,
+		.size = sizeof(uint8_t),
+		.field_index = 0,
+		.input_index = 0,
+		.offset = sizeof(struct ether_hdr) +
+			offsetof(struct ipv4_hdr, next_proto_id),
+	},
+
+	/* Source IP address (IPv4) */
+	[1] = {
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = 1,
+		.input_index = 1,
+		.offset = sizeof(struct ether_hdr) +
+			offsetof(struct ipv4_hdr, src_addr),
+	},
+
+	/* Destination IP address (IPv4) */
+	[2] = {
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = 2,
+		.input_index = 2,
+		.offset = sizeof(struct ether_hdr) +
+			offsetof(struct ipv4_hdr, dst_addr),
+	},
+
+	/* Source Port */
+	[3] = {
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = 3,
+		.input_index = 3,
+		.offset = sizeof(struct ether_hdr) +
+			sizeof(struct ipv4_hdr) +
+			offsetof(struct tcp_hdr, src_port),
+	},
+
+	/* Destination Port */
+	[4] = {
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = 4,
+		.input_index = 4,
+		.offset = sizeof(struct ether_hdr) +
+			sizeof(struct ipv4_hdr) +
+			offsetof(struct tcp_hdr, dst_port),
+	},
+};
+
+#define SIZEOF_VLAN_HDR                          4
+
+static struct rte_acl_field_def field_format_vlan_ipv4[] = {
+	/* Protocol */
+	[0] = {
+		.type = RTE_ACL_FIELD_TYPE_BITMASK,
+		.size = sizeof(uint8_t),
+		.field_index = 0,
+		.input_index = 0,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_VLAN_HDR +
+			offsetof(struct ipv4_hdr, next_proto_id),
+	},
+
+	/* Source IP address (IPv4) */
+	[1] = {
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = 1,
+		.input_index = 1,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_VLAN_HDR +
+			offsetof(struct ipv4_hdr, src_addr),
+	},
+
+	/* Destination IP address (IPv4) */
+	[2] = {
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = 2,
+		.input_index = 2,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_VLAN_HDR +
+			offsetof(struct ipv4_hdr, dst_addr),
+	},
+
+	/* Source Port */
+	[3] = {
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = 3,
+		.input_index = 3,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_VLAN_HDR +
+			sizeof(struct ipv4_hdr) +
+			offsetof(struct tcp_hdr, src_port),
+	},
+
+	/* Destination Port */
+	[4] = {
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = 4,
+		.input_index = 4,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_VLAN_HDR +
+			sizeof(struct ipv4_hdr) +
+			offsetof(struct tcp_hdr, dst_port),
+	},
+};
+
+#define SIZEOF_QINQ_HEADER                       8
+
+static struct rte_acl_field_def field_format_qinq_ipv4[] = {
+	/* Protocol */
+	[0] = {
+		.type = RTE_ACL_FIELD_TYPE_BITMASK,
+		.size = sizeof(uint8_t),
+		.field_index = 0,
+		.input_index = 0,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_QINQ_HEADER +
+			offsetof(struct ipv4_hdr, next_proto_id),
+	},
+
+	/* Source IP address (IPv4) */
+	[1] = {
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = 1,
+		.input_index = 1,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_QINQ_HEADER +
+			offsetof(struct ipv4_hdr, src_addr),
+	},
+
+	/* Destination IP address (IPv4) */
+	[2] = {
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = 2,
+		.input_index = 2,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_QINQ_HEADER +
+			offsetof(struct ipv4_hdr, dst_addr),
+	},
+
+	/* Source Port */
+	[3] = {
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = 3,
+		.input_index = 3,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_QINQ_HEADER +
+			sizeof(struct ipv4_hdr) +
+			offsetof(struct tcp_hdr, src_port),
+	},
+
+	/* Destination Port */
+	[4] = {
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = 4,
+		.input_index = 4,
+		.offset = sizeof(struct ether_hdr) +
+			SIZEOF_QINQ_HEADER +
+			sizeof(struct ipv4_hdr) +
+			offsetof(struct tcp_hdr, dst_port),
+	},
+};
+
+static int
+pipeline_firewall_parse_args(struct pipeline_firewall *p,
+	struct pipeline_params *params)
+{
+	uint32_t n_rules_present = 0;
+	uint32_t pkt_type_present = 0;
+	uint32_t i;
+
+	/* defaults */
+	p->n_rules = 4 * 1024;
+	p->n_rule_fields = RTE_DIM(field_format_ipv4);
+	p->field_format = field_format_ipv4;
+	p->field_format_size = sizeof(field_format_ipv4);
+
+	for (i = 0; i < params->n_args; i++) {
+		char *arg_name = params->args_name[i];
+		char *arg_value = params->args_value[i];
+
+		if (strcmp(arg_name, "n_rules") == 0) {
+			if (n_rules_present)
+				return -1;
+			n_rules_present = 1;
+
+			p->n_rules = atoi(arg_value);
+			continue;
+		}
+
+		if (strcmp(arg_name, "pkt_type") == 0) {
+			if (pkt_type_present)
+				return -1;
+			pkt_type_present = 1;
+
+			/* ipv4 */
+			if (strcmp(arg_value, "ipv4") == 0) {
+				p->n_rule_fields = RTE_DIM(field_format_ipv4);
+				p->field_format = field_format_ipv4;
+				p->field_format_size =
+					sizeof(field_format_ipv4);
+				continue;
+			}
+
+			/* vlan_ipv4 */
+			if (strcmp(arg_value, "vlan_ipv4") == 0) {
+				p->n_rule_fields =
+					RTE_DIM(field_format_vlan_ipv4);
+				p->field_format = field_format_vlan_ipv4;
+				p->field_format_size =
+					sizeof(field_format_vlan_ipv4);
+				continue;
+			}
+
+			/* qinq_ipv4 */
+			if (strcmp(arg_value, "qinq_ipv4") == 0) {
+				p->n_rule_fields =
+					RTE_DIM(field_format_qinq_ipv4);
+				p->field_format = field_format_qinq_ipv4;
+				p->field_format_size =
+					sizeof(field_format_qinq_ipv4);
+				continue;
+			}
+
+			/* other */
+			return -1;
+		}
+
+		/* other */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void *
+pipeline_firewall_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct pipeline *p;
+	struct pipeline_firewall *p_fw;
+	uint32_t size, i;
+
+	/* Check input arguments */
+	if ((params == NULL) ||
+		(params->n_ports_in == 0) ||
+		(params->n_ports_out == 0))
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct pipeline_firewall));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	p_fw = (struct pipeline_firewall *) p;
+	if (p == NULL)
+		return NULL;
+
+	strcpy(p->name, params->name);
+	p->log_level = params->log_level;
+
+	PLOG(p, HIGH, "Firewall");
+
+	/* Parse arguments */
+	if (pipeline_firewall_parse_args(p_fw, params))
+		return NULL;
+
+	/* Pipeline */
+	{
+		struct rte_pipeline_params pipeline_params = {
+			.name = params->name,
+			.socket_id = params->socket_id,
+			.offset_port_id = 0,
+		};
+
+		p->p = rte_pipeline_create(&pipeline_params);
+		if (p->p == NULL) {
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Input ports */
+	p->n_ports_in = params->n_ports_in;
+	for (i = 0; i < p->n_ports_in; i++) {
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = pipeline_port_in_params_get_ops(
+				&params->port_in[i]),
+			.arg_create = pipeline_port_in_params_convert(
+				&params->port_in[i]),
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = params->port_in[i].burst_size,
+		};
+
+		int status = rte_pipeline_port_in_create(p->p,
+			&port_params,
+			&p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Output ports */
+	p->n_ports_out = params->n_ports_out;
+	for (i = 0; i < p->n_ports_out; i++) {
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = pipeline_port_out_params_get_ops(
+				&params->port_out[i]),
+			.arg_create = pipeline_port_out_params_convert(
+				&params->port_out[i]),
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		int status = rte_pipeline_port_out_create(p->p,
+			&port_params,
+			&p->port_out_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Tables */
+	p->n_tables = 1;
+	{
+		struct rte_table_acl_params table_acl_params = {
+			.name = params->name,
+			.n_rules = p_fw->n_rules,
+			.n_rule_fields = p_fw->n_rule_fields,
+		};
+
+		struct rte_pipeline_table_params table_params = {
+				.ops = &rte_table_acl_ops,
+				.arg_create = &table_acl_params,
+				.f_action_hit = NULL,
+				.f_action_miss = NULL,
+				.arg_ah = NULL,
+				.action_data_size =
+					sizeof(struct firewall_table_entry) -
+					sizeof(struct rte_pipeline_table_entry),
+			};
+
+		int status;
+
+		memcpy(table_acl_params.field_format,
+			p_fw->field_format,
+			p_fw->field_format_size);
+
+		status = rte_pipeline_table_create(p->p,
+			&table_params,
+			&p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Connecting input ports to tables */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_connect_to_table(p->p,
+			p->port_in_id[i],
+			p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Enable input ports */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_enable(p->p,
+			p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p->p) < 0) {
+		rte_pipeline_free(p->p);
+		rte_free(p);
+		return NULL;
+	}
+
+	/* Message queues */
+	p->n_msgq = params->n_msgq;
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_in[i] = params->msgq_in[i];
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_out[i] = params->msgq_out[i];
+
+	/* Message handlers */
+	memcpy(p->handlers, handlers, sizeof(p->handlers));
+	memcpy(p_fw->custom_handlers,
+		custom_handlers,
+		sizeof(p_fw->custom_handlers));
+
+	return p;
+}
+
+static int
+pipeline_firewall_free(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	rte_pipeline_free(p->p);
+	rte_free(p);
+	return 0;
+}
+
+static int
+pipeline_firewall_track(void *pipeline,
+	__rte_unused uint32_t port_in,
+	uint32_t *port_out)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if ((p == NULL) ||
+		(port_in >= p->n_ports_in) ||
+		(port_out == NULL))
+		return -1;
+
+	if (p->n_ports_in == 1) {
+		*port_out = 0;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int
+pipeline_firewall_timer(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	pipeline_msg_req_handle(p);
+	rte_pipeline_flush(p->p);
+
+	return 0;
+}
+
+void *
+pipeline_firewall_msg_req_custom_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_firewall *p_fw = (struct pipeline_firewall *) p;
+	struct pipeline_custom_msg_req *req = msg;
+	pipeline_msg_req_handler f_handle;
+
+	f_handle = (req->subtype < PIPELINE_FIREWALL_MSG_REQS) ?
+		p_fw->custom_handlers[req->subtype] :
+		pipeline_msg_req_invalid_handler;
+
+	if (f_handle == NULL)
+		f_handle = pipeline_msg_req_invalid_handler;
+
+	return f_handle(p, req);
+}
+
+void *
+pipeline_firewall_msg_req_add_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_firewall_add_msg_req *req = msg;
+	struct pipeline_firewall_add_msg_rsp *rsp = msg;
+
+	struct rte_table_acl_rule_add_params params;
+	struct firewall_table_entry entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->port_id]},
+		},
+	};
+
+	memset(&params, 0, sizeof(params));
+
+	switch (req->key.type) {
+	case PIPELINE_FIREWALL_IPV4_5TUPLE:
+		params.priority = req->priority;
+		params.field_value[0].value.u8 =
+			req->key.key.ipv4_5tuple.proto;
+		params.field_value[0].mask_range.u8 =
+			req->key.key.ipv4_5tuple.proto_mask;
+		params.field_value[1].value.u32 =
+			req->key.key.ipv4_5tuple.src_ip;
+		params.field_value[1].mask_range.u32 =
+			req->key.key.ipv4_5tuple.src_ip_mask;
+		params.field_value[2].value.u32 =
+			req->key.key.ipv4_5tuple.dst_ip;
+		params.field_value[2].mask_range.u32 =
+			req->key.key.ipv4_5tuple.dst_ip_mask;
+		params.field_value[3].value.u16 =
+			req->key.key.ipv4_5tuple.src_port_from;
+		params.field_value[3].mask_range.u16 =
+			req->key.key.ipv4_5tuple.src_port_to;
+		params.field_value[4].value.u16 =
+			req->key.key.ipv4_5tuple.dst_port_from;
+		params.field_value[4].mask_range.u16 =
+			req->key.key.ipv4_5tuple.dst_port_to;
+		break;
+
+	default:
+		rsp->status = -1; /* Error */
+		return rsp;
+	}
+
+	rsp->status = rte_pipeline_table_entry_add(p->p,
+		p->table_id[0],
+		&params,
+		(struct rte_pipeline_table_entry *) &entry,
+		&rsp->key_found,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+void *
+pipeline_firewall_msg_req_del_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_firewall_del_msg_req *req = msg;
+	struct pipeline_firewall_del_msg_rsp *rsp = msg;
+
+	struct rte_table_acl_rule_delete_params params;
+
+	memset(&params, 0, sizeof(params));
+
+	switch (req->key.type) {
+	case PIPELINE_FIREWALL_IPV4_5TUPLE:
+		params.field_value[0].value.u8 =
+			req->key.key.ipv4_5tuple.proto;
+		params.field_value[0].mask_range.u8 =
+			req->key.key.ipv4_5tuple.proto_mask;
+		params.field_value[1].value.u32 =
+			req->key.key.ipv4_5tuple.src_ip;
+		params.field_value[1].mask_range.u32 =
+			req->key.key.ipv4_5tuple.src_ip_mask;
+		params.field_value[2].value.u32 =
+			req->key.key.ipv4_5tuple.dst_ip;
+		params.field_value[2].mask_range.u32 =
+			req->key.key.ipv4_5tuple.dst_ip_mask;
+		params.field_value[3].value.u16 =
+			req->key.key.ipv4_5tuple.src_port_from;
+		params.field_value[3].mask_range.u16 =
+			req->key.key.ipv4_5tuple.src_port_to;
+		params.field_value[4].value.u16 =
+			req->key.key.ipv4_5tuple.dst_port_from;
+		params.field_value[4].mask_range.u16 =
+			req->key.key.ipv4_5tuple.dst_port_to;
+		break;
+
+	default:
+		rsp->status = -1; /* Error */
+		return rsp;
+	}
+
+	rsp->status = rte_pipeline_table_entry_delete(p->p,
+		p->table_id[0],
+		&params,
+		&rsp->key_found,
+		NULL);
+
+	return rsp;
+}
+
+void *
+pipeline_firewall_msg_req_add_default_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_firewall_add_default_msg_req *req = msg;
+	struct pipeline_firewall_add_default_msg_rsp *rsp = msg;
+
+	struct firewall_table_entry default_entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->port_id]},
+		},
+	};
+
+	rsp->status = rte_pipeline_table_default_entry_add(p->p,
+		p->table_id[0],
+		(struct rte_pipeline_table_entry *) &default_entry,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+void *
+pipeline_firewall_msg_req_del_default_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_firewall_del_default_msg_rsp *rsp = msg;
+
+	rsp->status = rte_pipeline_table_default_entry_delete(p->p,
+		p->table_id[0],
+		NULL);
+
+	return rsp;
+}
+
+struct pipeline_be_ops pipeline_firewall_be_ops = {
+	.f_init = pipeline_firewall_init,
+	.f_free = pipeline_firewall_free,
+	.f_run = NULL,
+	.f_timer = pipeline_firewall_timer,
+	.f_track = pipeline_firewall_track,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_firewall_be.h b/examples/ip_pipeline/pipeline/pipeline_firewall_be.h
new file mode 100644
index 0000000..8e1fd69
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_firewall_be.h
@@ -0,0 +1,138 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_FIREWALL_BE_H__
+#define __INCLUDE_PIPELINE_FIREWALL_BE_H__
+
+#include "pipeline_common_be.h"
+
+enum pipeline_firewall_key_type {
+	PIPELINE_FIREWALL_IPV4_5TUPLE,
+};
+
+struct pipeline_firewall_key_ipv4_5tuple {
+	uint32_t src_ip;
+	uint32_t src_ip_mask;
+	uint32_t dst_ip;
+	uint32_t dst_ip_mask;
+	uint16_t src_port_from;
+	uint16_t src_port_to;
+	uint16_t dst_port_from;
+	uint16_t dst_port_to;
+	uint8_t proto;
+	uint8_t proto_mask;
+};
+
+struct pipeline_firewall_key {
+	enum pipeline_firewall_key_type type;
+	union {
+		struct pipeline_firewall_key_ipv4_5tuple ipv4_5tuple;
+	} key;
+};
+
+enum pipeline_firewall_msg_req_type {
+	PIPELINE_FIREWALL_MSG_REQ_ADD = 0,
+	PIPELINE_FIREWALL_MSG_REQ_DEL,
+	PIPELINE_FIREWALL_MSG_REQ_ADD_DEFAULT,
+	PIPELINE_FIREWALL_MSG_REQ_DEL_DEFAULT,
+	PIPELINE_FIREWALL_MSG_REQS
+};
+
+/*
+ * MSG ADD
+ */
+struct pipeline_firewall_add_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_firewall_msg_req_type subtype;
+
+	/* key */
+	struct pipeline_firewall_key key;
+
+	/* data */
+	int32_t priority;
+	uint32_t port_id;
+};
+
+struct pipeline_firewall_add_msg_rsp {
+	int status;
+	int key_found;
+	void *entry_ptr;
+};
+
+/*
+ * MSG DEL
+ */
+struct pipeline_firewall_del_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_firewall_msg_req_type subtype;
+
+	/* key */
+	struct pipeline_firewall_key key;
+};
+
+struct pipeline_firewall_del_msg_rsp {
+	int status;
+	int key_found;
+};
+
+/*
+ * MSG ADD DEFAULT
+ */
+struct pipeline_firewall_add_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_firewall_msg_req_type subtype;
+
+	/* data */
+	uint32_t port_id;
+};
+
+struct pipeline_firewall_add_default_msg_rsp {
+	int status;
+	void *entry_ptr;
+};
+
+/*
+ * MSG DEL DEFAULT
+ */
+struct pipeline_firewall_del_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_firewall_msg_req_type subtype;
+};
+
+struct pipeline_firewall_del_default_msg_rsp {
+	int status;
+};
+
+extern struct pipeline_be_ops pipeline_firewall_be_ops;
+
+#endif
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 10/11] ip_pipeline: added new implementation of routing pipeline
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (8 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 09/11] ip_pipeline: added new implementation of firewall pipeline Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 11/11] ip_pipeline: added new implementation of flow classification pipeline Maciej Gajdzica
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

Routing pipeline implementation is split to two files.
pipeline_routing.c file handles front-end functions (cli commands
parsing) pipeline_routing_ops.c contains implementation of functions
done by pipeline (back-end).

Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
---
 examples/ip_pipeline/Makefile                      |    2 +
 examples/ip_pipeline/init.c                        |    2 +
 examples/ip_pipeline/pipeline/pipeline_routing.c   | 1787 ++++++++++++++++----
 examples/ip_pipeline/pipeline/pipeline_routing.h   |   99 ++
 .../ip_pipeline/pipeline/pipeline_routing_be.c     |  869 ++++++++++
 .../ip_pipeline/pipeline/pipeline_routing_be.h     |  230 +++
 6 files changed, 2631 insertions(+), 358 deletions(-)
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_routing_be.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index 382fee6..a2881a6 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -64,6 +64,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
 
 CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/pipeline
 CFLAGS += -O3
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 6ba14da..728ce9a 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -48,6 +48,7 @@
 #include "pipeline_master.h"
 #include "pipeline_passthrough.h"
 #include "pipeline_firewall.h"
+#include "pipeline_routing.h"
 
 #define APP_NAME_SIZE	32
 
@@ -1289,6 +1290,7 @@ int app_init(struct app_params *app)
 	app_pipeline_type_register(app, &pipeline_master);
 	app_pipeline_type_register(app, &pipeline_passthrough);
 	app_pipeline_type_register(app, &pipeline_firewall);
+	app_pipeline_type_register(app, &pipeline_routing);
 
 	app_init_pipelines(app);
 	app_init_threads(app);
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline/pipeline_routing.c
index b1ce624..beec982 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -31,444 +31,1515 @@
  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_etheraddr.h>
 
-#include <rte_malloc.h>
-#include <rte_log.h>
-#include <rte_ethdev.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-#include <rte_byteorder.h>
+#include "app.h"
+#include "pipeline_common_fe.h"
+#include "pipeline_routing.h"
 
-#include <rte_port_ring.h>
-#include <rte_table_lpm.h>
-#include <rte_table_hash.h>
-#include <rte_pipeline.h>
+struct app_pipeline_routing_route {
+	struct pipeline_routing_route_key key;
+	struct app_pipeline_routing_route_params params;
+	void *entry_ptr;
 
-#include "main.h"
+	TAILQ_ENTRY(app_pipeline_routing_route) node;
+};
 
-#include <unistd.h>
+struct app_pipeline_routing_arp_entry {
+	struct pipeline_routing_arp_key key;
+	struct ether_addr macaddr;
+	void *entry_ptr;
 
-struct app_routing_table_entry {
-	struct rte_pipeline_table_entry head;
-	uint32_t nh_ip;
-	uint32_t nh_iface;
+	TAILQ_ENTRY(app_pipeline_routing_arp_entry) node;
 };
 
-struct app_arp_table_entry {
-	struct rte_pipeline_table_entry head;
-	struct ether_addr nh_arp;
+struct pipeline_routing {
+	/* Parameters */
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+
+	/* Routes */
+	TAILQ_HEAD(, app_pipeline_routing_route) routes;
+	uint32_t n_routes;
+
+	uint32_t default_route_present;
+	uint32_t default_route_port_id;
+	void *default_route_entry_ptr;
+
+	/* ARP entries */
+	TAILQ_HEAD(, app_pipeline_routing_arp_entry) arp_entries;
+	uint32_t n_arp_entries;
+
+	uint32_t default_arp_entry_present;
+	uint32_t default_arp_entry_port_id;
+	void *default_arp_entry_ptr;
 };
 
-static inline void
-app_routing_table_write_metadata(
-	struct rte_mbuf *pkt,
-	struct app_routing_table_entry *entry)
+static void *
+pipeline_routing_init(struct pipeline_params *params,
+	__rte_unused void *arg)
 {
-	struct app_pkt_metadata *c =
-		(struct app_pkt_metadata *) RTE_MBUF_METADATA_UINT8_PTR(pkt, 0);
+	struct pipeline_routing *p;
+	uint32_t size;
+
+	/* Check input arguments */
+	if ((params == NULL) ||
+		(params->n_ports_in == 0) ||
+		(params->n_ports_out == 0))
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct pipeline_routing));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		return NULL;
 
-	c->arp_key.nh_ip = entry->nh_ip;
-	c->arp_key.nh_iface = entry->nh_iface;
+	/* Initialization */
+	p->n_ports_in = params->n_ports_in;
+	p->n_ports_out = params->n_ports_out;
+
+	TAILQ_INIT(&p->routes);
+	p->n_routes = 0;
+
+	TAILQ_INIT(&p->arp_entries);
+	p->n_arp_entries = 0;
+
+	return p;
 }
 
 static int
-app_routing_table_ah(
-	struct rte_mbuf **pkts,
-	uint64_t *pkts_mask,
-	struct rte_pipeline_table_entry **entries,
-	__attribute__((unused)) void *arg)
+app_pipeline_routing_free(void *pipeline)
 {
-	uint64_t pkts_in_mask = *pkts_mask;
+	struct pipeline_routing *p = pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
 
-	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {
-		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);
-		uint32_t i;
+	/* Free resources */
+	while (!TAILQ_EMPTY(&p->routes)) {
+		struct app_pipeline_routing_route *route;
 
-		for (i = 0; i < n_pkts; i++) {
-			struct rte_mbuf *m = pkts[i];
-			struct app_routing_table_entry *a =
-				(struct app_routing_table_entry *) entries[i];
+		route = TAILQ_FIRST(&p->routes);
+		TAILQ_REMOVE(&p->routes, route, node);
+		rte_free(route);
+	}
+
+	while (!TAILQ_EMPTY(&p->arp_entries)) {
+		struct app_pipeline_routing_arp_entry *arp_entry;
+
+		arp_entry = TAILQ_FIRST(&p->arp_entries);
+		TAILQ_REMOVE(&p->arp_entries, arp_entry, node);
+		rte_free(arp_entry);
+	}
 
-			app_routing_table_write_metadata(m, a);
+	rte_free(p);
+	return 0;
+}
+
+static struct app_pipeline_routing_route *
+app_pipeline_routing_find_route(struct pipeline_routing *p,
+		const struct pipeline_routing_route_key *key)
+{
+	struct app_pipeline_routing_route *it, *found;
+
+	found = NULL;
+	TAILQ_FOREACH(it, &p->routes, node) {
+		if ((key->type == it->key.type) &&
+			(key->key.ipv4.ip == it->key.key.ipv4.ip) &&
+			(key->key.ipv4.depth == it->key.key.ipv4.depth)) {
+			found = it;
+			break;
 		}
-	} else
-		for ( ; pkts_in_mask; ) {
-			struct rte_mbuf *m;
-			struct app_routing_table_entry *a;
-			uint64_t pkt_mask;
-			uint32_t packet_index;
-
-			packet_index = __builtin_ctzll(pkts_in_mask);
-			pkt_mask = 1LLU << packet_index;
-			pkts_in_mask &= ~pkt_mask;
-
-			m = pkts[packet_index];
-			a = (struct app_routing_table_entry *)
-				entries[packet_index];
-			app_routing_table_write_metadata(m, a);
+	}
+
+	return found;
+}
+
+static struct app_pipeline_routing_arp_entry *
+app_pipeline_routing_find_arp_entry(struct pipeline_routing *p,
+		const struct pipeline_routing_arp_key *key)
+{
+	struct app_pipeline_routing_arp_entry *it, *found;
+
+	found = NULL;
+	TAILQ_FOREACH(it, &p->arp_entries, node) {
+		if ((key->type == it->key.type) &&
+			(key->key.ipv4.port_id == it->key.key.ipv4.port_id) &&
+			(key->key.ipv4.ip == it->key.key.ipv4.ip)) {
+			found = it;
+			break;
 		}
+	}
 
-	return 0;
+	return found;
 }
 
-static inline void
-app_arp_table_write_metadata(
-	struct rte_mbuf *pkt,
-	struct app_arp_table_entry *entry)
+static void
+print_route(const struct app_pipeline_routing_route *route)
 {
-	struct app_pkt_metadata *c =
-		(struct app_pkt_metadata *) RTE_MBUF_METADATA_UINT8_PTR(pkt, 0);
-	ether_addr_copy(&entry->nh_arp, &c->nh_arp);
+	if (route->key.type == PIPELINE_ROUTING_ROUTE_IPV4) {
+		const struct pipeline_routing_route_key_ipv4 *key =
+				&route->key.key.ipv4;
+
+		printf("IP Prefix = %" PRIu32 ".%" PRIu32
+			".%" PRIu32 ".%" PRIu32 "/%" PRIu32 " => "
+			"(Port = %" PRIu32 ", Next Hop IP = "
+			"%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ")\n",
+			(key->ip >> 24) & 0xFF,
+			(key->ip >> 16) & 0xFF,
+			(key->ip >> 8) & 0xFF,
+			key->ip & 0xFF,
+
+			key->depth,
+			route->params.port_id,
+
+			(route->params.ip >> 24) & 0xFF,
+			(route->params.ip >> 16) & 0xFF,
+			(route->params.ip >> 8) & 0xFF,
+			route->params.ip & 0xFF);
+	}
+}
+
+static void
+print_arp_entry(const struct app_pipeline_routing_arp_entry *entry)
+{
+	printf("(Port = %" PRIu32 ", IP = %" PRIu32 ".%" PRIu32
+		".%" PRIu32 ".%" PRIu32 ") => "
+		"HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
+		":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
+		entry->key.key.ipv4.port_id,
+		(entry->key.key.ipv4.ip >> 24) & 0xFF,
+		(entry->key.key.ipv4.ip >> 16) & 0xFF,
+		(entry->key.key.ipv4.ip >> 8) & 0xFF,
+		entry->key.key.ipv4.ip & 0xFF,
+
+		entry->macaddr.addr_bytes[0],
+		entry->macaddr.addr_bytes[1],
+		entry->macaddr.addr_bytes[2],
+		entry->macaddr.addr_bytes[3],
+		entry->macaddr.addr_bytes[4],
+		entry->macaddr.addr_bytes[5]);
 }
 
 static int
-app_arp_table_ah(
-	struct rte_mbuf **pkts,
-	uint64_t *pkts_mask,
-	struct rte_pipeline_table_entry **entries,
-	__attribute__((unused)) void *arg)
+app_pipeline_routing_route_ls(struct app_params *app, uint32_t pipeline_id)
 {
-	uint64_t pkts_in_mask = *pkts_mask;
+	struct pipeline_routing *p;
+	struct app_pipeline_routing_route *it;
 
-	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {
-		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);
-		uint32_t i;
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -EINVAL;
 
-		for (i = 0; i < n_pkts; i++) {
-			struct rte_mbuf *m = pkts[i];
-			struct app_arp_table_entry *a =
-				(struct app_arp_table_entry *) entries[i];
+	TAILQ_FOREACH(it, &p->routes, node)
+		print_route(it);
 
-			app_arp_table_write_metadata(m, a);
-		}
-	} else {
-		for ( ; pkts_in_mask; ) {
-			struct rte_mbuf *m;
-			struct app_arp_table_entry *a;
-			uint64_t pkt_mask;
-			uint32_t packet_index;
-
-			packet_index = __builtin_ctzll(pkts_in_mask);
-			pkt_mask = 1LLU << packet_index;
-			pkts_in_mask &= ~pkt_mask;
-
-			m = pkts[packet_index];
-			a = (struct app_arp_table_entry *)
-				entries[packet_index];
-			app_arp_table_write_metadata(m, a);
-		}
+	if (p->default_route_present)
+		printf("Default route: port %" PRIu32 " (entry ptr = %p)\n",
+				p->default_route_port_id,
+				p->default_route_entry_ptr);
+	else
+		printf("Default: DROP\n");
+
+	return 0;
+}
+
+int
+app_pipeline_routing_add_route(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_routing_route_key *key,
+	struct app_pipeline_routing_route_params *route_params)
+{
+	struct pipeline_routing *p;
+
+	struct pipeline_routing_route_add_msg_req *req;
+	struct pipeline_routing_route_add_msg_rsp *rsp;
+
+	struct app_pipeline_routing_route *entry;
+
+	int new_entry;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL) ||
+		(route_params == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	switch (key->type) {
+	case PIPELINE_ROUTING_ROUTE_IPV4:
+	{
+		uint32_t depth = key->key.ipv4.depth;
+		uint32_t netmask;
+
+		/* key */
+		if ((depth == 0) || (depth > 32))
+			return -1;
+
+		netmask = (~0) << (32 - depth);
+		key->key.ipv4.ip &= netmask;
+
+		/* route params */
+		if (route_params->port_id >= p->n_ports_out)
+			return -1;
+	}
+	break;
+
+	default:
+		return -1;
+	}
+
+	/* Find existing rule or allocate new rule */
+	entry = app_pipeline_routing_find_route(p, key);
+	new_entry = (entry == NULL);
+	if (entry == NULL) {
+		entry = rte_malloc(NULL, sizeof(*entry), RTE_CACHE_LINE_SIZE);
+
+		if (entry == NULL)
+			return -1;
+	}
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL) {
+		if (new_entry)
+			rte_free(entry);
+		return -1;
+	}
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD;
+	memcpy(&req->key, key, sizeof(*key));
+	req->flags = route_params->flags;
+	req->port_id = route_params->port_id;
+	req->ip = route_params->ip;
+
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL) {
+		if (new_entry)
+			rte_free(entry);
+		return -1;
+	}
+
+	/* Read response and write entry */
+	if (rsp->status ||
+		(rsp->entry_ptr == NULL) ||
+		((new_entry == 0) && (rsp->key_found == 0)) ||
+		((new_entry == 1) && (rsp->key_found == 1))) {
+		app_msg_free(app, rsp);
+		if (new_entry)
+			rte_free(entry);
+		return -1;
+	}
+
+	memcpy(&entry->key, key, sizeof(*key));
+	memcpy(&entry->params, route_params, sizeof(*route_params));
+	entry->entry_ptr = rsp->entry_ptr;
+
+	/* Commit entry */
+	if (new_entry) {
+		TAILQ_INSERT_TAIL(&p->routes, entry, node);
+		p->n_routes++;
+	}
+
+	print_route(entry);
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+	return 0;
+}
+
+int
+app_pipeline_routing_delete_route(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_routing_route_key *key)
+{
+	struct pipeline_routing *p;
+
+	struct pipeline_routing_route_delete_msg_req *req;
+	struct pipeline_routing_route_delete_msg_rsp *rsp;
+
+	struct app_pipeline_routing_route *entry;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	switch (key->type) {
+	case PIPELINE_ROUTING_ROUTE_IPV4:
+	{
+		uint32_t depth = key->key.ipv4.depth;
+		uint32_t netmask;
+
+		/* key */
+		if ((depth == 0) || (depth > 32))
+			return -1;
+
+		netmask = (~0) << (32 - depth);
+		key->key.ipv4.ip &= netmask;
+	}
+	break;
+
+	default:
+		return -1;
 	}
 
+	/* Find rule */
+	entry = app_pipeline_routing_find_route(p, key);
+	if (entry == NULL)
+		return 0;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL;
+	memcpy(&req->key, key, sizeof(*key));
+
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status || !rsp->key_found) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	/* Remove route */
+	TAILQ_REMOVE(&p->routes, entry, node);
+	p->n_routes--;
+	rte_free(entry);
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
 	return 0;
 }
 
-static uint64_t app_arp_table_hash(
-	void *key,
-	__attribute__((unused)) uint32_t key_size,
-	__attribute__((unused)) uint64_t seed)
+int
+app_pipeline_routing_add_default_route(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id)
 {
-	uint32_t *k = (uint32_t *) key;
+	struct pipeline_routing *p;
+
+	struct pipeline_routing_route_add_default_msg_req *req;
+	struct pipeline_routing_route_add_default_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (port_id >= p->n_ports_out)
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
 
-	return k[1];
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD_DEFAULT;
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response and write route */
+	if (rsp->status || (rsp->entry_ptr == NULL)) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	p->default_route_port_id = port_id;
+	p->default_route_entry_ptr = rsp->entry_ptr;
+
+	/* Commit route */
+	p->default_route_present = 1;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
 }
 
-struct app_core_routing_message_handle_params {
-	struct rte_ring *ring_req;
-	struct rte_ring *ring_resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t routing_table_id;
-	uint32_t arp_table_id;
-};
+int
+app_pipeline_routing_delete_default_route(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct pipeline_routing *p;
 
-static void
-app_message_handle(struct app_core_routing_message_handle_params *params);
+	struct pipeline_routing_arp_delete_default_msg_req *req;
+	struct pipeline_routing_arp_delete_default_msg_rsp *rsp;
 
-void
-app_main_loop_pipeline_routing(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
 
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t routing_table_id, arp_table_id;
-	uint32_t i;
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
 
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-	struct app_core_routing_message_handle_params mh_params;
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL_DEFAULT;
 
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_RT))
-		rte_panic("Core %u misconfiguration\n", core_id);
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
 
-	RTE_LOG(INFO, USER1, "Core %u is doing routing\n", core_id);
+	/* Read response and write route */
+	if (rsp->status) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	/* Commit route */
+	p->default_route_present = 0;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
 
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
+static int
+app_pipeline_routing_arp_ls(struct app_params *app, uint32_t pipeline_id)
+{
+	struct pipeline_routing *p;
+	struct app_pipeline_routing_arp_entry *it;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
 	if (p == NULL)
-		rte_panic("Unable to configure the pipeline\n");
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("Unable to configure input port for "
-				"ring %d\n", i);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("Unable to configure output port for "
-				"ring %d\n", i);
-	}
-
-	/* Routing table configuration */
+		return -EINVAL;
+
+	TAILQ_FOREACH(it, &p->arp_entries, node)
+		print_arp_entry(it);
+
+	if (p->default_arp_entry_present)
+		printf("Default entry: port %" PRIu32 " (entry ptr = %p)\n",
+				p->default_arp_entry_port_id,
+				p->default_arp_entry_ptr);
+	else
+		printf("Default: DROP\n");
+
+	return 0;
+}
+
+int
+app_pipeline_routing_add_arp_entry(struct app_params *app, uint32_t pipeline_id,
+		struct pipeline_routing_arp_key *key,
+		struct ether_addr *macaddr)
+{
+	struct pipeline_routing *p;
+
+	struct pipeline_routing_arp_add_msg_req *req;
+	struct pipeline_routing_arp_add_msg_rsp *rsp;
+
+	struct app_pipeline_routing_arp_entry *entry;
+
+	int new_entry;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL) ||
+		(macaddr == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	switch (key->type) {
+	case PIPELINE_ROUTING_ARP_IPV4:
 	{
-		struct rte_table_lpm_params table_lpm_params = {
-			.n_rules = app.max_routing_rules,
-			.entry_unique_size =
-				sizeof(struct app_routing_table_entry),
-			.offset = __builtin_offsetof(struct app_pkt_metadata,
-				flow_key.ip_dst),
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_lpm_ops,
-			.arg_create = &table_lpm_params,
-			.f_action_hit = app_routing_table_ah,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size =
-				sizeof(struct app_routing_table_entry) -
-				sizeof(struct rte_pipeline_table_entry),
-		};
-
-		if (rte_pipeline_table_create(p, &table_params,
-			&routing_table_id))
-			rte_panic("Unable to configure the LPM table\n");
-	}
-
-	/* ARP table configuration */
+		uint32_t port_id = key->key.ipv4.port_id;
+
+		/* key */
+		if (port_id >= p->n_ports_out)
+			return -1;
+	}
+	break;
+
+	default:
+		return -1;
+	}
+
+	/* Find existing entry or allocate new */
+	entry = app_pipeline_routing_find_arp_entry(p, key);
+	new_entry = (entry == NULL);
+	if (entry == NULL) {
+		entry = rte_malloc(NULL, sizeof(*entry), RTE_CACHE_LINE_SIZE);
+
+		if (entry == NULL)
+			return -1;
+	}
+
+	/* Message buffer allocation */
+	req = app_msg_alloc(app);
+	if (req == NULL) {
+		if (new_entry)
+			rte_free(entry);
+		return -1;
+	}
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_ADD;
+	memcpy(&req->key, key, sizeof(*key));
+	ether_addr_copy(macaddr, &req->macaddr);
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL) {
+		if (new_entry)
+			rte_free(entry);
+		return -1;
+	}
+
+	/* Read response and write entry */
+	if (rsp->status ||
+		(rsp->entry_ptr == NULL) ||
+		((new_entry == 0) && (rsp->key_found == 0)) ||
+		((new_entry == 1) && (rsp->key_found == 1))) {
+		app_msg_free(app, rsp);
+		if (new_entry)
+			rte_free(entry);
+		return -1;
+	}
+
+	memcpy(&entry->key, key, sizeof(*key));
+	ether_addr_copy(macaddr, &entry->macaddr);
+	entry->entry_ptr = rsp->entry_ptr;
+
+	/* Commit entry */
+	if (new_entry) {
+		TAILQ_INSERT_TAIL(&p->arp_entries, entry, node);
+		p->n_arp_entries++;
+	}
+
+	print_arp_entry(entry);
+
+	/* Message buffer free */
+	app_msg_free(app, rsp);
+	return 0;
+}
+
+int
+app_pipeline_routing_delete_arp_entry(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_routing_arp_key *key)
+{
+	struct pipeline_routing *p;
+
+	struct pipeline_routing_arp_delete_msg_req *req;
+	struct pipeline_routing_arp_delete_msg_rsp *rsp;
+
+	struct app_pipeline_routing_arp_entry *entry;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -EINVAL;
+
+	switch (key->type) {
+	case PIPELINE_ROUTING_ARP_IPV4:
 	{
-		struct rte_table_hash_key8_lru_params table_arp_params = {
-			.n_entries = app.max_arp_rules,
-			.f_hash = app_arp_table_hash,
-			.seed = 0,
-			.signature_offset = 0, /* Unused */
-			.key_offset = __builtin_offsetof(
-				struct app_pkt_metadata, arp_key),
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_hash_key8_lru_dosig_ops,
-			.arg_create = &table_arp_params,
-			.f_action_hit = app_arp_table_ah,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = sizeof(struct app_arp_table_entry) -
-				sizeof(struct rte_pipeline_table_entry),
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &arp_table_id))
-			rte_panic("Unable to configure the ARP table\n");
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++) {
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			routing_table_id))
-			rte_panic("Unable to connect input port %u to "
-				"table %u\n", port_in_id[i],  routing_table_id);
-	}
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("Pipeline consistency check failed\n");
-
-	/* Message handling */
-	mh_params.ring_req =
-		app_get_ring_req(app_get_first_core_id(APP_CORE_RT));
-	mh_params.ring_resp =
-		app_get_ring_resp(app_get_first_core_id(APP_CORE_RT));
-	mh_params.p = p;
-	mh_params.port_out_id = port_out_id;
-	mh_params.routing_table_id = routing_table_id;
-	mh_params.arp_table_id = arp_table_id;
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0) {
-			rte_pipeline_flush(p);
-			app_message_handle(&mh_params);
-		}
+		uint32_t port_id = key->key.ipv4.port_id;
+
+		/* key */
+		if (port_id >= p->n_ports_out)
+			return -1;
+	}
+	break;
+
+	default:
+		return -1;
+	}
+
+	/* Find rule */
+	entry = app_pipeline_routing_find_arp_entry(p, key);
+	if (entry == NULL)
+		return 0;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_DEL;
+	memcpy(&req->key, key, sizeof(*key));
+
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status || !rsp->key_found) {
+		app_msg_free(app, rsp);
+		return -1;
 	}
+
+	/* Remove entry */
+	TAILQ_REMOVE(&p->arp_entries, entry, node);
+	p->n_arp_entries--;
+	rte_free(entry);
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
 }
 
-void
-app_message_handle(struct app_core_routing_message_handle_params *params)
+int
+app_pipeline_routing_add_default_arp_entry(struct app_params *app,
+		uint32_t pipeline_id,
+		uint32_t port_id)
 {
-	struct rte_ring *ring_req = params->ring_req;
-	struct rte_ring *ring_resp;
-	void *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t routing_table_id, arp_table_id;
-	int result;
-
-	/* Read request message */
-	result = rte_ring_sc_dequeue(ring_req, &msg);
-	if (result != 0)
+	struct pipeline_routing *p;
+
+	struct pipeline_routing_arp_add_default_msg_req *req;
+	struct pipeline_routing_arp_add_default_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (port_id >= p->n_ports_out)
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_ADD_DEFAULT;
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response and write entry */
+	if (rsp->status || rsp->entry_ptr == NULL) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	p->default_arp_entry_port_id = port_id;
+	p->default_arp_entry_ptr = rsp->entry_ptr;
+
+	/* Commit entry */
+	p->default_arp_entry_present = 1;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+int
+app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct pipeline_routing *p;
+
+	struct pipeline_routing_arp_delete_default_msg_req *req;
+	struct pipeline_routing_arp_delete_default_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -EINVAL;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -ENOMEM;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_DEL_DEFAULT;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -ETIMEDOUT;
+
+	/* Read response and write entry */
+	if (rsp->status) {
+		app_msg_free(app, rsp);
+		return rsp->status;
+	}
+
+	/* Commit entry */
+	p->default_arp_entry_present = 0;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+/*
+ * route add
+ */
+
+struct cmd_route_add_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	uint32_t port;
+	cmdline_ipaddr_t nh_ip;
+};
+
+static void
+cmd_route_add_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct app_pipeline_routing_route_params rt_params;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	rt_params.flags = 0; /* remote route */
+	rt_params.port_id = params->port;
+	rt_params.ip = rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&rt_params);
+
+	if (status != 0) {
+		printf("Command failed\n");
 		return;
+	}
+}
 
-	ring_resp = params->ring_resp;
-	p = params->p;
-	port_out_id = params->port_out_id;
-	routing_table_id = params->routing_table_id;
-	arp_table_id = params->arp_table_id;
+static cmdline_parse_token_string_t cmd_route_add_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, depth, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, port, UINT32);
+
+static cmdline_parse_token_ipaddr_t cmd_route_add_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, nh_ip);
+
+static cmdline_parse_inst_t cmd_route_add = {
+	.f = cmd_route_add_parsed,
+	.data = NULL,
+	.help_str = "Route add",
+	.tokens = {
+		(void *)&cmd_route_add_p_string,
+		(void *)&cmd_route_add_p,
+		(void *)&cmd_route_add_route_string,
+		(void *)&cmd_route_add_add_string,
+		(void *)&cmd_route_add_ip,
+		(void *)&cmd_route_add_depth,
+		(void *)&cmd_route_add_port,
+		(void *)&cmd_route_add_nh_ip,
+		NULL,
+	},
+};
 
-	/* Handle request */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	switch (req->type) {
-	case APP_MSG_REQ_PING:
-	{
-		result = 0;
-		break;
+/*
+ * route del
+ */
+
+struct cmd_route_del_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+};
+
+static void
+cmd_route_del_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_del_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	status = app_pipeline_routing_delete_route(app, params->p, &key);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
 
-	case APP_MSG_REQ_RT_ADD:
-	{
-		struct app_routing_table_entry entry = {
-			.head = {
-				.action = RTE_PIPELINE_ACTION_TABLE,
-				{.table_id = arp_table_id},
-			},
-			.nh_ip = req->routing_add.nh_ip,
-			.nh_iface = port_out_id[req->routing_add.port],
-		};
+static cmdline_parse_token_string_t cmd_route_del_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_del_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_del_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_del_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_del_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_del_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_del_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_del_result, del_string,
+	"del");
+
+static cmdline_parse_token_ipaddr_t cmd_route_del_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_del_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_del_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_del_result, depth, UINT32);
+
+static cmdline_parse_inst_t cmd_route_del = {
+	.f = cmd_route_del_parsed,
+	.data = NULL,
+	.help_str = "Route delete",
+	.tokens = {
+		(void *)&cmd_route_del_p_string,
+		(void *)&cmd_route_del_p,
+		(void *)&cmd_route_del_route_string,
+		(void *)&cmd_route_del_del_string,
+		(void *)&cmd_route_del_ip,
+		(void *)&cmd_route_del_depth,
+		NULL,
+	},
+};
 
-		struct rte_table_lpm_key key = {
-			.ip = req->routing_add.ip,
-			.depth = req->routing_add.depth,
-		};
+/*
+ * route add default
+ */
 
-		struct rte_pipeline_table_entry *entry_ptr;
+struct cmd_route_add_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t default_string;
+	uint32_t port;
+};
 
-		int key_found;
+static void
+cmd_route_add_default_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add_default_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
 
-		result = rte_pipeline_table_entry_add(p, routing_table_id, &key,
-			(struct rte_pipeline_table_entry *) &entry, &key_found,
-			&entry_ptr);
-		break;
+	status = app_pipeline_routing_add_default_route(app, params->p,
+			params->port);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
 
-	case APP_MSG_REQ_RT_DEL:
-	{
-		struct rte_table_lpm_key key = {
-			.ip = req->routing_del.ip,
-			.depth = req->routing_del.depth,
-		};
+static cmdline_parse_token_string_t cmd_route_add_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add_default_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add_default_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add_default_result, p, UINT32);
+
+cmdline_parse_token_string_t cmd_route_add_default_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add_default_result,
+		route_string, "route");
+
+cmdline_parse_token_string_t cmd_route_add_default_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add_default_result,
+		add_string, "add");
+
+cmdline_parse_token_string_t cmd_route_add_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add_default_result,
+	default_string, "default");
+
+cmdline_parse_token_num_t cmd_route_add_default_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add_default_result,
+		port, UINT32);
+
+cmdline_parse_inst_t cmd_route_add_default = {
+	.f = cmd_route_add_default_parsed,
+	.data = NULL,
+	.help_str = "Route default set",
+	.tokens = {
+		(void *)&cmd_route_add_default_p_string,
+		(void *)&cmd_route_add_default_p,
+		(void *)&cmd_route_add_default_route_string,
+		(void *)&cmd_route_add_default_add_string,
+		(void *)&cmd_route_add_default_default_string,
+		(void *)&cmd_route_add_default_port,
+		NULL,
+	},
+};
 
-		int key_found;
+/*
+ * route del default
+ */
+
+struct cmd_route_del_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t default_string;
+};
+
+static void
+cmd_route_del_default_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	 void *data)
+{
+	struct cmd_route_del_default_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_routing_delete_default_route(app, params->p);
 
-		result = rte_pipeline_table_entry_delete(p, routing_table_id,
-			&key, &key_found, NULL);
-		break;
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
 
-	case APP_MSG_REQ_ARP_ADD:
-	{
+static cmdline_parse_token_string_t cmd_route_del_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_del_default_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_del_default_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_del_default_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_del_default_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_del_default_result,
+		route_string, "route");
+
+static cmdline_parse_token_string_t cmd_route_del_default_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_del_default_result,
+		del_string, "del");
+
+static cmdline_parse_token_string_t cmd_route_del_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_del_default_result,
+	default_string, "default");
+
+
+static cmdline_parse_inst_t cmd_route_del_default = {
+	.f = cmd_route_del_default_parsed,
+	.data = NULL,
+	.help_str = "Route default clear",
+	.tokens = {
+		(void *)&cmd_route_del_default_p_string,
+		(void *)&cmd_route_del_default_p,
+		(void *)&cmd_route_del_default_route_string,
+		(void *)&cmd_route_del_default_del_string,
+		(void *)&cmd_route_del_default_default_string,
+		NULL,
+	},
+};
 
-		struct app_arp_table_entry entry = {
-			.head = {
-				.action = RTE_PIPELINE_ACTION_PORT,
-				{.port_id =
-					port_out_id[req->arp_add.out_iface]},
-			},
-			.nh_arp = req->arp_add.nh_arp,
-		};
+/*
+ * route ls
+ */
 
-		struct app_arp_key arp_key = {
-			.nh_ip = req->arp_add.nh_ip,
-			.nh_iface = port_out_id[req->arp_add.out_iface],
-		};
+struct cmd_route_ls_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t ls_string;
+};
 
-		struct rte_pipeline_table_entry *entry_ptr;
+static void
+cmd_route_ls_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_ls_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
 
-		int key_found;
+	status = app_pipeline_routing_route_ls(app, params->p);
 
-		result = rte_pipeline_table_entry_add(p, arp_table_id, &arp_key,
-			(struct rte_pipeline_table_entry *) &entry, &key_found,
-			&entry_ptr);
-		break;
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
 
-	case APP_MSG_REQ_ARP_DEL:
-	{
-		struct app_arp_key arp_key = {
-			.nh_ip = req->arp_del.nh_ip,
-			.nh_iface = port_out_id[req->arp_del.out_iface],
-		};
+static cmdline_parse_token_string_t cmd_route_ls_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_ls_result, p_string, "p");
+
+static cmdline_parse_token_num_t cmd_route_ls_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_ls_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_ls_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_ls_result,
+	route_string, "route");
+
+static cmdline_parse_token_string_t cmd_route_ls_ls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_ls_result, ls_string,
+	"ls");
+
+static cmdline_parse_inst_t cmd_route_ls = {
+	.f = cmd_route_ls_parsed,
+	.data = NULL,
+	.help_str = "Route list",
+	.tokens = {
+		(void *)&cmd_route_ls_p_string,
+		(void *)&cmd_route_ls_p,
+		(void *)&cmd_route_ls_route_string,
+		(void *)&cmd_route_ls_ls_string,
+		NULL,
+	},
+};
+
+/*
+ * arp add
+ */
 
-		int key_found;
+struct cmd_arp_add_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t arp_string;
+	cmdline_fixed_string_t add_string;
+	uint32_t port_id;
+	cmdline_ipaddr_t ip;
+	struct ether_addr macaddr;
 
-		result = rte_pipeline_table_entry_delete(p, arp_table_id,
-			&arp_key, &key_found, NULL);
-		break;
+};
+
+static void
+cmd_arp_add_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_arp_add_result *params = parsed_result;
+	struct app_params *app = data;
+
+	struct pipeline_routing_arp_key key;
+	int status;
+
+	key.type = PIPELINE_ROUTING_ARP_IPV4;
+	key.key.ipv4.port_id = params->port_id;
+	key.key.ipv4.ip = rte_cpu_to_be_32(params->ip.addr.ipv4.s_addr);
+
+	status = app_pipeline_routing_add_arp_entry(app,
+		params->p,
+		&key,
+		&params->macaddr);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
 
-	default:
-		rte_panic("RT Unrecognized message type (%u)\n", req->type);
+static cmdline_parse_token_string_t cmd_arp_add_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_arp_add_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_add_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_arp_add_arp_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_result, arp_string, "arp");
+
+static cmdline_parse_token_string_t cmd_arp_add_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_result, add_string, "add");
+
+static cmdline_parse_token_num_t cmd_arp_add_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_add_result, port_id, UINT32);
+
+static cmdline_parse_token_ipaddr_t cmd_arp_add_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_arp_add_result, ip);
+
+static cmdline_parse_token_etheraddr_t cmd_arp_add_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_arp_add_result, macaddr);
+
+static cmdline_parse_inst_t cmd_arp_add = {
+	.f = cmd_arp_add_parsed,
+	.data = NULL,
+	.help_str = "ARP add",
+	.tokens = {
+		(void *)&cmd_arp_add_p_string,
+		(void *)&cmd_arp_add_p,
+		(void *)&cmd_arp_add_arp_string,
+		(void *)&cmd_arp_add_add_string,
+		(void *)&cmd_arp_add_port_id,
+		(void *)&cmd_arp_add_ip,
+		(void *)&cmd_arp_add_macaddr,
+		NULL,
+	},
+};
+
+/*
+ * arp del
+ */
+
+struct cmd_arp_del_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t arp_string;
+	cmdline_fixed_string_t del_string;
+	uint32_t port_id;
+	cmdline_ipaddr_t ip;
+};
+
+static void
+cmd_arp_del_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_arp_del_result *params = parsed_result;
+	struct app_params *app = data;
+
+	struct pipeline_routing_arp_key key;
+	int status;
+
+	key.type = PIPELINE_ROUTING_ARP_IPV4;
+	key.key.ipv4.ip = rte_cpu_to_be_32(params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.port_id = params->port_id;
+
+	status = app_pipeline_routing_delete_arp_entry(app, params->p, &key);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
 	}
+}
+
+static cmdline_parse_token_string_t cmd_arp_del_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_arp_del_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_del_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_arp_del_arp_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_result, arp_string, "arp");
+
+static cmdline_parse_token_string_t cmd_arp_del_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_result, del_string, "del");
+
+static cmdline_parse_token_num_t cmd_arp_del_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_del_result, port_id, UINT32);
+
+static cmdline_parse_token_ipaddr_t cmd_arp_del_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_arp_del_result, ip);
+
+static cmdline_parse_inst_t cmd_arp_del = {
+	.f = cmd_arp_del_parsed,
+	.data = NULL,
+	.help_str = "ARP delete",
+	.tokens = {
+		(void *)&cmd_arp_del_p_string,
+		(void *)&cmd_arp_del_p,
+		(void *)&cmd_arp_del_arp_string,
+		(void *)&cmd_arp_del_del_string,
+		(void *)&cmd_arp_del_port_id,
+		(void *)&cmd_arp_del_ip,
+		NULL,
+	},
+};
 
-	/* Fill in response message */
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	resp->result = result;
+/*
+ * arp add default
+ */
+
+struct cmd_arp_add_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t arp_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t default_string;
+	uint32_t port_id;
+};
 
-	/* Send response */
-	do {
-		result = rte_ring_sp_enqueue(ring_resp, msg);
-	} while (result == -ENOBUFS);
+static void
+cmd_arp_add_default_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_arp_add_default_result *params = parsed_result;
+	struct app_params *app = data;
+
+	int status;
+
+	status = app_pipeline_routing_add_default_arp_entry(app,
+		params->p,
+		params->port_id);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_arp_add_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_default_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_arp_add_default_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_add_default_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_arp_add_default_arp_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_default_result, arp_string,
+	"arp");
+
+static cmdline_parse_token_string_t cmd_arp_add_default_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_default_result, add_string,
+	"add");
+
+static cmdline_parse_token_string_t cmd_arp_add_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_add_default_result,
+		default_string, "default");
+
+static cmdline_parse_token_num_t cmd_arp_add_default_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_add_default_result, port_id,
+	UINT32);
+
+static cmdline_parse_inst_t cmd_arp_add_default = {
+	.f = cmd_arp_add_default_parsed,
+	.data = NULL,
+	.help_str = "ARP add default",
+	.tokens = {
+		(void *)&cmd_arp_add_default_p_string,
+		(void *)&cmd_arp_add_default_p,
+		(void *)&cmd_arp_add_default_arp_string,
+		(void *)&cmd_arp_add_default_add_string,
+		(void *)&cmd_arp_add_default_default_string,
+		(void *)&cmd_arp_add_default_port_id,
+		NULL,
+	},
+};
+
+/*
+ * arp del default
+ */
+
+struct cmd_arp_del_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t arp_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t default_string;
+};
+
+static void
+cmd_arp_del_default_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_arp_del_default_result *params = parsed_result;
+	struct app_params *app = data;
+
+	int status;
+
+	status = app_pipeline_routing_delete_default_arp_entry(app, params->p);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_arp_del_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_default_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_arp_del_default_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_del_default_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_arp_del_default_arp_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_default_result, arp_string,
+	"arp");
+
+static cmdline_parse_token_string_t cmd_arp_del_default_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_default_result, del_string,
+	"del");
+
+static cmdline_parse_token_string_t cmd_arp_del_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_del_default_result,
+		default_string, "default");
+
+static cmdline_parse_inst_t cmd_arp_del_default = {
+	.f = cmd_arp_del_default_parsed,
+	.data = NULL,
+	.help_str = "ARP delete default",
+	.tokens = {
+		(void *)&cmd_arp_del_default_p_string,
+		(void *)&cmd_arp_del_default_p,
+		(void *)&cmd_arp_del_default_arp_string,
+		(void *)&cmd_arp_del_default_del_string,
+		(void *)&cmd_arp_del_default_default_string,
+		NULL,
+	},
+};
+
+/*
+ * arp ls
+ */
+
+struct cmd_arp_ls_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t arp_string;
+	cmdline_fixed_string_t ls_string;
+};
+
+static void
+cmd_arp_ls_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_arp_ls_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing *p;
+
+	p = app_pipeline_data_fe(app, params->p);
+	if (p == NULL)
+		return;
+
+	app_pipeline_routing_arp_ls(app, params->p);
 }
+
+static cmdline_parse_token_string_t cmd_arp_ls_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_ls_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_arp_ls_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_arp_ls_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_arp_ls_arp_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_ls_result, arp_string,
+	"arp");
+
+static cmdline_parse_token_string_t cmd_arp_ls_ls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_arp_ls_result, ls_string,
+	"ls");
+
+static cmdline_parse_inst_t cmd_arp_ls = {
+	.f = cmd_arp_ls_parsed,
+	.data = NULL,
+	.help_str = "ARP list",
+	.tokens = {
+		(void *)&cmd_arp_ls_p_string,
+		(void *)&cmd_arp_ls_p,
+		(void *)&cmd_arp_ls_arp_string,
+		(void *)&cmd_arp_ls_ls_string,
+		NULL,
+	},
+};
+
+static cmdline_parse_ctx_t pipeline_cmds[] = {
+	(cmdline_parse_inst_t *)&cmd_route_add,
+	(cmdline_parse_inst_t *)&cmd_route_del,
+	(cmdline_parse_inst_t *)&cmd_route_add_default,
+	(cmdline_parse_inst_t *)&cmd_route_del_default,
+	(cmdline_parse_inst_t *)&cmd_route_ls,
+	(cmdline_parse_inst_t *)&cmd_arp_add,
+	(cmdline_parse_inst_t *)&cmd_arp_del,
+	(cmdline_parse_inst_t *)&cmd_arp_add_default,
+	(cmdline_parse_inst_t *)&cmd_arp_del_default,
+	(cmdline_parse_inst_t *)&cmd_arp_ls,
+	NULL,
+};
+
+static struct pipeline_fe_ops pipeline_routing_fe_ops = {
+	.f_init = pipeline_routing_init,
+	.f_free = app_pipeline_routing_free,
+	.cmds = pipeline_cmds,
+};
+
+struct pipeline_type pipeline_routing = {
+	.name = "ROUTING",
+	.be_ops = &pipeline_routing_be_ops,
+	.fe_ops = &pipeline_routing_fe_ops,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.h b/examples/ip_pipeline/pipeline/pipeline_routing.h
new file mode 100644
index 0000000..e1016f9
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.h
@@ -0,0 +1,99 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_ROUTING_H__
+#define __INCLUDE_PIPELINE_ROUTING_H__
+
+#include "pipeline.h"
+#include "pipeline_routing_be.h"
+
+/*
+ * Route
+ */
+
+struct app_pipeline_routing_route_params {
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+	uint32_t ip; /* IP address for the next hop (only for remote routes) */
+};
+
+int
+app_pipeline_routing_add_route(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_routing_route_key *key,
+	struct app_pipeline_routing_route_params *route_params);
+
+int
+app_pipeline_routing_delete_route(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_routing_route_key *key);
+
+int
+app_pipeline_routing_add_default_route(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id);
+
+int
+app_pipeline_routing_delete_default_route(struct app_params *app,
+	uint32_t pipeline_id);
+
+/*
+ * ARP
+ */
+
+int
+app_pipeline_routing_add_arp_entry(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_routing_arp_key *key,
+	struct ether_addr *macaddr);
+
+int
+app_pipeline_routing_delete_arp_entry(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_routing_arp_key *key);
+
+int
+app_pipeline_routing_add_default_arp_entry(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id);
+
+int
+app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
+	uint32_t pipeline_id);
+
+/*
+ * Pipeline type
+ */
+extern struct pipeline_type pipeline_routing;
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.c b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
new file mode 100644
index 0000000..1e817dd
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
@@ -0,0 +1,869 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_ip.h>
+#include <rte_byteorder.h>
+#include <rte_table_lpm.h>
+#include <rte_table_hash.h>
+#include <rte_pipeline.h>
+
+#include "pipeline_routing_be.h"
+#include "pipeline_actions_common.h"
+#include "hash_func.h"
+
+struct pipeline_routing {
+	struct pipeline p;
+	pipeline_msg_req_handler custom_handlers[PIPELINE_ROUTING_MSG_REQS];
+
+	uint32_t n_routes;
+	uint32_t n_arp_entries;
+	uint32_t ip_da_offset;
+	uint32_t arp_key_offset;
+} __rte_cache_aligned;
+
+static void *
+pipeline_routing_msg_req_custom_handler(struct pipeline *p, void *msg);
+
+static pipeline_msg_req_handler handlers[] = {
+	[PIPELINE_MSG_REQ_PING] =
+		pipeline_msg_req_ping_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_IN] =
+		pipeline_msg_req_stats_port_in_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_OUT] =
+		pipeline_msg_req_stats_port_out_handler,
+	[PIPELINE_MSG_REQ_STATS_TABLE] =
+		pipeline_msg_req_stats_table_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_ENABLE] =
+		pipeline_msg_req_port_in_enable_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_DISABLE] =
+		pipeline_msg_req_port_in_disable_handler,
+	[PIPELINE_MSG_REQ_CUSTOM] =
+		pipeline_routing_msg_req_custom_handler,
+};
+
+static void *
+pipeline_routing_msg_req_route_add_handler(struct pipeline *p,
+	void *msg);
+
+static void *
+pipeline_routing_msg_req_route_del_handler(struct pipeline *p,
+	void *msg);
+
+static void *
+pipeline_routing_msg_req_route_add_default_handler(struct pipeline *p,
+	void *msg);
+
+static void *
+pipeline_routing_msg_req_route_del_default_handler(struct pipeline *p,
+	void *msg);
+
+static void *
+pipeline_routing_msg_req_arp_add_handler(struct pipeline *p,
+	void *msg);
+
+static void *
+pipeline_routing_msg_req_arp_del_handler(struct pipeline *p,
+	void *msg);
+
+static void *
+pipeline_routing_msg_req_arp_add_default_handler(struct pipeline *p,
+	void *msg);
+
+static void *
+pipeline_routing_msg_req_arp_del_default_handler(struct pipeline *p,
+	void *msg);
+
+static pipeline_msg_req_handler custom_handlers[] = {
+	[PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD] =
+		pipeline_routing_msg_req_route_add_handler,
+	[PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL] =
+		pipeline_routing_msg_req_route_del_handler,
+	[PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD_DEFAULT] =
+		pipeline_routing_msg_req_route_add_default_handler,
+	[PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL_DEFAULT] =
+		pipeline_routing_msg_req_route_del_default_handler,
+	[PIPELINE_ROUTING_MSG_REQ_ARP_ADD] =
+		pipeline_routing_msg_req_arp_add_handler,
+	[PIPELINE_ROUTING_MSG_REQ_ARP_DEL] =
+		pipeline_routing_msg_req_arp_del_handler,
+	[PIPELINE_ROUTING_MSG_REQ_ARP_ADD_DEFAULT] =
+		pipeline_routing_msg_req_arp_add_default_handler,
+	[PIPELINE_ROUTING_MSG_REQ_ARP_DEL_DEFAULT] =
+		pipeline_routing_msg_req_arp_del_default_handler,
+};
+
+/*
+ * Routing table
+ */
+struct routing_table_entry {
+	struct rte_pipeline_table_entry head;
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+};
+
+static inline void
+pkt_work_routing(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	struct routing_table_entry *entry =
+		(struct routing_table_entry *) table_entry;
+	struct pipeline_routing *p_rt = arg;
+
+	struct pipeline_routing_arp_key_ipv4 *arp_key =
+		(struct pipeline_routing_arp_key_ipv4 *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->arp_key_offset);
+	uint32_t ip = RTE_MBUF_METADATA_UINT32(pkt, p_rt->ip_da_offset);
+
+	arp_key->port_id = entry->port_id;
+	arp_key->ip = entry->ip;
+	if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+		arp_key->ip = ip;
+}
+
+static inline void
+pkt4_work_routing(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	struct routing_table_entry *entry0 =
+		(struct routing_table_entry *) table_entries[0];
+	struct routing_table_entry *entry1 =
+		(struct routing_table_entry *) table_entries[1];
+	struct routing_table_entry *entry2 =
+		(struct routing_table_entry *) table_entries[2];
+	struct routing_table_entry *entry3 =
+		(struct routing_table_entry *) table_entries[3];
+	struct pipeline_routing *p_rt = arg;
+
+	struct pipeline_routing_arp_key_ipv4 *arp_key0 =
+		(struct pipeline_routing_arp_key_ipv4 *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0], p_rt->arp_key_offset);
+	struct pipeline_routing_arp_key_ipv4 *arp_key1 =
+		(struct pipeline_routing_arp_key_ipv4 *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1], p_rt->arp_key_offset);
+	struct pipeline_routing_arp_key_ipv4 *arp_key2 =
+		(struct pipeline_routing_arp_key_ipv4 *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2], p_rt->arp_key_offset);
+	struct pipeline_routing_arp_key_ipv4 *arp_key3 =
+		(struct pipeline_routing_arp_key_ipv4 *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3], p_rt->arp_key_offset);
+
+	uint32_t ip0 = RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->ip_da_offset);
+	uint32_t ip1 = RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->ip_da_offset);
+	uint32_t ip2 = RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->ip_da_offset);
+	uint32_t ip3 = RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->ip_da_offset);
+
+	arp_key0->port_id = entry0->port_id;
+	arp_key1->port_id = entry1->port_id;
+	arp_key2->port_id = entry2->port_id;
+	arp_key3->port_id = entry3->port_id;
+
+	arp_key0->ip = entry0->ip;
+	if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+		arp_key0->ip = ip0;
+
+	arp_key1->ip = entry1->ip;
+	if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+		arp_key1->ip = ip1;
+
+	arp_key2->ip = entry2->ip;
+	if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+		arp_key2->ip = ip2;
+
+	arp_key3->ip = entry3->ip;
+	if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+		arp_key3->ip = ip3;
+}
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit,
+	pkt_work_routing,
+	pkt4_work_routing);
+
+/*
+ * ARP table
+ */
+struct arp_table_entry {
+	struct rte_pipeline_table_entry head;
+	uint64_t macaddr;
+};
+
+static inline void
+pkt_work_arp(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	__rte_unused void *arg)
+{
+	struct arp_table_entry *entry = (struct arp_table_entry *) table_entry;
+
+	/* Read: pkt buffer - mbuf */
+	uint8_t *raw = rte_pktmbuf_mtod(pkt, uint8_t *);
+
+	/* Read: table entry */
+	uint64_t mac_addr_dst = entry->macaddr;
+	uint64_t mac_addr_src = 0;
+
+	/* Compute: Ethernet header */
+	uint64_t slab0 = mac_addr_dst | (mac_addr_src << 48);
+	uint32_t slab1 = mac_addr_src >> 16;
+
+	/* Write: pkt buffer - pkt headers */
+	*((uint64_t *) raw) = slab0;
+	*((uint32_t *) (raw + 8)) = slab1;
+}
+
+static inline void
+pkt4_work_arp(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	__rte_unused void *arg)
+{
+	struct arp_table_entry *entry0 =
+		(struct arp_table_entry *) table_entries[0];
+	struct arp_table_entry *entry1 =
+		(struct arp_table_entry *) table_entries[1];
+	struct arp_table_entry *entry2 =
+		(struct arp_table_entry *) table_entries[2];
+	struct arp_table_entry *entry3 =
+		(struct arp_table_entry *) table_entries[3];
+
+	/* Read: pkt buffer - mbuf */
+	uint8_t *raw0 = rte_pktmbuf_mtod(pkts[0], uint8_t *);
+	uint8_t *raw1 = rte_pktmbuf_mtod(pkts[1], uint8_t *);
+	uint8_t *raw2 = rte_pktmbuf_mtod(pkts[2], uint8_t *);
+	uint8_t *raw3 = rte_pktmbuf_mtod(pkts[3], uint8_t *);
+
+	/* Read: table entry */
+	uint64_t mac_addr_dst0 = entry0->macaddr;
+	uint64_t mac_addr_dst1 = entry1->macaddr;
+	uint64_t mac_addr_dst2 = entry2->macaddr;
+	uint64_t mac_addr_dst3 = entry3->macaddr;
+
+	uint64_t mac_addr_src0 = 0;
+	uint64_t mac_addr_src1 = 0;
+	uint64_t mac_addr_src2 = 0;
+	uint64_t mac_addr_src3 = 0;
+
+	/* Compute: Ethernet header */
+	uint64_t pkt0_slab0 = mac_addr_dst0 | (mac_addr_src0 << 48);
+	uint64_t pkt1_slab0 = mac_addr_dst1 | (mac_addr_src1 << 48);
+	uint64_t pkt2_slab0 = mac_addr_dst2 | (mac_addr_src2 << 48);
+	uint64_t pkt3_slab0 = mac_addr_dst3 | (mac_addr_src3 << 48);
+
+	uint32_t pkt0_slab1 = mac_addr_src0 >> 16;
+	uint32_t pkt1_slab1 = mac_addr_src1 >> 16;
+	uint32_t pkt2_slab1 = mac_addr_src2 >> 16;
+	uint32_t pkt3_slab1 = mac_addr_src3 >> 16;
+
+	/* Write: pkt buffer - pkt headers */
+	*((uint64_t *) raw0) = pkt0_slab0;
+	*((uint32_t *) (raw0 + 8)) = pkt0_slab1;
+	*((uint64_t *) raw1) = pkt1_slab0;
+	*((uint32_t *) (raw1 + 8)) = pkt1_slab1;
+	*((uint64_t *) raw2) = pkt2_slab0;
+	*((uint32_t *) (raw2 + 8)) = pkt2_slab1;
+	*((uint64_t *) raw3) = pkt3_slab0;
+	*((uint32_t *) (raw3 + 8)) = pkt3_slab1;
+}
+
+PIPELINE_TABLE_AH_HIT(arp_table_ah_hit,
+	pkt_work_arp,
+	pkt4_work_arp);
+
+static int
+pipeline_routing_parse_args(struct pipeline_routing *p,
+	struct pipeline_params *params)
+{
+	uint32_t n_routes_present = 0;
+	uint32_t n_arp_entries_present = 0;
+	uint32_t ip_da_offset_present = 0;
+	uint32_t arp_key_offset_present = 0;
+	uint32_t i;
+
+	for (i = 0; i < params->n_args; i++) {
+		char *arg_name = params->args_name[i];
+		char *arg_value = params->args_value[i];
+
+		/* n_routes */
+		if (strcmp(arg_name, "n_routes") == 0) {
+			if (n_routes_present)
+				return -1;
+			n_routes_present = 1;
+
+			p->n_routes = atoi(arg_value);
+			if (p->n_routes == 0)
+				return -1;
+
+			continue;
+		}
+
+		/* n_arp_entries */
+		if (strcmp(arg_name, "n_arp_entries") == 0) {
+			if (n_arp_entries_present)
+				return -1;
+			n_arp_entries_present = 1;
+
+			p->n_arp_entries = atoi(arg_value);
+			if (p->n_arp_entries == 0)
+				return -1;
+
+			continue;
+		}
+
+		/* ip_da_offset */
+		if (strcmp(arg_name, "ip_da_offset") == 0) {
+			if (ip_da_offset_present)
+				return -1;
+			ip_da_offset_present = 1;
+
+			p->ip_da_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* arp_key_offset */
+		if (strcmp(arg_name, "arp_key_offset") == 0) {
+			if (arp_key_offset_present)
+				return -1;
+			arp_key_offset_present = 1;
+
+			p->arp_key_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* any other */
+		return -1;
+	}
+
+	/* Check that mandatory arguments are present */
+	if ((n_routes_present == 0) ||
+		(n_arp_entries_present == 0) ||
+		(ip_da_offset_present == 0) ||
+		(n_arp_entries_present && (arp_key_offset_present == 0)))
+		return -1;
+
+	return 0;
+}
+
+static void *
+pipeline_routing_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct pipeline *p;
+	struct pipeline_routing *p_rt;
+	uint32_t size, i;
+
+	/* Check input arguments */
+	if ((params == NULL) ||
+		(params->n_ports_in == 0) ||
+		(params->n_ports_out == 0))
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct pipeline_routing));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	p_rt = (struct pipeline_routing *) p;
+	if (p == NULL)
+		return NULL;
+
+	strcpy(p->name, params->name);
+	p->log_level = params->log_level;
+
+	PLOG(p, HIGH, "Routing");
+
+	/* Parse arguments */
+	if (pipeline_routing_parse_args(p_rt, params))
+		return NULL;
+
+	/* Pipeline */
+	{
+		struct rte_pipeline_params pipeline_params = {
+			.name = params->name,
+			.socket_id = params->socket_id,
+			.offset_port_id = 0,
+		};
+
+		p->p = rte_pipeline_create(&pipeline_params);
+		if (p->p == NULL) {
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Input ports */
+	p->n_ports_in = params->n_ports_in;
+	for (i = 0; i < p->n_ports_in; i++) {
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = pipeline_port_in_params_get_ops(
+				&params->port_in[i]),
+			.arg_create = pipeline_port_in_params_convert(
+				&params->port_in[i]),
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = params->port_in[i].burst_size,
+		};
+
+		int status = rte_pipeline_port_in_create(p->p,
+			&port_params,
+			&p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Output ports */
+	p->n_ports_out = params->n_ports_out;
+	for (i = 0; i < p->n_ports_out; i++) {
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = pipeline_port_out_params_get_ops(
+				&params->port_out[i]),
+			.arg_create = pipeline_port_out_params_convert(
+				&params->port_out[i]),
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		int status = rte_pipeline_port_out_create(p->p,
+			&port_params,
+			&p->port_out_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Routing table */
+	p->n_tables = 1;
+	{
+		struct rte_table_lpm_params table_lpm_params = {
+			.n_rules = p_rt->n_routes,
+			.entry_unique_size = sizeof(struct routing_table_entry),
+			.offset = p_rt->ip_da_offset,
+		};
+
+		struct rte_pipeline_table_params table_params = {
+				.ops = &rte_table_lpm_ops,
+				.arg_create = &table_lpm_params,
+				.f_action_hit = routing_table_ah_hit,
+				.f_action_miss = NULL,
+				.arg_ah = p_rt,
+				.action_data_size =
+					sizeof(struct routing_table_entry) -
+					sizeof(struct rte_pipeline_table_entry),
+			};
+
+		int status;
+
+		status = rte_pipeline_table_create(p->p,
+			&table_params,
+			&p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* ARP table configuration */
+	if (p_rt->n_arp_entries) {
+		struct rte_table_hash_key8_ext_params table_arp_params = {
+			.n_entries = p_rt->n_arp_entries,
+			.n_entries_ext = p_rt->n_arp_entries,
+			.f_hash = hash_default_key8,
+			.seed = 0,
+			.signature_offset = 0, /* Unused */
+			.key_offset = p_rt->arp_key_offset,
+		};
+
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_hash_key8_ext_dosig_ops,
+			.arg_create = &table_arp_params,
+			.f_action_hit = arp_table_ah_hit,
+			.f_action_miss = NULL,
+			.arg_ah = p_rt,
+			.action_data_size = sizeof(struct arp_table_entry) -
+				sizeof(struct rte_pipeline_table_entry),
+		};
+
+		int status;
+
+		status = rte_pipeline_table_create(p->p,
+			&table_params,
+			&p->table_id[1]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+
+		p->n_tables++;
+	}
+
+	/* Connecting input ports to tables */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_connect_to_table(p->p,
+			p->port_in_id[i],
+			p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Enable input ports */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_enable(p->p,
+			p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p->p) < 0) {
+		rte_pipeline_free(p->p);
+		rte_free(p);
+		return NULL;
+	}
+
+	/* Message queues */
+	p->n_msgq = params->n_msgq;
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_in[i] = params->msgq_in[i];
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_out[i] = params->msgq_out[i];
+
+	/* Message handlers */
+	memcpy(p->handlers, handlers, sizeof(p->handlers));
+	memcpy(p_rt->custom_handlers,
+		custom_handlers,
+		sizeof(p_rt->custom_handlers));
+
+	return p;
+}
+
+static int
+pipeline_routing_free(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	rte_pipeline_free(p->p);
+	rte_free(p);
+	return 0;
+}
+
+static int
+pipeline_routing_track(void *pipeline,
+	__rte_unused uint32_t port_in,
+	uint32_t *port_out)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if ((p == NULL) ||
+		(port_in >= p->n_ports_in) ||
+		(port_out == NULL))
+		return -1;
+
+	if (p->n_ports_in == 1) {
+		*port_out = 0;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int
+pipeline_routing_timer(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	pipeline_msg_req_handle(p);
+	rte_pipeline_flush(p->p);
+
+	return 0;
+}
+
+void *
+pipeline_routing_msg_req_custom_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_routing *p_rt = (struct pipeline_routing *) p;
+	struct pipeline_custom_msg_req *req = msg;
+	pipeline_msg_req_handler f_handle;
+
+	f_handle = (req->subtype < PIPELINE_ROUTING_MSG_REQS) ?
+		p_rt->custom_handlers[req->subtype] :
+		pipeline_msg_req_invalid_handler;
+
+	if (f_handle == NULL)
+		f_handle = pipeline_msg_req_invalid_handler;
+
+	return f_handle(p, req);
+}
+
+void *
+pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_routing_route_add_msg_req *req = msg;
+	struct pipeline_routing_route_add_msg_rsp *rsp = msg;
+
+	struct rte_table_lpm_key key = {
+		.ip = req->key.key.ipv4.ip,
+		.depth = req->key.key.ipv4.depth,
+	};
+
+	struct routing_table_entry entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_TABLE,
+			{.table_id = p->table_id[1]},
+		},
+
+		.flags = req->flags,
+		.port_id = req->port_id,
+		.ip = rte_bswap32(req->ip),
+	};
+
+	if (req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) {
+		rsp->status = -1;
+		return rsp;
+	}
+
+	rsp->status = rte_pipeline_table_entry_add(p->p,
+		p->table_id[0],
+		&key,
+		(struct rte_pipeline_table_entry *) &entry,
+		&rsp->key_found,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+void *
+pipeline_routing_msg_req_route_del_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_routing_route_delete_msg_req *req = msg;
+	struct pipeline_routing_route_delete_msg_rsp *rsp = msg;
+
+	struct rte_table_lpm_key key = {
+		.ip = req->key.key.ipv4.ip,
+		.depth = req->key.key.ipv4.depth,
+	};
+
+	if (req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) {
+		rsp->status = -1;
+		return rsp;
+	}
+
+	rsp->status = rte_pipeline_table_entry_delete(p->p,
+		p->table_id[0],
+		&key,
+		&rsp->key_found,
+		NULL);
+
+	return rsp;
+}
+
+void *
+pipeline_routing_msg_req_route_add_default_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_routing_route_add_default_msg_req *req = msg;
+	struct pipeline_routing_route_add_default_msg_rsp *rsp = msg;
+
+	struct routing_table_entry default_entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->port_id]},
+		},
+
+		.flags = 0,
+		.port_id = 0,
+		.ip = 0,
+	};
+
+	rsp->status = rte_pipeline_table_default_entry_add(p->p,
+		p->table_id[0],
+		(struct rte_pipeline_table_entry *) &default_entry,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+void *
+pipeline_routing_msg_req_route_del_default_handler(struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_routing_route_delete_default_msg_rsp *rsp = msg;
+
+	rsp->status = rte_pipeline_table_default_entry_delete(p->p,
+		p->table_id[0],
+		NULL);
+
+	return rsp;
+}
+
+void *
+pipeline_routing_msg_req_arp_add_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_routing_arp_add_msg_req *req = msg;
+	struct pipeline_routing_arp_add_msg_rsp *rsp = msg;
+
+	struct pipeline_routing_arp_key_ipv4 key = {
+		.port_id = req->key.key.ipv4.port_id,
+		.ip = rte_bswap32(req->key.key.ipv4.ip),
+	};
+
+	struct arp_table_entry entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->key.key.ipv4.port_id]},
+		},
+
+		.macaddr = 0, /* set below */
+	};
+
+	if (req->key.type != PIPELINE_ROUTING_ARP_IPV4) {
+		rsp->status = -1;
+		return rsp;
+	}
+
+	*((struct ether_addr *) &entry.macaddr) = req->macaddr;
+
+	rsp->status = rte_pipeline_table_entry_add(p->p,
+		p->table_id[1],
+		&key,
+		(struct rte_pipeline_table_entry *) &entry,
+		&rsp->key_found,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+void *
+pipeline_routing_msg_req_arp_del_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_routing_arp_delete_msg_req *req = msg;
+	struct pipeline_routing_arp_delete_msg_rsp *rsp = msg;
+
+	struct pipeline_routing_arp_key_ipv4 key = {
+		.port_id = req->key.key.ipv4.port_id,
+		.ip = rte_bswap32(req->key.key.ipv4.ip),
+	};
+
+	if (req->key.type != PIPELINE_ROUTING_ARP_IPV4) {
+		rsp->status = -1;
+		return rsp;
+	}
+
+	rsp->status = rte_pipeline_table_entry_delete(p->p,
+		p->table_id[1],
+		&key,
+		&rsp->key_found,
+		NULL);
+
+	return rsp;
+}
+
+void *
+pipeline_routing_msg_req_arp_add_default_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_routing_arp_add_default_msg_req *req = msg;
+	struct pipeline_routing_arp_add_default_msg_rsp *rsp = msg;
+
+	struct arp_table_entry default_entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->port_id]},
+		},
+
+		.macaddr = 0,
+	};
+
+	rsp->status = rte_pipeline_table_default_entry_add(p->p,
+		p->table_id[1],
+		(struct rte_pipeline_table_entry *) &default_entry,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+void *
+pipeline_routing_msg_req_arp_del_default_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_routing_arp_delete_default_msg_rsp *rsp = msg;
+
+	rsp->status = rte_pipeline_table_default_entry_delete(p->p,
+		p->table_id[1],
+		NULL);
+
+	return rsp;
+}
+
+struct pipeline_be_ops pipeline_routing_be_ops = {
+	.f_init = pipeline_routing_init,
+	.f_free = pipeline_routing_free,
+	.f_run = NULL,
+	.f_timer = pipeline_routing_timer,
+	.f_track = pipeline_routing_track,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.h b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
new file mode 100644
index 0000000..45f37a1
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
@@ -0,0 +1,230 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_ROUTING_BE_H__
+#define __INCLUDE_PIPELINE_ROUTING_BE_H__
+
+#include <rte_ether.h>
+
+#include "pipeline_common_be.h"
+
+/*
+ * Route
+ */
+enum pipeline_routing_route_key_type {
+	PIPELINE_ROUTING_ROUTE_IPV4,
+};
+
+struct pipeline_routing_route_key_ipv4 {
+	uint32_t ip;
+	uint32_t depth;
+};
+
+struct pipeline_routing_route_key {
+	enum pipeline_routing_route_key_type type;
+	union {
+		struct pipeline_routing_route_key_ipv4 ipv4;
+	} key;
+};
+
+enum pipeline_routing_route_flags {
+	PIPELINE_ROUTING_ROUTE_LOCAL = 1 << 0, /* 0 = remote; 1 = local */
+};
+
+/*
+ * ARP
+ */
+enum pipeline_routing_arp_key_type {
+	PIPELINE_ROUTING_ARP_IPV4,
+};
+
+struct pipeline_routing_arp_key_ipv4 {
+	uint32_t port_id;
+	uint32_t ip;
+};
+
+struct pipeline_routing_arp_key {
+	enum pipeline_routing_arp_key_type type;
+	union {
+		struct pipeline_routing_arp_key_ipv4 ipv4;
+	} key;
+};
+
+/*
+ * Messages
+ */
+enum pipeline_routing_msg_req_type {
+	PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD,
+	PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL,
+	PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD_DEFAULT,
+	PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL_DEFAULT,
+	PIPELINE_ROUTING_MSG_REQ_ARP_ADD,
+	PIPELINE_ROUTING_MSG_REQ_ARP_DEL,
+	PIPELINE_ROUTING_MSG_REQ_ARP_ADD_DEFAULT,
+	PIPELINE_ROUTING_MSG_REQ_ARP_DEL_DEFAULT,
+	PIPELINE_ROUTING_MSG_REQS
+};
+
+/*
+ * MSG ROUTE ADD
+ */
+struct pipeline_routing_route_add_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+
+	/* key */
+	struct pipeline_routing_route_key key;
+
+	/* data */
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+};
+
+struct pipeline_routing_route_add_msg_rsp {
+	int status;
+	int key_found;
+	void *entry_ptr;
+};
+
+/*
+ * MSG ROUTE DELETE
+ */
+struct pipeline_routing_route_delete_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+
+	/* key */
+	struct pipeline_routing_route_key key;
+};
+
+struct pipeline_routing_route_delete_msg_rsp {
+	int status;
+	int key_found;
+};
+
+/*
+ * MSG ROUTE ADD DEFAULT
+ */
+struct pipeline_routing_route_add_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+
+	/* data */
+	uint32_t port_id;
+};
+
+struct pipeline_routing_route_add_default_msg_rsp {
+	int status;
+	void *entry_ptr;
+};
+
+/*
+ * MSG ROUTE DELETE DEFAULT
+ */
+struct pipeline_routing_route_delete_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+};
+
+struct pipeline_routing_route_delete_default_msg_rsp {
+	int status;
+};
+
+/*
+ * MSG ARP ADD
+ */
+struct pipeline_routing_arp_add_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+
+	/* key */
+	struct pipeline_routing_arp_key key;
+
+	/* data */
+	struct ether_addr macaddr;
+};
+
+struct pipeline_routing_arp_add_msg_rsp {
+	int status;
+	int key_found;
+	void *entry_ptr;
+};
+
+/*
+ * MSG ARP DELETE
+ */
+struct pipeline_routing_arp_delete_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+
+	/* key */
+	struct pipeline_routing_arp_key key;
+};
+
+struct pipeline_routing_arp_delete_msg_rsp {
+	int status;
+	int key_found;
+};
+
+/*
+ * MSG ARP ADD DEFAULT
+ */
+struct pipeline_routing_arp_add_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+
+	/* data */
+	uint32_t port_id;
+};
+
+struct pipeline_routing_arp_add_default_msg_rsp {
+	int status;
+	void *entry_ptr;
+};
+
+/*
+ * MSG ARP DELETE DEFAULT
+ */
+struct pipeline_routing_arp_delete_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_routing_msg_req_type subtype;
+};
+
+struct pipeline_routing_arp_delete_default_msg_rsp {
+	int status;
+};
+
+extern struct pipeline_be_ops pipeline_routing_be_ops;
+
+#endif
-- 
1.7.9.5

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

* [dpdk-dev] [PATCH v6 11/11] ip_pipeline: added new implementation of flow classification pipeline
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (9 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 10/11] ip_pipeline: added new implementation of routing pipeline Maciej Gajdzica
@ 2015-07-07  8:09 ` Maciej Gajdzica
  2015-07-07  8:27 ` [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Thomas Monjalon
  2015-07-07  9:23 ` Dumitrescu, Cristian
  12 siblings, 0 replies; 21+ messages in thread
From: Maciej Gajdzica @ 2015-07-07  8:09 UTC (permalink / raw)
  To: dev

Flow classification pipeline implementation is split to two files.
pipeline_flow_classification.c file handles front-end functions (cli
commands parsing) pipeline_flow_classification_ops.c contains
implementation of functions done by pipeline (back-end).

Signed-off-by: Maciej Gajdzica <maciejx.t.gajdzica@intel.com>
---
 examples/ip_pipeline/Makefile                      |    2 +
 examples/ip_pipeline/init.c                        |    2 +
 .../pipeline/pipeline_flow_classification.c        | 2209 ++++++++++++++++++--
 .../pipeline/pipeline_flow_classification.h        |  105 +
 .../pipeline/pipeline_flow_classification_be.c     |  589 ++++++
 .../pipeline/pipeline_flow_classification_be.h     |  140 ++
 6 files changed, 2820 insertions(+), 227 deletions(-)
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_classification_be.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index a2881a6..f3ff1ec 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -64,6 +64,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_passthrough.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_classification_be.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_classification.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
 
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 728ce9a..3f9c68d 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -48,6 +48,7 @@
 #include "pipeline_master.h"
 #include "pipeline_passthrough.h"
 #include "pipeline_firewall.h"
+#include "pipeline_flow_classification.h"
 #include "pipeline_routing.h"
 
 #define APP_NAME_SIZE	32
@@ -1289,6 +1290,7 @@ int app_init(struct app_params *app)
 	app_pipeline_common_cmd_push(app);
 	app_pipeline_type_register(app, &pipeline_master);
 	app_pipeline_type_register(app, &pipeline_passthrough);
+	app_pipeline_type_register(app, &pipeline_flow_classification);
 	app_pipeline_type_register(app, &pipeline_firewall);
 	app_pipeline_type_register(app, &pipeline_routing);
 
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_classification.c b/examples/ip_pipeline/pipeline/pipeline_flow_classification.c
index cc0cbf1..4b82180 100644
--- a/examples/ip_pipeline/pipeline/pipeline_flow_classification.c
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_classification.c
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -32,275 +32,2030 @@
  */
 
 #include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
 
+#include <rte_common.h>
+#include <rte_hexdump.h>
 #include <rte_malloc.h>
-#include <rte_log.h>
-#include <rte_ethdev.h>
-#include <rte_ether.h>
-#include <rte_ip.h>
-#include <rte_byteorder.h>
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_etheraddr.h>
 
-#include <rte_port_ring.h>
-#include <rte_table_hash.h>
-#include <rte_pipeline.h>
+#include "app.h"
+#include "pipeline_common_fe.h"
+#include "pipeline_flow_classification.h"
+#include "hash_func.h"
 
-#include "main.h"
+/*
+ * Key conversion
+ */
+
+struct pkt_key_qinq {
+	uint16_t ethertype_svlan;
+	uint16_t svlan;
+	uint16_t ethertype_cvlan;
+	uint16_t cvlan;
+} __attribute__((__packed__));
+
+struct pkt_key_ipv4_5tuple {
+	uint8_t ttl;
+	uint8_t proto;
+	uint16_t checksum;
+	uint32_t ip_src;
+	uint32_t ip_dst;
+	uint16_t port_src;
+	uint16_t port_dst;
+} __attribute__((__packed__));
+
+struct pkt_key_ipv6_5tuple {
+	uint16_t payload_length;
+	uint8_t proto;
+	uint8_t hop_limit;
+	uint8_t ip_src[16];
+	uint8_t ip_dst[16];
+	uint16_t port_src;
+	uint16_t port_dst;
+} __attribute__((__packed__));
+
+static int
+app_pipeline_fc_key_convert(struct pipeline_fc_key *key_in,
+	uint8_t *key_out,
+	uint32_t *signature)
+{
+	uint8_t buffer[PIPELINE_FC_FLOW_KEY_MAX_SIZE];
+	void *key_buffer = (key_out) ? key_out : buffer;
+
+	switch (key_in->type) {
+	case FLOW_KEY_QINQ:
+	{
+		struct pkt_key_qinq *qinq = key_buffer;
+
+		qinq->ethertype_svlan = 0;
+		qinq->svlan = rte_bswap16(key_in->key.qinq.svlan);
+		qinq->ethertype_cvlan = 0;
+		qinq->cvlan = rte_bswap16(key_in->key.qinq.cvlan);
+
+		if (signature)
+			*signature = (uint32_t) hash_default_key8(qinq, 8, 0);
+		return 0;
+	}
+
+	case FLOW_KEY_IPV4_5TUPLE:
+	{
+		struct pkt_key_ipv4_5tuple *ipv4 = key_buffer;
+
+		ipv4->ttl = 0;
+		ipv4->proto = key_in->key.ipv4_5tuple.proto;
+		ipv4->checksum = 0;
+		ipv4->ip_src = rte_bswap32(key_in->key.ipv4_5tuple.ip_src);
+		ipv4->ip_dst = rte_bswap32(key_in->key.ipv4_5tuple.ip_dst);
+		ipv4->port_src = rte_bswap16(key_in->key.ipv4_5tuple.port_src);
+		ipv4->port_dst = rte_bswap16(key_in->key.ipv4_5tuple.port_dst);
+
+		if (signature)
+			*signature = (uint32_t) hash_default_key16(ipv4, 16, 0);
+		return 0;
+	}
 
-struct app_core_fc_message_handle_params {
-	struct rte_ring *ring_req;
-	struct rte_ring *ring_resp;
+	case FLOW_KEY_IPV6_5TUPLE:
+	{
+		struct pkt_key_ipv6_5tuple *ipv6 = key_buffer;
+
+		memset(ipv6, 0, 64);
+		ipv6->payload_length = 0;
+		ipv6->proto = key_in->key.ipv6_5tuple.proto;
+		ipv6->hop_limit = 0;
+		memcpy(&ipv6->ip_src, &key_in->key.ipv6_5tuple.ip_src, 16);
+		memcpy(&ipv6->ip_dst, &key_in->key.ipv6_5tuple.ip_dst, 16);
+		ipv6->port_src = rte_bswap16(key_in->key.ipv6_5tuple.port_src);
+		ipv6->port_dst = rte_bswap16(key_in->key.ipv6_5tuple.port_dst);
+
+		if (signature)
+			*signature = (uint32_t) hash_default_key64(ipv6, 64, 0);
+		return 0;
+	}
+
+	default:
+		return -1;
+	}
+}
 
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
+/*
+ * Flow classification pipeline
+ */
+
+struct app_pipeline_fc_flow {
+	struct pipeline_fc_key key;
+	uint32_t port_id;
+	uint32_t signature;
+	void *entry_ptr;
+
+	TAILQ_ENTRY(app_pipeline_fc_flow) node;
 };
 
-static void
-app_message_handle(struct app_core_fc_message_handle_params *params);
+#define N_BUCKETS                                65536
+
+struct app_pipeline_fc {
+	/* Parameters */
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+
+	/* Flows */
+	TAILQ_HEAD(, app_pipeline_fc_flow) flows[N_BUCKETS];
+	uint32_t n_flows;
+
+	/* Default flow */
+	uint32_t default_flow_present;
+	uint32_t default_flow_port_id;
+	void *default_flow_entry_ptr;
+};
 
-static int app_flow_classification_table_init(
-	struct rte_pipeline *p,
-	uint32_t *port_out_id,
-	uint32_t table_id)
+static struct app_pipeline_fc_flow *
+app_pipeline_fc_flow_find(struct app_pipeline_fc *p,
+	struct pipeline_fc_key *key)
 {
-	struct app_flow_key flow_key;
+	struct app_pipeline_fc_flow *f;
+	uint32_t signature, bucket_id;
+
+	app_pipeline_fc_key_convert(key, NULL, &signature);
+	bucket_id = signature & (N_BUCKETS - 1);
+
+	TAILQ_FOREACH(f, &p->flows[bucket_id], node)
+		if ((signature == f->signature) &&
+			(memcmp(key,
+				&f->key,
+				sizeof(struct pipeline_fc_key)) == 0))
+			return f;
+
+	return NULL;
+}
+
+static void*
+app_pipeline_fc_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct app_pipeline_fc *p;
+	uint32_t size, i;
+
+	/* Check input arguments */
+	if ((params == NULL) ||
+		(params->n_ports_in == 0) ||
+		(params->n_ports_out == 0))
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct app_pipeline_fc));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		return NULL;
+
+	/* Initialization */
+	p->n_ports_in = params->n_ports_in;
+	p->n_ports_out = params->n_ports_out;
+
+	for (i = 0; i < N_BUCKETS; i++)
+		TAILQ_INIT(&p->flows[i]);
+	p->n_flows = 0;
+
+	return (void *) p;
+}
+
+static int
+app_pipeline_fc_free(void *pipeline)
+{
+	struct app_pipeline_fc *p = pipeline;
 	uint32_t i;
 
-	/* Add entries to tables */
-	for (i = 0; i < (1 << 24); i++) {
-		struct rte_pipeline_table_entry entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[i & (app.n_ports - 1)]},
-		};
-		struct rte_pipeline_table_entry *entry_ptr;
-		int key_found, status;
-
-		flow_key.ttl = 0;
-		flow_key.proto = 6; /* TCP */
-		flow_key.header_checksum = 0;
-		flow_key.ip_src = 0;
-		flow_key.ip_dst = rte_bswap32(i);
-		flow_key.port_src = 0;
-		flow_key.port_dst = 0;
-
-		status = rte_pipeline_table_entry_add(p, table_id,
-			(void *) &flow_key, &entry, &key_found, &entry_ptr);
-		if (status < 0)
-			rte_panic("Unable to add entry to table %u (%d)\n",
-				table_id, status);
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	for (i = 0; i < N_BUCKETS; i++)
+		while (!TAILQ_EMPTY(&p->flows[i])) {
+			struct app_pipeline_fc_flow *flow;
+
+			flow = TAILQ_FIRST(&p->flows[i]);
+			TAILQ_REMOVE(&p->flows[i], flow, node);
+			rte_free(flow);
+		}
+
+	rte_free(p);
+	return 0;
+}
+
+static int
+app_pipeline_fc_key_check(struct pipeline_fc_key *key)
+{
+	switch (key->type) {
+	case FLOW_KEY_QINQ:
+	{
+		uint16_t svlan = key->key.qinq.svlan;
+		uint16_t cvlan = key->key.qinq.cvlan;
+
+		if ((svlan & 0xF000) ||
+			(cvlan & 0xF000))
+			return -1;
+
+		return 0;
+	}
+
+	case FLOW_KEY_IPV4_5TUPLE:
+		return 0;
+
+	case FLOW_KEY_IPV6_5TUPLE:
+		return 0;
+
+	default:
+		return -1;
+	}
+}
+
+int
+app_pipeline_fc_add(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_fc_key *key,
+	uint32_t port_id)
+{
+	struct app_pipeline_fc *p;
+	struct app_pipeline_fc_flow *flow;
+
+	struct pipeline_fc_add_msg_req *req;
+	struct pipeline_fc_add_msg_rsp *rsp;
+
+	uint32_t signature;
+	int new_flow;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (port_id >= p->n_ports_out)
+		return -1;
+
+	if (app_pipeline_fc_key_check(key) != 0)
+		return -1;
+
+	/* Find existing flow or allocate new flow */
+	flow = app_pipeline_fc_flow_find(p, key);
+	new_flow = (flow == NULL);
+	if (flow == NULL) {
+		flow = rte_malloc(NULL, sizeof(*flow), RTE_CACHE_LINE_SIZE);
+
+		if (flow == NULL)
+			return -1;
+	}
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FC_MSG_REQ_FLOW_ADD;
+	app_pipeline_fc_key_convert(key, req->key, &signature);
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL) {
+		if (new_flow)
+			rte_free(flow);
+		return -1;
+	}
+
+	/* Read response and write flow */
+	if (rsp->status ||
+		(rsp->entry_ptr == NULL) ||
+		((new_flow == 0) && (rsp->key_found == 0)) ||
+		((new_flow == 1) && (rsp->key_found == 1))) {
+		app_msg_free(app, rsp);
+		if (new_flow)
+			rte_free(flow);
+		return -1;
+	}
+
+	memset(&flow->key, 0, sizeof(flow->key));
+	memcpy(&flow->key, key, sizeof(flow->key));
+	flow->port_id = port_id;
+	flow->signature = signature;
+	flow->entry_ptr = rsp->entry_ptr;
+
+	/* Commit rule */
+	if (new_flow) {
+		uint32_t bucket_id = signature & (N_BUCKETS - 1);
+
+		TAILQ_INSERT_TAIL(&p->flows[bucket_id], flow, node);
+		p->n_flows++;
 	}
 
+	/* Free response */
+	app_msg_free(app, rsp);
+
 	return 0;
 }
 
-void
-app_main_loop_pipeline_flow_classification(void) {
-	struct rte_pipeline_params pipeline_params = {
-		.name = "pipeline",
-		.socket_id = rte_socket_id(),
-	};
-
-	struct rte_pipeline *p;
-	uint32_t port_in_id[APP_MAX_PORTS];
-	uint32_t port_out_id[APP_MAX_PORTS];
-	uint32_t table_id;
-	uint32_t i;
+int
+app_pipeline_fc_add_bulk(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_fc_key *key,
+	uint32_t *port_id,
+	uint32_t n_keys)
+{
+	struct app_pipeline_fc *p;
+	struct pipeline_fc_add_bulk_msg_req *req;
+	struct pipeline_fc_add_bulk_msg_rsp *rsp;
 
-	uint32_t core_id = rte_lcore_id();
-	struct app_core_params *core_params = app_get_core_params(core_id);
-	struct app_core_fc_message_handle_params mh_params;
+	struct app_pipeline_fc_flow **flow;
+	uint32_t *signature;
+	int *new_flow;
+	struct pipeline_fc_add_bulk_flow_req *flow_req;
+	struct pipeline_fc_add_bulk_flow_rsp *flow_rsp;
 
-	if ((core_params == NULL) || (core_params->core_type != APP_CORE_FC))
-		rte_panic("Core %u misconfiguration\n", core_id);
+	uint32_t i;
+	int status;
 
-	RTE_LOG(INFO, USER1, "Core %u is doing flow classification "
-		"(pipeline with hash table, 16-byte key, LRU)\n", core_id);
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL) ||
+		(port_id == NULL) ||
+		(n_keys == 0))
+		return -1;
 
-	/* Pipeline configuration */
-	p = rte_pipeline_create(&pipeline_params);
+	p = app_pipeline_data_fe(app, pipeline_id);
 	if (p == NULL)
-		rte_panic("Unable to configure the pipeline\n");
-
-	/* Input port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_reader_params port_ring_params = {
-			.ring = app.rings[core_params->swq_in[i]],
-		};
-
-		struct rte_pipeline_port_in_params port_params = {
-			.ops = &rte_port_ring_reader_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.arg_ah = NULL,
-			.burst_size = app.bsz_swq_rd,
-		};
-
-		if (rte_pipeline_port_in_create(p, &port_params,
-			&port_in_id[i]))
-			rte_panic("Unable to configure input port for "
-				"ring %d\n", i);
-	}
-
-	/* Output port configuration */
-	for (i = 0; i < app.n_ports; i++) {
-		struct rte_port_ring_writer_params port_ring_params = {
-			.ring = app.rings[core_params->swq_out[i]],
-			.tx_burst_sz = app.bsz_swq_wr,
-		};
-
-		struct rte_pipeline_port_out_params port_params = {
-			.ops = &rte_port_ring_writer_ops,
-			.arg_create = (void *) &port_ring_params,
-			.f_action = NULL,
-			.f_action_bulk = NULL,
-			.arg_ah = NULL,
-		};
-
-		if (rte_pipeline_port_out_create(p, &port_params,
-			&port_out_id[i]))
-			rte_panic("Unable to configure output port for "
-				"ring %d\n", i);
-	}
-
-	/* Table configuration */
-	{
-		struct rte_table_hash_key16_lru_params table_hash_params = {
-			.n_entries = 1 << 24,
-			.signature_offset = __builtin_offsetof(
-				struct app_pkt_metadata, signature),
-			.key_offset = __builtin_offsetof(
-				struct app_pkt_metadata, flow_key),
-			.f_hash = test_hash,
-			.seed = 0,
-		};
-
-		struct rte_pipeline_table_params table_params = {
-			.ops = &rte_table_hash_key16_lru_ops,
-			.arg_create = &table_hash_params,
-			.f_action_hit = NULL,
-			.f_action_miss = NULL,
-			.arg_ah = NULL,
-			.action_data_size = 0,
-		};
-
-		if (rte_pipeline_table_create(p, &table_params, &table_id))
-			rte_panic("Unable to configure the hash table\n");
-	}
-
-	/* Interconnecting ports and tables */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
-			table_id))
-			rte_panic("Unable to connect input port %u to "
-				"table %u\n", port_in_id[i],  table_id);
-
-	/* Enable input ports */
-	for (i = 0; i < app.n_ports; i++)
-		if (rte_pipeline_port_in_enable(p, port_in_id[i]))
-			rte_panic("Unable to enable input port %u\n",
-				port_in_id[i]);
-
-	/* Check pipeline consistency */
-	if (rte_pipeline_check(p) < 0)
-		rte_panic("Pipeline consistency check failed\n");
-
-	/* Message handling */
-	mh_params.ring_req = app_get_ring_req(
-		app_get_first_core_id(APP_CORE_FC));
-	mh_params.ring_resp = app_get_ring_resp(
-		app_get_first_core_id(APP_CORE_FC));
-	mh_params.p = p;
-	mh_params.port_out_id = port_out_id;
-	mh_params.table_id = table_id;
-
-	/* Run-time */
-	for (i = 0; ; i++) {
-		rte_pipeline_run(p);
-
-		if ((i & APP_FLUSH) == 0) {
-			rte_pipeline_flush(p);
-			app_message_handle(&mh_params);
+		return -1;
+
+	for (i = 0; i < n_keys; i++)
+		if (port_id[i] >= p->n_ports_out)
+			return -1;
+
+	for (i = 0; i < n_keys; i++)
+		if (app_pipeline_fc_key_check(&key[i]) != 0)
+			return -1;
+
+	/* Memory allocation */
+	flow = rte_malloc(NULL,
+		n_keys * sizeof(struct app_pipeline_fc_flow *),
+		RTE_CACHE_LINE_SIZE);
+	if (flow == NULL)
+		return -1;
+
+	signature = rte_malloc(NULL,
+		n_keys * sizeof(uint32_t),
+		RTE_CACHE_LINE_SIZE);
+	if (signature == NULL) {
+		rte_free(flow);
+		return -1;
+	}
+
+	new_flow = rte_malloc(
+		NULL,
+		n_keys * sizeof(int),
+		RTE_CACHE_LINE_SIZE);
+	if (new_flow == NULL) {
+		rte_free(signature);
+		rte_free(flow);
+		return -1;
+	}
+
+	flow_req = rte_malloc(NULL,
+		n_keys * sizeof(struct pipeline_fc_add_bulk_flow_req),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_req == NULL) {
+		rte_free(new_flow);
+		rte_free(signature);
+		rte_free(flow);
+		return -1;
+	}
+
+	flow_rsp = rte_malloc(NULL,
+		n_keys * sizeof(struct pipeline_fc_add_bulk_flow_rsp),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_req == NULL) {
+		rte_free(flow_req);
+		rte_free(new_flow);
+		rte_free(signature);
+		rte_free(flow);
+		return -1;
+	}
+
+	/* Find existing flow or allocate new flow */
+	for (i = 0; i < n_keys; i++) {
+		flow[i] = app_pipeline_fc_flow_find(p, &key[i]);
+		new_flow[i] = (flow[i] == NULL);
+		if (flow[i] == NULL) {
+			flow[i] = rte_zmalloc(NULL,
+				sizeof(struct app_pipeline_fc_flow),
+				RTE_CACHE_LINE_SIZE);
+
+			if (flow[i] == NULL) {
+				uint32_t j;
+
+				for (j = 0; j < i; j++)
+					if (new_flow[j])
+						rte_free(flow[j]);
+
+				rte_free(flow_rsp);
+				rte_free(flow_req);
+				rte_free(new_flow);
+				rte_free(signature);
+				rte_free(flow);
+				return -1;
+			}
 		}
 	}
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL) {
+		for (i = 0; i < n_keys; i++)
+			if (new_flow[i])
+				rte_free(flow[i]);
+
+		rte_free(flow_rsp);
+		rte_free(flow_req);
+		rte_free(new_flow);
+		rte_free(signature);
+		rte_free(flow);
+		return -1;
+	}
+
+	for (i = 0; i < n_keys; i++) {
+		app_pipeline_fc_key_convert(&key[i],
+			flow_req[i].key,
+			&signature[i]);
+		flow_req[i].port_id = port_id[i];
+	}
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FC_MSG_REQ_FLOW_ADD_BULK;
+	req->req = flow_req;
+	req->rsp = flow_rsp;
+	req->n_keys = n_keys;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, 10000);
+	if (rsp == NULL) {
+		for (i = 0; i < n_keys; i++)
+			if (new_flow[i])
+				rte_free(flow[i]);
+
+		rte_free(flow_rsp);
+		rte_free(flow_req);
+		rte_free(new_flow);
+		rte_free(signature);
+		rte_free(flow);
+		return -1;
+	}
+
+	/* Read response */
+	status = 0;
+
+	for (i = 0; i < rsp->n_keys; i++)
+		if ((flow_rsp[i].entry_ptr == NULL) ||
+			((new_flow[i] == 0) && (flow_rsp[i].key_found == 0)) ||
+			((new_flow[i] == 1) && (flow_rsp[i].key_found == 1)))
+			status = -1;
+
+	if (rsp->n_keys < n_keys)
+		status = -1;
+
+	/* Commit flows */
+	for (i = 0; i < rsp->n_keys; i++) {
+		memcpy(&flow[i]->key, &key[i], sizeof(flow[i]->key));
+		flow[i]->port_id = port_id[i];
+		flow[i]->signature = signature[i];
+		flow[i]->entry_ptr = flow_rsp[i].entry_ptr;
+
+		if (new_flow[i]) {
+			uint32_t bucket_id = signature[i] & (N_BUCKETS - 1);
+
+			TAILQ_INSERT_TAIL(&p->flows[bucket_id], flow[i], node);
+			p->n_flows++;
+		}
+	}
+
+	/* Free resources */
+	app_msg_free(app, rsp);
+
+	for (i = rsp->n_keys; i < n_keys; i++)
+		if (new_flow[i])
+			rte_free(flow[i]);
+
+	rte_free(flow_rsp);
+	rte_free(flow_req);
+	rte_free(new_flow);
+	rte_free(signature);
+	rte_free(flow);
+
+	return status;
+}
+
+int
+app_pipeline_fc_del(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_fc_key *key)
+{
+	struct app_pipeline_fc *p;
+	struct app_pipeline_fc_flow *flow;
+
+	struct pipeline_fc_del_msg_req *req;
+	struct pipeline_fc_del_msg_rsp *rsp;
+
+	uint32_t signature, bucket_id;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(key == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (app_pipeline_fc_key_check(key) != 0)
+		return -1;
+
+	/* Find rule */
+	flow = app_pipeline_fc_flow_find(p, key);
+	if (flow == NULL)
+		return 0;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FC_MSG_REQ_FLOW_DEL;
+	app_pipeline_fc_key_convert(key, req->key, &signature);
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status || !rsp->key_found) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	/* Remove rule */
+	bucket_id = signature & (N_BUCKETS - 1);
+	TAILQ_REMOVE(&p->flows[bucket_id], flow, node);
+	p->n_flows--;
+	rte_free(flow);
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
 }
 
-void
-app_message_handle(struct app_core_fc_message_handle_params *params)
+int
+app_pipeline_fc_add_default(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id)
 {
-	struct rte_ring *ring_req = params->ring_req;
-	struct rte_ring *ring_resp;
-	void *msg;
-	struct app_msg_req *req;
-	struct app_msg_resp *resp;
-	struct rte_pipeline *p;
-	uint32_t *port_out_id;
-	uint32_t table_id;
-	int result;
-
-	/* Read request message */
-	result = rte_ring_sc_dequeue(ring_req, &msg);
-	if (result != 0)
-		return;
+	struct app_pipeline_fc *p;
 
-	ring_resp = params->ring_resp;
-	p = params->p;
-	port_out_id = params->port_out_id;
-	table_id = params->table_id;
+	struct pipeline_fc_add_default_msg_req *req;
+	struct pipeline_fc_add_default_msg_rsp *rsp;
 
-	/* Handle request */
-	req = (struct app_msg_req *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	switch (req->type) {
-	case APP_MSG_REQ_PING:
-	{
-		result = 0;
-		break;
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (port_id >= p->n_ports_out)
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FC_MSG_REQ_FLOW_ADD_DEFAULT;
+	req->port_id = port_id;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response and write flow */
+	if (rsp->status || (rsp->entry_ptr == NULL)) {
+		app_msg_free(app, rsp);
+		return -1;
 	}
 
-	case APP_MSG_REQ_FC_ADD_ALL:
-	{
-		result = app_flow_classification_table_init(p, port_out_id,
-			table_id);
-		break;
+	p->default_flow_port_id = port_id;
+	p->default_flow_entry_ptr = rsp->entry_ptr;
+
+	/* Commit route */
+	p->default_flow_present = 1;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+int
+app_pipeline_fc_del_default(struct app_params *app,
+	uint32_t pipeline_id)
+{
+	struct app_pipeline_fc *p;
+
+	struct pipeline_fc_del_default_msg_req *req;
+	struct pipeline_fc_del_default_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -EINVAL;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FC_MSG_REQ_FLOW_DEL_DEFAULT;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status) {
+		app_msg_free(app, rsp);
+		return -1;
 	}
 
-	case APP_MSG_REQ_FC_ADD:
-	{
-		struct rte_pipeline_table_entry entry = {
-			.action = RTE_PIPELINE_ACTION_PORT,
-			{.port_id = port_out_id[req->flow_classif_add.port]},
-		};
+	/* Commit route */
+	p->default_flow_present = 0;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+/*
+ * Flow ls
+ */
+
+static void
+print_fc_qinq_flow(struct app_pipeline_fc_flow *flow)
+{
+	printf("(SVLAN = %" PRIu32 ", "
+		"CVLAN = %" PRIu32 ") => "
+		"Port = %" PRIu32 " "
+		"(signature = 0x%08" PRIx32 ", "
+		"entry_ptr = %p)\n",
+
+		flow->key.key.qinq.svlan,
+		flow->key.key.qinq.cvlan,
+		flow->port_id,
+		flow->signature,
+		flow->entry_ptr);
+}
+
+static void
+print_fc_ipv4_5tuple_flow(struct app_pipeline_fc_flow *flow)
+{
+	printf("(SA = %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ", "
+		   "DA = %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ", "
+		   "SP = %" PRIu32 ", "
+		   "DP = %" PRIu32 ", "
+		   "Proto = %" PRIu32 ") => "
+		   "Port = %" PRIu32 " "
+		   "(signature = 0x%08" PRIx32 ", "
+		   "entry_ptr = %p)\n",
 
-		struct rte_pipeline_table_entry *entry_ptr;
+		   (flow->key.key.ipv4_5tuple.ip_src >> 24) & 0xFF,
+		   (flow->key.key.ipv4_5tuple.ip_src >> 16) & 0xFF,
+		   (flow->key.key.ipv4_5tuple.ip_src >> 8) & 0xFF,
+		   flow->key.key.ipv4_5tuple.ip_src & 0xFF,
 
-		int key_found;
+		   (flow->key.key.ipv4_5tuple.ip_dst >> 24) & 0xFF,
+		   (flow->key.key.ipv4_5tuple.ip_dst >> 16) & 0xFF,
+		   (flow->key.key.ipv4_5tuple.ip_dst >> 8) & 0xFF,
+		   flow->key.key.ipv4_5tuple.ip_dst & 0xFF,
 
-		result = rte_pipeline_table_entry_add(p, table_id,
-			req->flow_classif_add.key_raw, &entry, &key_found,
-			&entry_ptr);
+		   flow->key.key.ipv4_5tuple.port_src,
+		   flow->key.key.ipv4_5tuple.port_dst,
+
+		   flow->key.key.ipv4_5tuple.proto,
+
+		   flow->port_id,
+		   flow->signature,
+		   flow->entry_ptr);
+}
+
+static void
+print_fc_ipv6_5tuple_flow(struct app_pipeline_fc_flow *flow) {
+	printf("(SA = %02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
+		":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
+		":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
+		":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32 ", "
+		"DA = %02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
+		":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
+		":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
+		":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32 ", "
+		"SP = %" PRIu32 ", "
+		"DP = %" PRIu32 " "
+		"Proto = %" PRIu32 " "
+		"=> Port = %" PRIu32 " "
+		"(signature = 0x%08" PRIx32 ", "
+		"entry_ptr = %p)\n",
+
+		flow->key.key.ipv6_5tuple.ip_src[0],
+		flow->key.key.ipv6_5tuple.ip_src[1],
+		flow->key.key.ipv6_5tuple.ip_src[2],
+		flow->key.key.ipv6_5tuple.ip_src[3],
+		flow->key.key.ipv6_5tuple.ip_src[4],
+		flow->key.key.ipv6_5tuple.ip_src[5],
+		flow->key.key.ipv6_5tuple.ip_src[6],
+		flow->key.key.ipv6_5tuple.ip_src[7],
+		flow->key.key.ipv6_5tuple.ip_src[8],
+		flow->key.key.ipv6_5tuple.ip_src[9],
+		flow->key.key.ipv6_5tuple.ip_src[10],
+		flow->key.key.ipv6_5tuple.ip_src[11],
+		flow->key.key.ipv6_5tuple.ip_src[12],
+		flow->key.key.ipv6_5tuple.ip_src[13],
+		flow->key.key.ipv6_5tuple.ip_src[14],
+		flow->key.key.ipv6_5tuple.ip_src[15],
+
+		flow->key.key.ipv6_5tuple.ip_dst[0],
+		flow->key.key.ipv6_5tuple.ip_dst[1],
+		flow->key.key.ipv6_5tuple.ip_dst[2],
+		flow->key.key.ipv6_5tuple.ip_dst[3],
+		flow->key.key.ipv6_5tuple.ip_dst[4],
+		flow->key.key.ipv6_5tuple.ip_dst[5],
+		flow->key.key.ipv6_5tuple.ip_dst[6],
+		flow->key.key.ipv6_5tuple.ip_dst[7],
+		flow->key.key.ipv6_5tuple.ip_dst[8],
+		flow->key.key.ipv6_5tuple.ip_dst[9],
+		flow->key.key.ipv6_5tuple.ip_dst[10],
+		flow->key.key.ipv6_5tuple.ip_dst[11],
+		flow->key.key.ipv6_5tuple.ip_dst[12],
+		flow->key.key.ipv6_5tuple.ip_dst[13],
+		flow->key.key.ipv6_5tuple.ip_dst[14],
+		flow->key.key.ipv6_5tuple.ip_dst[15],
+
+		flow->key.key.ipv6_5tuple.port_src,
+		flow->key.key.ipv6_5tuple.port_dst,
+
+		flow->key.key.ipv6_5tuple.proto,
+
+		flow->port_id,
+		flow->signature,
+		flow->entry_ptr);
+}
+
+static void
+print_fc_flow(struct app_pipeline_fc_flow *flow)
+{
+	switch (flow->key.type) {
+	case FLOW_KEY_QINQ:
+		print_fc_qinq_flow(flow);
 		break;
-	}
 
-	case APP_MSG_REQ_FC_DEL:
-	{
-		int key_found;
+	case FLOW_KEY_IPV4_5TUPLE:
+		print_fc_ipv4_5tuple_flow(flow);
+		break;
 
-		result = rte_pipeline_table_entry_delete(p, table_id,
-			req->flow_classif_add.key_raw, &key_found, NULL);
+	case FLOW_KEY_IPV6_5TUPLE:
+		print_fc_ipv6_5tuple_flow(flow);
 		break;
 	}
+}
 
-	default:
-		rte_panic("FC Unrecognized message type (%u)\n", req->type);
+static int
+app_pipeline_fc_ls(struct app_params *app,
+		uint32_t pipeline_id)
+{
+	struct app_pipeline_fc *p;
+	struct app_pipeline_fc_flow *flow;
+	uint32_t i;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	for (i = 0; i < N_BUCKETS; i++)
+		TAILQ_FOREACH(flow, &p->flows[i], node)
+			print_fc_flow(flow);
+
+	if (p->default_flow_present)
+		printf("Default flow: port %" PRIu32 " (entry ptr = %p)\n",
+			p->default_flow_port_id,
+			p->default_flow_entry_ptr);
+	else
+		printf("Default: DROP\n");
+
+	return 0;
+}
+
+/*
+ * flow add qinq
+ */
+
+struct cmd_fc_add_qinq_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t qinq_string;
+	uint16_t svlan;
+	uint16_t cvlan;
+	uint32_t port;
+};
+
+static void
+cmd_fc_add_qinq_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_add_qinq_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key key;
+	int status;
+
+	memset(&key, 0, sizeof(key));
+	key.type = FLOW_KEY_QINQ;
+	key.key.qinq.svlan = params->svlan;
+	key.key.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_fc_add(app,
+		params->pipeline_id,
+		&key,
+		params->port);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_result, p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_add_qinq_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_qinq_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_result, flow_string,
+		"flow");
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_result, add_string,
+		"add");
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_result, qinq_string,
+		"qinq");
+
+cmdline_parse_token_num_t cmd_fc_add_qinq_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_qinq_result, svlan, UINT16);
+
+cmdline_parse_token_num_t cmd_fc_add_qinq_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_qinq_result, cvlan, UINT16);
+
+cmdline_parse_token_num_t cmd_fc_add_qinq_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_qinq_result, port, UINT32);
+
+cmdline_parse_inst_t cmd_fc_add_qinq = {
+	.f = cmd_fc_add_qinq_parsed,
+	.data = NULL,
+	.help_str = "Flow add (Q-in-Q)",
+	.tokens = {
+		(void *) &cmd_fc_add_qinq_p_string,
+		(void *) &cmd_fc_add_qinq_pipeline_id,
+		(void *) &cmd_fc_add_qinq_flow_string,
+		(void *) &cmd_fc_add_qinq_add_string,
+		(void *) &cmd_fc_add_qinq_qinq_string,
+		(void *) &cmd_fc_add_qinq_svlan,
+		(void *) &cmd_fc_add_qinq_cvlan,
+		(void *) &cmd_fc_add_qinq_port,
+		NULL,
+	},
+};
+
+/*
+ * flow add qinq all
+ */
+
+struct cmd_fc_add_qinq_all_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t qinq_string;
+	cmdline_fixed_string_t all_string;
+	uint32_t n_flows;
+	uint32_t n_ports;
+};
+
+#ifndef N_FLOWS_BULK
+#define N_FLOWS_BULK					4096
+#endif
+
+static void
+cmd_fc_add_qinq_all_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_add_qinq_all_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key *key;
+	uint32_t *port_id;
+	uint32_t flow_id;
+
+	key = rte_zmalloc(NULL,
+		N_FLOWS_BULK * sizeof(*key),
+		RTE_CACHE_LINE_SIZE);
+	if (key == NULL) {
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	port_id = rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(*port_id),
+		RTE_CACHE_LINE_SIZE);
+	if (port_id == NULL) {
+		rte_free(key);
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	for (flow_id = 0; flow_id < params->n_flows; flow_id++) {
+		uint32_t pos = flow_id & (N_FLOWS_BULK - 1);
+
+		key[pos].type = FLOW_KEY_QINQ;
+		key[pos].key.qinq.svlan = flow_id >> 12;
+		key[pos].key.qinq.cvlan = flow_id & 0xFFF;
+
+		port_id[pos] = flow_id % params->n_ports;
+
+		if ((pos == N_FLOWS_BULK - 1) ||
+			(flow_id == params->n_flows - 1)) {
+			int status;
+
+			status = app_pipeline_fc_add_bulk(app,
+				params->pipeline_id,
+				key,
+				port_id,
+				pos + 1);
+
+			if (status != 0) {
+				printf("Command failed\n");
+
+				break;
+			}
+		}
+	}
+
+	rte_free(port_id);
+	rte_free(key);
+}
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_all_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_all_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_fc_add_qinq_all_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_qinq_all_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_all_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_all_result, flow_string,
+		"flow");
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_all_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_all_result, add_string,
+		"add");
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_all_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_all_result, qinq_string,
+		"qinq");
+
+cmdline_parse_token_string_t cmd_fc_add_qinq_all_all_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_qinq_all_result, all_string,
+		"all");
+
+cmdline_parse_token_num_t cmd_fc_add_qinq_all_n_flows =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_qinq_all_result, n_flows,
+		UINT32);
+
+cmdline_parse_token_num_t cmd_fc_add_qinq_all_n_ports =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_qinq_all_result, n_ports,
+		UINT32);
+
+cmdline_parse_inst_t cmd_fc_add_qinq_all = {
+	.f = cmd_fc_add_qinq_all_parsed,
+	.data = NULL,
+	.help_str = "Flow add all (Q-in-Q)",
+	.tokens = {
+		(void *) &cmd_fc_add_qinq_all_p_string,
+		(void *) &cmd_fc_add_qinq_all_pipeline_id,
+		(void *) &cmd_fc_add_qinq_all_flow_string,
+		(void *) &cmd_fc_add_qinq_all_add_string,
+		(void *) &cmd_fc_add_qinq_all_qinq_string,
+		(void *) &cmd_fc_add_qinq_all_all_string,
+		(void *) &cmd_fc_add_qinq_all_n_flows,
+		(void *) &cmd_fc_add_qinq_all_n_ports,
+		NULL,
+	},
+};
+
+/*
+ * flow add ipv4_5tuple
+ */
+
+struct cmd_fc_add_ipv4_5tuple_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t ipv4_5tuple_string;
+	cmdline_ipaddr_t ip_src;
+	cmdline_ipaddr_t ip_dst;
+	uint16_t port_src;
+	uint16_t port_dst;
+	uint32_t proto;
+	uint32_t port;
+};
+
+static void
+cmd_fc_add_ipv4_5tuple_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_add_ipv4_5tuple_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key key;
+	int status;
+
+	memset(&key, 0, sizeof(key));
+	key.type = FLOW_KEY_IPV4_5TUPLE;
+	key.key.ipv4_5tuple.ip_src = rte_bswap32(
+		params->ip_src.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.ip_dst = rte_bswap32(
+		params->ip_dst.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.port_src = params->port_src;
+	key.key.ipv4_5tuple.port_dst = params->port_dst;
+	key.key.ipv4_5tuple.proto = params->proto;
+
+	status = app_pipeline_fc_add(app,
+		params->pipeline_id,
+		&key,
+		params->port);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result,
+		flow_string, "flow");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result,
+		add_string, "add");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_ipv4_5tuple_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result,
+		ipv4_5tuple_string, "ipv4_5tuple");
+
+cmdline_parse_token_ipaddr_t cmd_fc_add_ipv4_5tuple_ip_src =
+	TOKEN_IPV4_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, ip_src);
+
+cmdline_parse_token_ipaddr_t cmd_fc_add_ipv4_5tuple_ip_dst =
+	TOKEN_IPV4_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, ip_dst);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_port_src =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, port_src,
+		UINT16);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_port_dst =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, port_dst,
+		UINT16);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_proto =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, proto,
+		UINT32);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_result, port,
+		UINT32);
+
+cmdline_parse_inst_t cmd_fc_add_ipv4_5tuple = {
+	.f = cmd_fc_add_ipv4_5tuple_parsed,
+	.data = NULL,
+	.help_str = "Flow add (IPv4 5-tuple)",
+	.tokens = {
+		(void *) &cmd_fc_add_ipv4_5tuple_p_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_pipeline_id,
+		(void *) &cmd_fc_add_ipv4_5tuple_flow_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_add_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_ipv4_5tuple_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_ip_src,
+		(void *) &cmd_fc_add_ipv4_5tuple_ip_dst,
+		(void *) &cmd_fc_add_ipv4_5tuple_port_src,
+		(void *) &cmd_fc_add_ipv4_5tuple_port_dst,
+		(void *) &cmd_fc_add_ipv4_5tuple_proto,
+		(void *) &cmd_fc_add_ipv4_5tuple_port,
+		NULL,
+	},
+};
+
+/*
+ * flow add ipv4_5tuple all
+ */
+
+struct cmd_fc_add_ipv4_5tuple_all_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t ipv4_5tuple_string;
+	cmdline_fixed_string_t all_string;
+	uint32_t n_flows;
+	uint32_t n_ports;
+};
+
+static void
+cmd_fc_add_ipv4_5tuple_all_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_add_ipv4_5tuple_all_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key *key;
+	uint32_t *port_id;
+	uint32_t flow_id;
+
+	key = rte_zmalloc(NULL,
+		N_FLOWS_BULK * sizeof(*key),
+		RTE_CACHE_LINE_SIZE);
+	if (key == NULL) {
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	port_id = rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(*port_id),
+		RTE_CACHE_LINE_SIZE);
+	if (port_id == NULL) {
+		rte_free(key);
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	for (flow_id = 0; flow_id < params->n_flows; flow_id++) {
+		uint32_t pos = flow_id & (N_FLOWS_BULK - 1);
+
+		key[pos].type = FLOW_KEY_IPV4_5TUPLE;
+		key[pos].key.ipv4_5tuple.ip_src = 0;
+		key[pos].key.ipv4_5tuple.ip_dst = flow_id;
+		key[pos].key.ipv4_5tuple.port_src = 0;
+		key[pos].key.ipv4_5tuple.port_dst = 0;
+		key[pos].key.ipv4_5tuple.proto = 6;
+
+		port_id[pos] = flow_id % params->n_ports;
+
+		if ((pos == N_FLOWS_BULK - 1) ||
+			(flow_id == params->n_flows - 1)) {
+			int status;
+
+			status = app_pipeline_fc_add_bulk(app,
+				params->pipeline_id,
+				key,
+				port_id,
+				pos + 1);
+
+			if (status != 0) {
+				printf("Command failed\n");
+
+				break;
+			}
+		}
 	}
 
-	/* Fill in response message */
-	resp = (struct app_msg_resp *)rte_ctrlmbuf_data((struct rte_mbuf *)msg);
-	resp->result = result;
+	rte_free(port_id);
+	rte_free(key);
+}
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_all_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_all_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_all_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		flow_string, "flow");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_all_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		add_string, "add");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_all_ipv4_5tuple_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		ipv4_5tuple_string, "ipv4_5tuple");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv4_5tuple_all_all_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		all_string, "all");
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_all_n_flows =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		n_flows, UINT32);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv4_5tuple_all_n_ports =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv4_5tuple_all_result,
+		n_ports, UINT32);
+
+cmdline_parse_inst_t cmd_fc_add_ipv4_5tuple_all = {
+	.f = cmd_fc_add_ipv4_5tuple_all_parsed,
+	.data = NULL,
+	.help_str = "Flow add all (IPv4 5-tuple)",
+	.tokens = {
+		(void *) &cmd_fc_add_ipv4_5tuple_all_p_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_all_pipeline_id,
+		(void *) &cmd_fc_add_ipv4_5tuple_all_flow_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_all_add_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_all_ipv4_5tuple_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_all_all_string,
+		(void *) &cmd_fc_add_ipv4_5tuple_all_n_flows,
+		(void *) &cmd_fc_add_ipv4_5tuple_all_n_ports,
+		NULL,
+	},
+};
+
+/*
+ * flow add ipv6_5tuple
+ */
+
+struct cmd_fc_add_ipv6_5tuple_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t ipv6_5tuple_string;
+	cmdline_ipaddr_t ip_src;
+	cmdline_ipaddr_t ip_dst;
+	uint16_t port_src;
+	uint16_t port_dst;
+	uint32_t proto;
+	uint32_t port;
+};
+
+static void
+cmd_fc_add_ipv6_5tuple_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_add_ipv6_5tuple_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key key;
+	int status;
+
+	memset(&key, 0, sizeof(key));
+	key.type = FLOW_KEY_IPV6_5TUPLE;
+	memcpy(key.key.ipv6_5tuple.ip_src,
+		params->ip_src.addr.ipv6.s6_addr,
+		16);
+	memcpy(key.key.ipv6_5tuple.ip_dst,
+		params->ip_dst.addr.ipv6.s6_addr,
+		16);
+	key.key.ipv6_5tuple.port_src = params->port_src;
+	key.key.ipv6_5tuple.port_dst = params->port_dst;
+	key.key.ipv6_5tuple.proto = params->proto;
+
+	status = app_pipeline_fc_add(app,
+		params->pipeline_id,
+		&key,
+		params->port);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result,
+		p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result,
+		flow_string, "flow");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result,
+		add_string, "add");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_ipv6_5tuple_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result,
+		ipv6_5tuple_string, "ipv6_5tuple");
+
+cmdline_parse_token_ipaddr_t cmd_fc_add_ipv6_5tuple_ip_src =
+	TOKEN_IPV6_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result, ip_src);
+
+cmdline_parse_token_ipaddr_t cmd_fc_add_ipv6_5tuple_ip_dst =
+	TOKEN_IPV6_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result, ip_dst);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_port_src =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result, port_src,
+		UINT16);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_port_dst =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result, port_dst,
+		UINT16);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_proto =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result, proto,
+		UINT32);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_result, port,
+		UINT32);
+
+cmdline_parse_inst_t cmd_fc_add_ipv6_5tuple = {
+	.f = cmd_fc_add_ipv6_5tuple_parsed,
+	.data = NULL,
+	.help_str = "Flow add (IPv6 5-tuple)",
+	.tokens = {
+		(void *) &cmd_fc_add_ipv6_5tuple_p_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_pipeline_id,
+		(void *) &cmd_fc_add_ipv6_5tuple_flow_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_add_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_ipv6_5tuple_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_ip_src,
+		(void *) &cmd_fc_add_ipv6_5tuple_ip_dst,
+		(void *) &cmd_fc_add_ipv6_5tuple_port_src,
+		(void *) &cmd_fc_add_ipv6_5tuple_port_dst,
+		(void *) &cmd_fc_add_ipv6_5tuple_proto,
+		(void *) &cmd_fc_add_ipv6_5tuple_port,
+		NULL,
+	},
+};
+
+/*
+ * flow add ipv6_5tuple all
+ */
+
+struct cmd_fc_add_ipv6_5tuple_all_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t ipv6_5tuple_string;
+	cmdline_fixed_string_t all_string;
+	uint32_t n_flows;
+	uint32_t n_ports;
+};
+
+static void
+cmd_fc_add_ipv6_5tuple_all_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_add_ipv6_5tuple_all_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key *key;
+	uint32_t *port_id;
+	uint32_t flow_id;
+
+	key = rte_zmalloc(NULL,
+		N_FLOWS_BULK * sizeof(*key),
+		RTE_CACHE_LINE_SIZE);
+	if (key == NULL) {
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	port_id = rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(*port_id),
+		RTE_CACHE_LINE_SIZE);
+	if (port_id == NULL) {
+		rte_free(key);
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	for (flow_id = 0; flow_id < params->n_flows; flow_id++) {
+		uint32_t pos = flow_id & (N_FLOWS_BULK - 1);
+		uint32_t *x;
+
+		key[pos].type = FLOW_KEY_IPV6_5TUPLE;
+		x = (uint32_t *) key[pos].key.ipv6_5tuple.ip_dst;
+		*x = rte_bswap32(flow_id);
+		key[pos].key.ipv6_5tuple.proto = 6;
+
+		port_id[pos] = flow_id % params->n_ports;
+
+		if ((pos == N_FLOWS_BULK - 1) ||
+			(flow_id == params->n_flows - 1)) {
+			int status;
+
+			status = app_pipeline_fc_add_bulk(app,
+				params->pipeline_id,
+				key,
+				port_id,
+				pos + 1);
+
+			if (status != 0) {
+				printf("Command failed\n");
+
+				break;
+			}
+		}
+	}
+
+	rte_free(port_id);
+	rte_free(key);
+}
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_all_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_all_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_all_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		flow_string, "flow");
 
-	/* Send response */
-	do {
-		result = rte_ring_sp_enqueue(ring_resp, msg);
-	} while (result == -ENOBUFS);
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_all_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		add_string, "add");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_all_ipv6_5tuple_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		ipv6_5tuple_string, "ipv6_5tuple");
+
+cmdline_parse_token_string_t cmd_fc_add_ipv6_5tuple_all_all_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		all_string, "all");
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_all_n_flows =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		n_flows, UINT32);
+
+cmdline_parse_token_num_t cmd_fc_add_ipv6_5tuple_all_n_ports =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_ipv6_5tuple_all_result,
+		n_ports, UINT32);
+
+cmdline_parse_inst_t cmd_fc_add_ipv6_5tuple_all = {
+	.f = cmd_fc_add_ipv6_5tuple_all_parsed,
+	.data = NULL,
+	.help_str = "Flow add all (ipv6 5-tuple)",
+	.tokens = {
+		(void *) &cmd_fc_add_ipv6_5tuple_all_p_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_all_pipeline_id,
+		(void *) &cmd_fc_add_ipv6_5tuple_all_flow_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_all_add_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_all_ipv6_5tuple_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_all_all_string,
+		(void *) &cmd_fc_add_ipv6_5tuple_all_n_flows,
+		(void *) &cmd_fc_add_ipv6_5tuple_all_n_ports,
+		NULL,
+	},
+};
+
+/*
+ * flow del qinq
+ */
+struct cmd_fc_del_qinq_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t qinq_string;
+	uint16_t svlan;
+	uint16_t cvlan;
+};
+
+static void
+cmd_fc_del_qinq_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_del_qinq_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key key;
+	int status;
+
+	memset(&key, 0, sizeof(key));
+	key.type = FLOW_KEY_QINQ;
+	key.key.qinq.svlan = params->svlan;
+	key.key.qinq.cvlan = params->cvlan;
+	status = app_pipeline_fc_del(app, params->pipeline_id, &key);
+
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_del_qinq_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_qinq_result, p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_del_qinq_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_qinq_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_fc_del_qinq_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_qinq_result, flow_string,
+		"flow");
+
+cmdline_parse_token_string_t cmd_fc_del_qinq_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_qinq_result, del_string,
+		"del");
+
+cmdline_parse_token_string_t cmd_fc_del_qinq_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_qinq_result, qinq_string,
+		"qinq");
+
+cmdline_parse_token_num_t cmd_fc_del_qinq_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_qinq_result, svlan, UINT16);
+
+cmdline_parse_token_num_t cmd_fc_del_qinq_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_qinq_result, cvlan, UINT16);
+
+cmdline_parse_inst_t cmd_fc_del_qinq = {
+	.f = cmd_fc_del_qinq_parsed,
+	.data = NULL,
+	.help_str = "Flow delete (Q-in-Q)",
+	.tokens = {
+		(void *) &cmd_fc_del_qinq_p_string,
+		(void *) &cmd_fc_del_qinq_pipeline_id,
+		(void *) &cmd_fc_del_qinq_flow_string,
+		(void *) &cmd_fc_del_qinq_del_string,
+		(void *) &cmd_fc_del_qinq_qinq_string,
+		(void *) &cmd_fc_del_qinq_svlan,
+		(void *) &cmd_fc_del_qinq_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * flow del ipv4_5tuple
+ */
+
+struct cmd_fc_del_ipv4_5tuple_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t ipv4_5tuple_string;
+	cmdline_ipaddr_t ip_src;
+	cmdline_ipaddr_t ip_dst;
+	uint16_t port_src;
+	uint16_t port_dst;
+	uint32_t proto;
+};
+
+static void
+cmd_fc_del_ipv4_5tuple_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_del_ipv4_5tuple_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key key;
+	int status;
+
+	memset(&key, 0, sizeof(key));
+	key.type = FLOW_KEY_IPV4_5TUPLE;
+	key.key.ipv4_5tuple.ip_src = rte_bswap32(
+		params->ip_src.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.ip_dst = rte_bswap32(
+		params->ip_dst.addr.ipv4.s_addr);
+	key.key.ipv4_5tuple.port_src = params->port_src;
+	key.key.ipv4_5tuple.port_dst = params->port_dst;
+	key.key.ipv4_5tuple.proto = params->proto;
+
+	status = app_pipeline_fc_del(app, params->pipeline_id, &key);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_del_ipv4_5tuple_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_del_ipv4_5tuple_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fc_del_ipv4_5tuple_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		flow_string, "flow");
+
+cmdline_parse_token_string_t cmd_fc_del_ipv4_5tuple_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		del_string, "del");
+
+cmdline_parse_token_string_t cmd_fc_del_ipv4_5tuple_ipv4_5tuple_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		ipv4_5tuple_string, "ipv4_5tuple");
+
+cmdline_parse_token_ipaddr_t cmd_fc_del_ipv4_5tuple_ip_src =
+	TOKEN_IPV4_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		ip_src);
+
+cmdline_parse_token_ipaddr_t cmd_fc_del_ipv4_5tuple_ip_dst =
+	TOKEN_IPV4_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result, ip_dst);
+
+cmdline_parse_token_num_t cmd_fc_del_ipv4_5tuple_port_src =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		port_src, UINT16);
+
+cmdline_parse_token_num_t cmd_fc_del_ipv4_5tuple_port_dst =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		port_dst, UINT16);
+
+cmdline_parse_token_num_t cmd_fc_del_ipv4_5tuple_proto =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv4_5tuple_result,
+		proto, UINT32);
+
+cmdline_parse_inst_t cmd_fc_del_ipv4_5tuple = {
+	.f = cmd_fc_del_ipv4_5tuple_parsed,
+	.data = NULL,
+	.help_str = "Flow delete (IPv4 5-tuple)",
+	.tokens = {
+		(void *) &cmd_fc_del_ipv4_5tuple_p_string,
+		(void *) &cmd_fc_del_ipv4_5tuple_pipeline_id,
+		(void *) &cmd_fc_del_ipv4_5tuple_flow_string,
+		(void *) &cmd_fc_del_ipv4_5tuple_del_string,
+		(void *) &cmd_fc_del_ipv4_5tuple_ipv4_5tuple_string,
+		(void *) &cmd_fc_del_ipv4_5tuple_ip_src,
+		(void *) &cmd_fc_del_ipv4_5tuple_ip_dst,
+		(void *) &cmd_fc_del_ipv4_5tuple_port_src,
+		(void *) &cmd_fc_del_ipv4_5tuple_port_dst,
+		(void *) &cmd_fc_del_ipv4_5tuple_proto,
+		NULL,
+	},
+};
+
+/*
+ * flow del ipv6_5tuple
+ */
+
+struct cmd_fc_del_ipv6_5tuple_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t ipv6_5tuple_string;
+	cmdline_ipaddr_t ip_src;
+	cmdline_ipaddr_t ip_dst;
+	uint16_t port_src;
+	uint16_t port_dst;
+	uint32_t proto;
+};
+
+static void
+cmd_fc_del_ipv6_5tuple_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_del_ipv6_5tuple_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fc_key key;
+	int status;
+
+	memset(&key, 0, sizeof(key));
+	key.type = FLOW_KEY_IPV6_5TUPLE;
+	memcpy(key.key.ipv6_5tuple.ip_src,
+		params->ip_src.addr.ipv6.s6_addr,
+		16);
+	memcpy(key.key.ipv6_5tuple.ip_dst,
+		params->ip_dst.addr.ipv6.s6_addr,
+		16);
+	key.key.ipv6_5tuple.port_src = params->port_src;
+	key.key.ipv6_5tuple.port_dst = params->port_dst;
+	key.key.ipv6_5tuple.proto = params->proto;
+
+	status = app_pipeline_fc_del(app, params->pipeline_id, &key);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_del_ipv6_5tuple_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result,
+		p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_del_ipv6_5tuple_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result,
+		pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fc_del_ipv6_5tuple_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result,
+		flow_string, "flow");
+
+cmdline_parse_token_string_t cmd_fc_del_ipv6_5tuple_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result,
+		del_string, "del");
+
+cmdline_parse_token_string_t cmd_fc_del_ipv6_5tuple_ipv6_5tuple_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result,
+		ipv6_5tuple_string, "ipv6_5tuple");
+
+cmdline_parse_token_ipaddr_t cmd_fc_del_ipv6_5tuple_ip_src =
+	TOKEN_IPV6_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result, ip_src);
+
+cmdline_parse_token_ipaddr_t cmd_fc_del_ipv6_5tuple_ip_dst =
+	TOKEN_IPV6_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result, ip_dst);
+
+cmdline_parse_token_num_t cmd_fc_del_ipv6_5tuple_port_src =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result, port_src,
+		UINT16);
+
+cmdline_parse_token_num_t cmd_fc_del_ipv6_5tuple_port_dst =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result, port_dst,
+		UINT16);
+
+cmdline_parse_token_num_t cmd_fc_del_ipv6_5tuple_proto =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_ipv6_5tuple_result, proto,
+		UINT32);
+
+cmdline_parse_inst_t cmd_fc_del_ipv6_5tuple = {
+	.f = cmd_fc_del_ipv6_5tuple_parsed,
+	.data = NULL,
+	.help_str = "Flow delete (IPv6 5-tuple)",
+	.tokens = {
+		(void *) &cmd_fc_del_ipv6_5tuple_p_string,
+		(void *) &cmd_fc_del_ipv6_5tuple_pipeline_id,
+		(void *) &cmd_fc_del_ipv6_5tuple_flow_string,
+		(void *) &cmd_fc_del_ipv6_5tuple_del_string,
+		(void *) &cmd_fc_del_ipv6_5tuple_ipv6_5tuple_string,
+		(void *) &cmd_fc_del_ipv6_5tuple_ip_src,
+		(void *) &cmd_fc_del_ipv6_5tuple_ip_dst,
+		(void *) &cmd_fc_del_ipv6_5tuple_port_src,
+		(void *) &cmd_fc_del_ipv6_5tuple_port_dst,
+		(void *) &cmd_fc_del_ipv6_5tuple_proto,
+		NULL,
+	},
+};
+
+/*
+ * flow add default
+ */
+
+struct cmd_fc_add_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_fixed_string_t default_string;
+	uint32_t port;
+};
+
+static void
+cmd_fc_add_default_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_add_default_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_fc_add_default(app, params->pipeline_id,
+		params->port);
+
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_add_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_default_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_fc_add_default_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_default_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_fc_add_default_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_default_result, flow_string,
+		"flow");
+
+cmdline_parse_token_string_t cmd_fc_add_default_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_default_result, add_string,
+		"add");
+
+cmdline_parse_token_string_t cmd_fc_add_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_add_default_result,
+		default_string, "default");
+
+cmdline_parse_token_num_t cmd_fc_add_default_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_add_default_result, port, UINT32);
+
+cmdline_parse_inst_t cmd_fc_add_default = {
+	.f = cmd_fc_add_default_parsed,
+	.data = NULL,
+	.help_str = "Flow add default",
+	.tokens = {
+		(void *) &cmd_fc_add_default_p_string,
+		(void *) &cmd_fc_add_default_pipeline_id,
+		(void *) &cmd_fc_add_default_flow_string,
+		(void *) &cmd_fc_add_default_add_string,
+		(void *) &cmd_fc_add_default_default_string,
+		(void *) &cmd_fc_add_default_port,
+		NULL,
+	},
+};
+
+/*
+ * flow del default
+ */
+
+struct cmd_fc_del_default_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t del_string;
+	cmdline_fixed_string_t default_string;
+};
+
+static void
+cmd_fc_del_default_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_del_default_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_fc_del_default(app, params->pipeline_id);
+	if (status != 0)
+		printf("Command failed\n");
 }
+
+cmdline_parse_token_string_t cmd_fc_del_default_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_default_result, p_string,
+		"p");
+
+cmdline_parse_token_num_t cmd_fc_del_default_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_del_default_result, pipeline_id,
+		UINT32);
+
+cmdline_parse_token_string_t cmd_fc_del_default_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_default_result, flow_string,
+		"flow");
+
+cmdline_parse_token_string_t cmd_fc_del_default_del_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_default_result, del_string,
+		"del");
+
+cmdline_parse_token_string_t cmd_fc_del_default_default_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_del_default_result,
+		default_string, "default");
+
+cmdline_parse_inst_t cmd_fc_del_default = {
+	.f = cmd_fc_del_default_parsed,
+	.data = NULL,
+	.help_str = "Flow delete default",
+	.tokens = {
+		(void *) &cmd_fc_del_default_p_string,
+		(void *) &cmd_fc_del_default_pipeline_id,
+		(void *) &cmd_fc_del_default_flow_string,
+		(void *) &cmd_fc_del_default_del_string,
+		(void *) &cmd_fc_del_default_default_string,
+		NULL,
+	},
+};
+
+/*
+ * flow ls
+ */
+
+struct cmd_fc_ls_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t ls_string;
+};
+
+static void
+cmd_fc_ls_parsed(
+	void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fc_ls_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_fc_ls(app, params->pipeline_id);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fc_ls_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_ls_result, p_string, "p");
+
+cmdline_parse_token_num_t cmd_fc_ls_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fc_ls_result, pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fc_ls_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_ls_result,
+	flow_string, "flow");
+
+cmdline_parse_token_string_t cmd_fc_ls_ls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fc_ls_result, ls_string,
+	"ls");
+
+cmdline_parse_inst_t cmd_fc_ls = {
+	.f = cmd_fc_ls_parsed,
+	.data = NULL,
+	.help_str = "Flow list",
+	.tokens = {
+		(void *) &cmd_fc_ls_p_string,
+		(void *) &cmd_fc_ls_pipeline_id,
+		(void *) &cmd_fc_ls_flow_string,
+		(void *) &cmd_fc_ls_ls_string,
+		NULL,
+	},
+};
+
+static cmdline_parse_ctx_t pipeline_cmds[] = {
+	(cmdline_parse_inst_t *) &cmd_fc_add_qinq,
+	(cmdline_parse_inst_t *) &cmd_fc_add_ipv4_5tuple,
+	(cmdline_parse_inst_t *) &cmd_fc_add_ipv6_5tuple,
+
+	(cmdline_parse_inst_t *) &cmd_fc_del_qinq,
+	(cmdline_parse_inst_t *) &cmd_fc_del_ipv4_5tuple,
+	(cmdline_parse_inst_t *) &cmd_fc_del_ipv6_5tuple,
+
+	(cmdline_parse_inst_t *) &cmd_fc_add_default,
+	(cmdline_parse_inst_t *) &cmd_fc_del_default,
+
+	(cmdline_parse_inst_t *) &cmd_fc_add_qinq_all,
+	(cmdline_parse_inst_t *) &cmd_fc_add_ipv4_5tuple_all,
+	(cmdline_parse_inst_t *) &cmd_fc_add_ipv6_5tuple_all,
+
+	(cmdline_parse_inst_t *) &cmd_fc_ls,
+	NULL,
+};
+
+static struct pipeline_fe_ops pipeline_flow_classification_fe_ops = {
+	.f_init = app_pipeline_fc_init,
+	.f_free = app_pipeline_fc_free,
+	.cmds = pipeline_cmds,
+};
+
+struct pipeline_type pipeline_flow_classification = {
+	.name = "FLOW_CLASSIFICATION",
+	.be_ops = &pipeline_flow_classification_be_ops,
+	.fe_ops = &pipeline_flow_classification_fe_ops,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_classification.h b/examples/ip_pipeline/pipeline/pipeline_flow_classification.h
new file mode 100644
index 0000000..7529314
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_classification.h
@@ -0,0 +1,105 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_FLOW_CLASSIFICATION_H__
+#define __INCLUDE_PIPELINE_FLOW_CLASSIFICATION_H__
+
+#include "pipeline.h"
+#include "pipeline_flow_classification_be.h"
+
+enum flow_key_type {
+	FLOW_KEY_QINQ,
+	FLOW_KEY_IPV4_5TUPLE,
+	FLOW_KEY_IPV6_5TUPLE,
+};
+
+struct flow_key_qinq {
+	uint16_t svlan;
+	uint16_t cvlan;
+};
+
+struct flow_key_ipv4_5tuple {
+	uint32_t ip_src;
+	uint32_t ip_dst;
+	uint16_t port_src;
+	uint16_t port_dst;
+	uint32_t proto;
+};
+
+struct flow_key_ipv6_5tuple {
+	uint8_t ip_src[16];
+	uint8_t ip_dst[16];
+	uint16_t port_src;
+	uint16_t port_dst;
+	uint32_t proto;
+};
+
+struct pipeline_fc_key {
+	enum flow_key_type type;
+	union {
+		struct flow_key_qinq qinq;
+		struct flow_key_ipv4_5tuple ipv4_5tuple;
+		struct flow_key_ipv6_5tuple ipv6_5tuple;
+	} key;
+};
+
+int
+app_pipeline_fc_add(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_fc_key *key,
+	uint32_t port_id);
+
+int
+app_pipeline_fc_add_bulk(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_fc_key *key,
+	uint32_t *port_id,
+	uint32_t n_keys);
+
+int
+app_pipeline_fc_del(struct app_params *app,
+	uint32_t pipeline_id,
+	struct pipeline_fc_key *key);
+
+int
+app_pipeline_fc_add_default(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t port_id);
+
+int
+app_pipeline_fc_del_default(struct app_params *app,
+	uint32_t pipeline_id);
+
+extern struct pipeline_type pipeline_flow_classification;
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_classification_be.c b/examples/ip_pipeline/pipeline/pipeline_flow_classification_be.c
new file mode 100644
index 0000000..06a648d
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_classification_be.c
@@ -0,0 +1,589 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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 <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_table_hash.h>
+#include <rte_byteorder.h>
+
+#include "pipeline_flow_classification_be.h"
+#include "hash_func.h"
+
+struct pipeline_flow_classification {
+	struct pipeline p;
+	pipeline_msg_req_handler custom_handlers[PIPELINE_FC_MSG_REQS];
+
+	uint32_t n_flows;
+	uint32_t key_offset;
+	uint32_t key_size;
+	uint32_t hash_offset;
+} __rte_cache_aligned;
+
+static void *
+pipeline_fc_msg_req_custom_handler(struct pipeline *p, void *msg);
+
+static pipeline_msg_req_handler handlers[] = {
+	[PIPELINE_MSG_REQ_PING] =
+		pipeline_msg_req_ping_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_IN] =
+		pipeline_msg_req_stats_port_in_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_OUT] =
+		pipeline_msg_req_stats_port_out_handler,
+	[PIPELINE_MSG_REQ_STATS_TABLE] =
+		pipeline_msg_req_stats_table_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_ENABLE] =
+		pipeline_msg_req_port_in_enable_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_DISABLE] =
+		pipeline_msg_req_port_in_disable_handler,
+	[PIPELINE_MSG_REQ_CUSTOM] =
+		pipeline_fc_msg_req_custom_handler,
+};
+
+static void *
+pipeline_fc_msg_req_add_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_fc_msg_req_add_bulk_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_fc_msg_req_del_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_fc_msg_req_add_default_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_fc_msg_req_del_default_handler(struct pipeline *p, void *msg);
+
+static pipeline_msg_req_handler custom_handlers[] = {
+	[PIPELINE_FC_MSG_REQ_FLOW_ADD] =
+		pipeline_fc_msg_req_add_handler,
+	[PIPELINE_FC_MSG_REQ_FLOW_ADD_BULK] =
+		pipeline_fc_msg_req_add_bulk_handler,
+	[PIPELINE_FC_MSG_REQ_FLOW_DEL] =
+		pipeline_fc_msg_req_del_handler,
+	[PIPELINE_FC_MSG_REQ_FLOW_ADD_DEFAULT] =
+		pipeline_fc_msg_req_add_default_handler,
+	[PIPELINE_FC_MSG_REQ_FLOW_DEL_DEFAULT] =
+		pipeline_fc_msg_req_del_default_handler,
+};
+
+/*
+ * Flow table
+ */
+struct flow_table_entry {
+	struct rte_pipeline_table_entry head;
+};
+
+rte_table_hash_op_hash hash_func[] = {
+	hash_default_key8,
+	hash_default_key16,
+	hash_default_key24,
+	hash_default_key32,
+	hash_default_key40,
+	hash_default_key48,
+	hash_default_key56,
+	hash_default_key64
+};
+
+static int
+pipeline_fc_parse_args(struct pipeline_flow_classification *p,
+	struct pipeline_params *params)
+{
+	uint32_t n_flows_present = 0;
+	uint32_t key_offset_present = 0;
+	uint32_t key_size_present = 0;
+	uint32_t hash_offset_present = 0;
+
+	uint32_t i;
+
+	for (i = 0; i < params->n_args; i++) {
+		char *arg_name = params->args_name[i];
+		char *arg_value = params->args_value[i];
+
+		/* n_flows */
+		if (strcmp(arg_name, "n_flows") == 0) {
+			if (n_flows_present)
+				return -1;
+			n_flows_present = 1;
+
+			p->n_flows = atoi(arg_value);
+			if (p->n_flows == 0)
+				return -1;
+
+			continue;
+		}
+
+		/* key_offset */
+		if (strcmp(arg_name, "key_offset") == 0) {
+			if (key_offset_present)
+				return -1;
+			key_offset_present = 1;
+
+			p->key_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* key_size */
+		if (strcmp(arg_name, "key_size") == 0) {
+			if (key_size_present)
+				return -1;
+			key_size_present = 1;
+
+			p->key_size = atoi(arg_value);
+			if ((p->key_size == 0) ||
+				(p->key_size > PIPELINE_FC_FLOW_KEY_MAX_SIZE) ||
+				(p->key_size % 8))
+				return -1;
+
+			continue;
+		}
+
+		/* hash_offset */
+		if (strcmp(arg_name, "hash_offset") == 0) {
+			if (hash_offset_present)
+				return -1;
+			hash_offset_present = 1;
+
+			p->hash_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* Unknown argument */
+		return -1;
+	}
+
+	/* Check that mandatory arguments are present */
+	if ((n_flows_present == 0) ||
+		(key_offset_present == 0) ||
+		(key_size_present == 0) ||
+		(hash_offset_present == 0))
+		return -1;
+
+	return 0;
+}
+
+static void *pipeline_fc_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct pipeline *p;
+	struct pipeline_flow_classification *p_fc;
+	uint32_t size, i;
+
+	/* Check input arguments */
+	if (params == NULL)
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(
+		sizeof(struct pipeline_flow_classification));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		return NULL;
+	p_fc = (struct pipeline_flow_classification *) p;
+
+	strcpy(p->name, params->name);
+	p->log_level = params->log_level;
+
+	PLOG(p, HIGH, "Flow classification");
+
+	/* Parse arguments */
+	if (pipeline_fc_parse_args(p_fc, params))
+		return NULL;
+
+	/* Pipeline */
+	{
+		struct rte_pipeline_params pipeline_params = {
+			.name = params->name,
+			.socket_id = params->socket_id,
+			.offset_port_id = 0,
+		};
+
+		p->p = rte_pipeline_create(&pipeline_params);
+		if (p->p == NULL) {
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Input ports */
+	p->n_ports_in = params->n_ports_in;
+	for (i = 0; i < p->n_ports_in; i++) {
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = pipeline_port_in_params_get_ops(
+				&params->port_in[i]),
+			.arg_create = pipeline_port_in_params_convert(
+				&params->port_in[i]),
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = params->port_in[i].burst_size,
+		};
+
+		int status = rte_pipeline_port_in_create(p->p,
+			&port_params,
+			&p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Output ports */
+	p->n_ports_out = params->n_ports_out;
+	for (i = 0; i < p->n_ports_out; i++) {
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = pipeline_port_out_params_get_ops(
+				&params->port_out[i]),
+			.arg_create = pipeline_port_out_params_convert(
+				&params->port_out[i]),
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		int status = rte_pipeline_port_out_create(p->p,
+			&port_params,
+			&p->port_out_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Tables */
+	p->n_tables = 1;
+	{
+		struct rte_table_hash_key8_ext_params
+			table_hash_key8_params = {
+			.n_entries = p_fc->n_flows,
+			.n_entries_ext = p_fc->n_flows,
+			.signature_offset = p_fc->hash_offset,
+			.key_offset = p_fc->key_offset,
+			.f_hash = hash_func[(p_fc->key_size / 8) - 1],
+			.seed = 0,
+		};
+
+		struct rte_table_hash_key16_ext_params
+			table_hash_key16_params = {
+			.n_entries = p_fc->n_flows,
+			.n_entries_ext = p_fc->n_flows,
+			.signature_offset = p_fc->hash_offset,
+			.key_offset = p_fc->key_offset,
+			.f_hash = hash_func[(p_fc->key_size / 8) - 1],
+			.seed = 0,
+		};
+
+		struct rte_table_hash_ext_params
+			table_hash_params = {
+			.key_size = p_fc->key_size,
+			.n_keys = p_fc->n_flows,
+			.n_buckets = p_fc->n_flows / 4,
+			.n_buckets_ext = p_fc->n_flows / 4,
+			.f_hash = hash_func[(p_fc->key_size / 8) - 1],
+			.seed = 0,
+			.signature_offset = p_fc->hash_offset,
+			.key_offset = p_fc->key_offset,
+		};
+
+		struct rte_pipeline_table_params table_params = {
+			.ops = NULL, /* set below */
+			.arg_create = NULL, /* set below */
+			.f_action_hit = NULL,
+			.f_action_miss = NULL,
+			.arg_ah = NULL,
+			.action_data_size = sizeof(struct flow_table_entry) -
+				sizeof(struct rte_pipeline_table_entry),
+		};
+
+		int status;
+
+		switch (p_fc->key_size) {
+		case 8:
+			table_params.ops = &rte_table_hash_key8_lru_ops;
+			table_params.arg_create = &table_hash_key8_params;
+			break;
+
+		case 16:
+			table_params.ops = &rte_table_hash_key16_ext_ops;
+			table_params.arg_create = &table_hash_key16_params;
+			break;
+
+		default:
+			table_params.ops = &rte_table_hash_ext_ops;
+			table_params.arg_create = &table_hash_params;
+		}
+
+		status = rte_pipeline_table_create(p->p,
+			&table_params,
+			&p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Connecting input ports to tables */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_connect_to_table(p->p,
+			p->port_in_id[i],
+			p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Enable input ports */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_enable(p->p,
+			p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p->p) < 0) {
+		rte_pipeline_free(p->p);
+		rte_free(p);
+		return NULL;
+	}
+
+	/* Message queues */
+	p->n_msgq = params->n_msgq;
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_in[i] = params->msgq_in[i];
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_out[i] = params->msgq_out[i];
+
+	/* Message handlers */
+	memcpy(p->handlers, handlers, sizeof(p->handlers));
+	memcpy(p_fc->custom_handlers,
+		custom_handlers,
+		sizeof(p_fc->custom_handlers));
+
+	return p;
+}
+
+static int
+pipeline_fc_free(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	rte_pipeline_free(p->p);
+	rte_free(p);
+	return 0;
+}
+
+static int
+pipeline_fc_track(void *pipeline,
+	__rte_unused uint32_t port_in,
+	uint32_t *port_out)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if ((p == NULL) ||
+		(port_in >= p->n_ports_in) ||
+		(port_out == NULL))
+		return -1;
+
+	if (p->n_ports_in == 1) {
+		*port_out = 0;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int
+pipeline_fc_timer(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	pipeline_msg_req_handle(p);
+	rte_pipeline_flush(p->p);
+
+	return 0;
+}
+
+static void *
+pipeline_fc_msg_req_custom_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_flow_classification *p_fc =
+			(struct pipeline_flow_classification *) p;
+	struct pipeline_custom_msg_req *req = msg;
+	pipeline_msg_req_handler f_handle;
+
+	f_handle = (req->subtype < PIPELINE_FC_MSG_REQS) ?
+		p_fc->custom_handlers[req->subtype] :
+		pipeline_msg_req_invalid_handler;
+
+	if (f_handle == NULL)
+		f_handle = pipeline_msg_req_invalid_handler;
+
+	return f_handle(p, req);
+}
+
+static void *
+pipeline_fc_msg_req_add_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_fc_add_msg_req *req = msg;
+	struct pipeline_fc_add_msg_rsp *rsp = msg;
+
+	struct flow_table_entry entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->port_id]},
+		},
+	};
+
+	rsp->status = rte_pipeline_table_entry_add(p->p,
+		p->table_id[0],
+		&req->key,
+		(struct rte_pipeline_table_entry *) &entry,
+		&rsp->key_found,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+static void *
+pipeline_fc_msg_req_add_bulk_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_fc_add_bulk_msg_req *req = msg;
+	struct pipeline_fc_add_bulk_msg_rsp *rsp = msg;
+	uint32_t i;
+
+	for (i = 0; i < req->n_keys; i++) {
+		struct pipeline_fc_add_bulk_flow_req *flow_req = &req->req[i];
+		struct pipeline_fc_add_bulk_flow_rsp *flow_rsp = &req->rsp[i];
+
+		struct flow_table_entry entry = {
+			.head = {
+				.action = RTE_PIPELINE_ACTION_PORT,
+				{.port_id = p->port_out_id[flow_req->port_id]},
+			},
+		};
+
+		int status = rte_pipeline_table_entry_add(p->p,
+			p->table_id[0],
+			&flow_req->key,
+			(struct rte_pipeline_table_entry *) &entry,
+			&flow_rsp->key_found,
+			(struct rte_pipeline_table_entry **)
+				&flow_rsp->entry_ptr);
+
+		if (status)
+			break;
+	}
+
+	rsp->n_keys = i;
+
+	return rsp;
+}
+
+static void *
+pipeline_fc_msg_req_del_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_fc_del_msg_req *req = msg;
+	struct pipeline_fc_del_msg_rsp *rsp = msg;
+
+	rsp->status = rte_pipeline_table_entry_delete(p->p,
+		p->table_id[0],
+		&req->key,
+		&rsp->key_found,
+		NULL);
+
+	return rsp;
+}
+
+static void *
+pipeline_fc_msg_req_add_default_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_fc_add_default_msg_req *req = msg;
+	struct pipeline_fc_add_default_msg_rsp *rsp = msg;
+
+	struct flow_table_entry default_entry = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->port_id]},
+		},
+	};
+
+	rsp->status = rte_pipeline_table_default_entry_add(p->p,
+		p->table_id[0],
+		(struct rte_pipeline_table_entry *) &default_entry,
+		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
+
+	return rsp;
+}
+
+static void *
+pipeline_fc_msg_req_del_default_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_fc_del_default_msg_rsp *rsp = msg;
+
+	rsp->status = rte_pipeline_table_default_entry_delete(p->p,
+		p->table_id[0],
+		NULL);
+
+	return rsp;
+}
+
+struct pipeline_be_ops pipeline_flow_classification_be_ops = {
+	.f_init = pipeline_fc_init,
+	.f_free = pipeline_fc_free,
+	.f_run = NULL,
+	.f_timer = pipeline_fc_timer,
+	.f_track = pipeline_fc_track,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_classification_be.h b/examples/ip_pipeline/pipeline/pipeline_flow_classification_be.h
new file mode 100644
index 0000000..46403d5
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_classification_be.h
@@ -0,0 +1,140 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_FLOW_CLASSIFICATION_BE_H__
+#define __INCLUDE_PIPELINE_FLOW_CLASSIFICATION_BE_H__
+
+#include "pipeline_common_be.h"
+
+enum pipeline_fc_msg_req_type {
+	PIPELINE_FC_MSG_REQ_FLOW_ADD = 0,
+	PIPELINE_FC_MSG_REQ_FLOW_ADD_BULK,
+	PIPELINE_FC_MSG_REQ_FLOW_DEL,
+	PIPELINE_FC_MSG_REQ_FLOW_ADD_DEFAULT,
+	PIPELINE_FC_MSG_REQ_FLOW_DEL_DEFAULT,
+	PIPELINE_FC_MSG_REQS,
+};
+
+#ifndef PIPELINE_FC_FLOW_KEY_MAX_SIZE
+#define PIPELINE_FC_FLOW_KEY_MAX_SIZE            64
+#endif
+
+/*
+ * MSG ADD
+ */
+struct pipeline_fc_add_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fc_msg_req_type subtype;
+
+	uint8_t key[PIPELINE_FC_FLOW_KEY_MAX_SIZE];
+
+	uint32_t port_id;
+};
+
+struct pipeline_fc_add_msg_rsp {
+	int status;
+	int key_found;
+	void *entry_ptr;
+};
+
+/*
+ * MSG ADD BULK
+ */
+struct pipeline_fc_add_bulk_flow_req {
+	uint8_t key[PIPELINE_FC_FLOW_KEY_MAX_SIZE];
+	uint32_t port_id;
+};
+
+struct pipeline_fc_add_bulk_flow_rsp {
+	int key_found;
+	void *entry_ptr;
+};
+
+struct pipeline_fc_add_bulk_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fc_msg_req_type subtype;
+
+	struct pipeline_fc_add_bulk_flow_req *req;
+	struct pipeline_fc_add_bulk_flow_rsp *rsp;
+	uint32_t n_keys;
+};
+
+struct pipeline_fc_add_bulk_msg_rsp {
+	uint32_t n_keys;
+};
+
+/*
+ * MSG DEL
+ */
+struct pipeline_fc_del_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fc_msg_req_type subtype;
+
+	uint8_t key[PIPELINE_FC_FLOW_KEY_MAX_SIZE];
+};
+
+struct pipeline_fc_del_msg_rsp {
+	int status;
+	int key_found;
+};
+
+/*
+ * MSG ADD DEFAULT
+ */
+struct pipeline_fc_add_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fc_msg_req_type subtype;
+
+	uint32_t port_id;
+};
+
+struct pipeline_fc_add_default_msg_rsp {
+	int status;
+	void *entry_ptr;
+};
+
+/*
+ * MSG DEL DEFAULT
+ */
+struct pipeline_fc_del_default_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fc_msg_req_type subtype;
+};
+
+struct pipeline_fc_del_default_msg_rsp {
+	int status;
+};
+
+extern struct pipeline_be_ops pipeline_flow_classification_be_ops;
+
+#endif
-- 
1.7.9.5

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

* Re: [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (10 preceding siblings ...)
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 11/11] ip_pipeline: added new implementation of flow classification pipeline Maciej Gajdzica
@ 2015-07-07  8:27 ` Thomas Monjalon
  2015-07-07 11:33   ` Dumitrescu, Cristian
  2015-07-07  9:23 ` Dumitrescu, Cristian
  12 siblings, 1 reply; 21+ messages in thread
From: Thomas Monjalon @ 2015-07-07  8:27 UTC (permalink / raw)
  To: Maciej Gajdzica; +Cc: dev

2015-07-07 10:09, Maciej Gajdzica:
> Changes in v4:
> - fixed build issue with gcc 5
> - fixed bugs in flow classification and firewall pipelines
> 
> Changes in v5:
> - fixed build issue with clang 3.6
> 
> Changes in v6:
> - fixed build issues with icc 15
> - fixed build issues with gcc 32bit

Please, from now, avoid sending patches without having checked build with
GCC 5 32/64-bit, clang and ICC if possible.
Thanks

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

* Re: [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements
  2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
                   ` (11 preceding siblings ...)
  2015-07-07  8:27 ` [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Thomas Monjalon
@ 2015-07-07  9:23 ` Dumitrescu, Cristian
  2015-07-09 15:27   ` Thomas Monjalon
  12 siblings, 1 reply; 21+ messages in thread
From: Dumitrescu, Cristian @ 2015-07-07  9:23 UTC (permalink / raw)
  To: Gajdzica, MaciejX T, dev



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Maciej Gajdzica
> Sent: Tuesday, July 7, 2015 9:09 AM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application
> enhancements
> 
> Changes in v6:
> - fixed build issues with icc 15
> - fixed build issues with gcc 32bit
> 

Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>

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

* Re: [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements
  2015-07-07  8:27 ` [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Thomas Monjalon
@ 2015-07-07 11:33   ` Dumitrescu, Cristian
  0 siblings, 0 replies; 21+ messages in thread
From: Dumitrescu, Cristian @ 2015-07-07 11:33 UTC (permalink / raw)
  To: Thomas Monjalon, Gajdzica, MaciejX T; +Cc: dev


> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas Monjalon
> Sent: Tuesday, July 7, 2015 9:28 AM
> To: Gajdzica, MaciejX T
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application
> enhancements
> 
> 2015-07-07 10:09, Maciej Gajdzica:
> > Changes in v4:
> > - fixed build issue with gcc 5
> > - fixed bugs in flow classification and firewall pipelines
> >
> > Changes in v5:
> > - fixed build issue with clang 3.6
> >
> > Changes in v6:
> > - fixed build issues with icc 15
> > - fixed build issues with gcc 32bit
> 
> Please, from now, avoid sending patches without having checked build with
> GCC 5 32/64-bit, clang and ICC if possible.
> Thanks

Will do, apologies for all these issues. Thanks, Thomas!

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

* Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
  2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline Maciej Gajdzica
@ 2015-07-08 22:33   ` Thomas Monjalon
  2015-07-09  8:03     ` Gajdzica, MaciejX T
  2015-07-09  9:58     ` Gajdzica, MaciejX T
  0 siblings, 2 replies; 21+ messages in thread
From: Thomas Monjalon @ 2015-07-08 22:33 UTC (permalink / raw)
  To: Maciej Gajdzica, Jasvinder Singh; +Cc: dev

2015-07-07 10:09, Maciej Gajdzica:
> From: Jasvinder Singh <jasvinder.singh@intel.com>
> 
> Master pipeline is responsible for command line handling and
> communicationg with all other pipelines via message queues. Removed
> cmdline.c file, as its functionality will be split over multiple
> pipeline files.
> 
> Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>

In this v6, it builds well until patch 05 which uses some not exported symbols:
pipeline_common_be.c:(.text+0x51): undefined reference to `rte_pipeline_port_in_stats_read'
pipeline_common_be.c:(.text+0x91): undefined reference to `rte_pipeline_port_out_stats_read'
pipeline_common_be.c:(.text+0xd4): undefined reference to `rte_pipeline_table_stats_read'

Please check build with shared libraries.

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

* Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
  2015-07-08 22:33   ` Thomas Monjalon
@ 2015-07-09  8:03     ` Gajdzica, MaciejX T
  2015-07-09  9:58     ` Gajdzica, MaciejX T
  1 sibling, 0 replies; 21+ messages in thread
From: Gajdzica, MaciejX T @ 2015-07-09  8:03 UTC (permalink / raw)
  To: Thomas Monjalon, Singh, Jasvinder; +Cc: dev

> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> Sent: Thursday, July 09, 2015 12:33 AM
> To: Gajdzica, MaciejX T; Singh, Jasvinder
> Cc: dev@dpdk.org; Dumitrescu, Cristian
> Subject: Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
> 
> 2015-07-07 10:09, Maciej Gajdzica:
> > From: Jasvinder Singh <jasvinder.singh@intel.com>
> >
> > Master pipeline is responsible for command line handling and
> > communicationg with all other pipelines via message queues. Removed
> > cmdline.c file, as its functionality will be split over multiple
> > pipeline files.
> >
> > Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
> 
> In this v6, it builds well until patch 05 which uses some not exported symbols:
> pipeline_common_be.c:(.text+0x51): undefined reference to
> `rte_pipeline_port_in_stats_read'
> pipeline_common_be.c:(.text+0x91): undefined reference to
> `rte_pipeline_port_out_stats_read'
> pipeline_common_be.c:(.text+0xd4): undefined reference to
> `rte_pipeline_table_stats_read'
> 
> Please check build with shared libraries.

There are missing symbols in map file for librte_port and librte_pipeline. I will send separate patch for that.

Best Regards
Maciek

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

* Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
  2015-07-08 22:33   ` Thomas Monjalon
  2015-07-09  8:03     ` Gajdzica, MaciejX T
@ 2015-07-09  9:58     ` Gajdzica, MaciejX T
  2015-07-09 10:00       ` Dumitrescu, Cristian
  1 sibling, 1 reply; 21+ messages in thread
From: Gajdzica, MaciejX T @ 2015-07-09  9:58 UTC (permalink / raw)
  To: Thomas Monjalon, Singh, Jasvinder; +Cc: dev



> -----Original Message-----
> From: Gajdzica, MaciejX T
> Sent: Thursday, July 09, 2015 10:03 AM
> To: 'Thomas Monjalon'; Singh, Jasvinder
> Cc: dev@dpdk.org; Dumitrescu, Cristian
> Subject: RE: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
> 
> > -----Original Message-----
> > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > Sent: Thursday, July 09, 2015 12:33 AM
> > To: Gajdzica, MaciejX T; Singh, Jasvinder
> > Cc: dev@dpdk.org; Dumitrescu, Cristian
> > Subject: Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master
> > pipeline
> >
> > 2015-07-07 10:09, Maciej Gajdzica:
> > > From: Jasvinder Singh <jasvinder.singh@intel.com>
> > >
> > > Master pipeline is responsible for command line handling and
> > > communicationg with all other pipelines via message queues. Removed
> > > cmdline.c file, as its functionality will be split over multiple
> > > pipeline files.
> > >
> > > Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
> >
> > In this v6, it builds well until patch 05 which uses some not exported symbols:
> > pipeline_common_be.c:(.text+0x51): undefined reference to
> > `rte_pipeline_port_in_stats_read'
> > pipeline_common_be.c:(.text+0x91): undefined reference to
> > `rte_pipeline_port_out_stats_read'
> > pipeline_common_be.c:(.text+0xd4): undefined reference to
> > `rte_pipeline_table_stats_read'
> >
> > Please check build with shared libraries.
> 
> There are missing symbols in map file for librte_port and librte_pipeline. I will
> send separate patch for that.

This compilation problem with shared libraries is not because something with this
patchset is wrong, but because exported symbols in map file in earlier patchsets are
missing. So after separate patches with fix will be applied, could you restore this patchset?

Best Regards
Maciek

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

* Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
  2015-07-09  9:58     ` Gajdzica, MaciejX T
@ 2015-07-09 10:00       ` Dumitrescu, Cristian
  2015-07-09 10:06         ` Gajdzica, MaciejX T
  0 siblings, 1 reply; 21+ messages in thread
From: Dumitrescu, Cristian @ 2015-07-09 10:00 UTC (permalink / raw)
  To: Gajdzica, MaciejX T, Thomas Monjalon, Singh, Jasvinder; +Cc: dev



> -----Original Message-----
> From: Gajdzica, MaciejX T
> Sent: Thursday, July 9, 2015 12:58 PM
> To: Thomas Monjalon; Singh, Jasvinder
> Cc: dev@dpdk.org; Dumitrescu, Cristian
> Subject: RE: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
> 
> 
> 
> > -----Original Message-----
> > From: Gajdzica, MaciejX T
> > Sent: Thursday, July 09, 2015 10:03 AM
> > To: 'Thomas Monjalon'; Singh, Jasvinder
> > Cc: dev@dpdk.org; Dumitrescu, Cristian
> > Subject: RE: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master
> pipeline
> >
> > > -----Original Message-----
> > > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > > Sent: Thursday, July 09, 2015 12:33 AM
> > > To: Gajdzica, MaciejX T; Singh, Jasvinder
> > > Cc: dev@dpdk.org; Dumitrescu, Cristian
> > > Subject: Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master
> > > pipeline
> > >
> > > 2015-07-07 10:09, Maciej Gajdzica:
> > > > From: Jasvinder Singh <jasvinder.singh@intel.com>
> > > >
> > > > Master pipeline is responsible for command line handling and
> > > > communicationg with all other pipelines via message queues. Removed
> > > > cmdline.c file, as its functionality will be split over multiple
> > > > pipeline files.
> > > >
> > > > Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
> > >
> > > In this v6, it builds well until patch 05 which uses some not exported
> symbols:
> > > pipeline_common_be.c:(.text+0x51): undefined reference to
> > > `rte_pipeline_port_in_stats_read'
> > > pipeline_common_be.c:(.text+0x91): undefined reference to
> > > `rte_pipeline_port_out_stats_read'
> > > pipeline_common_be.c:(.text+0xd4): undefined reference to
> > > `rte_pipeline_table_stats_read'
> > >
> > > Please check build with shared libraries.
> >
> > There are missing symbols in map file for librte_port and librte_pipeline. I
> will
> > send separate patch for that.
> 
> This compilation problem with shared libraries is not because something with
> this
> patchset is wrong, but because exported symbols in map file in earlier
> patchsets are
> missing. So after separate patches with fix will be applied, could you restore
> this patchset?
> 
> Best Regards
> Maciek

Hi Maciek, I suggest we add the missing symbols to the librte_port, lib_rte_table and librte_pipeline as a new patch; this should fix the v6 patch series on ip_pipeline without any need for v7? Thanks, Cristian

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

* Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
  2015-07-09 10:00       ` Dumitrescu, Cristian
@ 2015-07-09 10:06         ` Gajdzica, MaciejX T
  0 siblings, 0 replies; 21+ messages in thread
From: Gajdzica, MaciejX T @ 2015-07-09 10:06 UTC (permalink / raw)
  To: Dumitrescu, Cristian, Thomas Monjalon, Singh, Jasvinder; +Cc: dev



> -----Original Message-----
> From: Dumitrescu, Cristian
> Sent: Thursday, July 09, 2015 12:01 PM
> To: Gajdzica, MaciejX T; Thomas Monjalon; Singh, Jasvinder
> Cc: dev@dpdk.org
> Subject: RE: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline
> 
> 
> 
> > -----Original Message-----
> > From: Gajdzica, MaciejX T
> > Sent: Thursday, July 9, 2015 12:58 PM
> > To: Thomas Monjalon; Singh, Jasvinder
> > Cc: dev@dpdk.org; Dumitrescu, Cristian
> > Subject: RE: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master
> > pipeline
> >
> >
> >
> > > -----Original Message-----
> > > From: Gajdzica, MaciejX T
> > > Sent: Thursday, July 09, 2015 10:03 AM
> > > To: 'Thomas Monjalon'; Singh, Jasvinder
> > > Cc: dev@dpdk.org; Dumitrescu, Cristian
> > > Subject: RE: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master
> > pipeline
> > >
> > > > -----Original Message-----
> > > > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > > > Sent: Thursday, July 09, 2015 12:33 AM
> > > > To: Gajdzica, MaciejX T; Singh, Jasvinder
> > > > Cc: dev@dpdk.org; Dumitrescu, Cristian
> > > > Subject: Re: [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master
> > > > pipeline
> > > >
> > > > 2015-07-07 10:09, Maciej Gajdzica:
> > > > > From: Jasvinder Singh <jasvinder.singh@intel.com>
> > > > >
> > > > > Master pipeline is responsible for command line handling and
> > > > > communicationg with all other pipelines via message queues.
> > > > > Removed cmdline.c file, as its functionality will be split over
> > > > > multiple pipeline files.
> > > > >
> > > > > Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
> > > >
> > > > In this v6, it builds well until patch 05 which uses some not
> > > > exported
> > symbols:
> > > > pipeline_common_be.c:(.text+0x51): undefined reference to
> > > > `rte_pipeline_port_in_stats_read'
> > > > pipeline_common_be.c:(.text+0x91): undefined reference to
> > > > `rte_pipeline_port_out_stats_read'
> > > > pipeline_common_be.c:(.text+0xd4): undefined reference to
> > > > `rte_pipeline_table_stats_read'
> > > >
> > > > Please check build with shared libraries.
> > >
> > > There are missing symbols in map file for librte_port and
> > > librte_pipeline. I
> > will
> > > send separate patch for that.
> >
> > This compilation problem with shared libraries is not because
> > something with this patchset is wrong, but because exported symbols in
> > map file in earlier patchsets are missing. So after separate patches
> > with fix will be applied, could you restore this patchset?
> >
> > Best Regards
> > Maciek
> 
> Hi Maciek, I suggest we add the missing symbols to the librte_port, lib_rte_table
> and librte_pipeline as a new patch; this should fix the v6 patch series on
> ip_pipeline without any need for v7? Thanks, Cristian

Hi Cristian

I already sent new patch with this fix:

http://dpdk.org/ml/archives/dev/2015-July/021194.html
http://dpdk.org/ml/archives/dev/2015-July/021193.html

Best Regards
Maciek

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

* Re: [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements
  2015-07-07  9:23 ` Dumitrescu, Cristian
@ 2015-07-09 15:27   ` Thomas Monjalon
  0 siblings, 0 replies; 21+ messages in thread
From: Thomas Monjalon @ 2015-07-09 15:27 UTC (permalink / raw)
  To: Gajdzica, MaciejX T; +Cc: dev

2015-07-07 09:23, Dumitrescu, Cristian:
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Maciej Gajdzica
> > Changes in v6:
> > - fixed build issues with icc 15
> > - fixed build issues with gcc 32bit
> 
> Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>

Applied, thanks

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

end of thread, other threads:[~2015-07-09 15:28 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-07  8:09 [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 01/11] ip_pipeline: add parsing for config files with new syntax Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 02/11] ip_pipeline: added config checks Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 03/11] ip_pipeline: modified init to match new params struct Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 04/11] ip_pipeline: moved pipelines to separate folder Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 05/11] ip_pipeline: added master pipeline Maciej Gajdzica
2015-07-08 22:33   ` Thomas Monjalon
2015-07-09  8:03     ` Gajdzica, MaciejX T
2015-07-09  9:58     ` Gajdzica, MaciejX T
2015-07-09 10:00       ` Dumitrescu, Cristian
2015-07-09 10:06         ` Gajdzica, MaciejX T
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 06/11] ip_pipeline: added application thread Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 07/11] ip_pipeline: moved config files to separate folder Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 08/11] ip_pipeline: added new implementation of passthrough pipeline Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 09/11] ip_pipeline: added new implementation of firewall pipeline Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 10/11] ip_pipeline: added new implementation of routing pipeline Maciej Gajdzica
2015-07-07  8:09 ` [dpdk-dev] [PATCH v6 11/11] ip_pipeline: added new implementation of flow classification pipeline Maciej Gajdzica
2015-07-07  8:27 ` [dpdk-dev] [PATCH v6 00/11] ip_pipeline: ip_pipeline application enhancements Thomas Monjalon
2015-07-07 11:33   ` Dumitrescu, Cristian
2015-07-07  9:23 ` Dumitrescu, Cristian
2015-07-09 15:27   ` Thomas Monjalon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).