[dpdk-dev,01/11] ip_pipeline: add parsing for config files with new syntax

Message ID 1432914198-11812-2-git-send-email-maciejx.t.gajdzica@intel.com (mailing list archive)
State Superseded, archived
Headers

Commit Message

Maciej Gajdzica May 29, 2015, 3:43 p.m. UTC
  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       |   27 +-
 examples/ip_pipeline/app.h          |  586 ++++++++++
 examples/ip_pipeline/config.c       |  419 -------
 examples/ip_pipeline/config_parse.c | 2071 +++++++++++++++++++++++++++++++++++
 examples/ip_pipeline/cpu_core_map.c |  465 ++++++++
 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     |   79 ++
 examples/ip_pipeline/pipeline_ops.h |  247 +++++
 10 files changed, 3539 insertions(+), 852 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/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_ops.h
  

Comments

Stephen Hemminger June 1, 2015, 1:34 p.m. UTC | #1
On Fri, 29 May 2015 17:43:08 +0200
Maciej Gajdzica <maciejx.t.gajdzica@intel.com> wrote:

> +/**
> + * Find object of name *name* in *obj_array* which is constant size array of
> + * elements that have field *name*.
> + *
> + * @param obj_array
> + *  Constant size array
> + * @param name
> + *  name of object to find.
> + * @return
> + *  Pointer to object in *obj_array* or NULL if not found.
> + */
> +#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;                    \
> +})

Converting all the functions to macro's is a step backwards in several ways.
 * macro's are hard to support
 * macro's lead to lots of programming errors
 * macro's look ugly

Why not use real functions, or make the example into C++ if you have
to do generic programming.
  
Cristian Dumitrescu June 4, 2015, 5:29 p.m. UTC | #2
> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Stephen
> Hemminger
> Sent: Monday, June 1, 2015 2:34 PM
> To: Gajdzica, MaciejX T
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH 01/11] ip_pipeline: add parsing for config
> files with new syntax
> 
> On Fri, 29 May 2015 17:43:08 +0200
> Maciej Gajdzica <maciejx.t.gajdzica@intel.com> wrote:
> 
> > +/**
> > + * Find object of name *name* in *obj_array* which is constant size array
> of
> > + * elements that have field *name*.
> > + *
> > + * @param obj_array
> > + *  Constant size array
> > + * @param name
> > + *  name of object to find.
> > + * @return
> > + *  Pointer to object in *obj_array* or NULL if not found.
> > + */
> > +#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;                    \
> > +})
> 
> Converting all the functions to macro's is a step backwards in several ways.
>  * macro's are hard to support
>  * macro's lead to lots of programming errors
>  * macro's look ugly
> 
> Why not use real functions, or make the example into C++ if you have
> to do generic programming.

We are using macros here only because C language does not offer us a better choice (i.e. support for templates). The alternative would be to write a quasi-identical function per each object type, which would lead to unnecessary code duplication.

We did our best to keep the number of macros small and to implement each macro as straightforward as possible.

All the DPDK sample applications are written in C, so this is the main reason we want to keep this application as C code. As people expect C code from DPDK sample apps, it is easier for people to reuse parts of this application.
  

Patch

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index e70fdc7..2f224cc 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -43,20 +43,21 @@  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
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += config_parse.c
+#SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += init.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += cpu_core_map.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
+#ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
+#SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall.c
+#endif
 
 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..9ff7454
--- /dev/null
+++ b/examples/ip_pipeline/app.h
@@ -0,0 +1,586 @@ 
+/*-
+ *   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_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_CHECK(exp, fmt, ...)    do {            \
+	if (!(exp)) {                                   \
+		fprintf( stderr, fmt "\n", ## __VA_ARGS__); \
+		abort();                                    \
+	}                                               \
+} while (0)
+
+#ifndef APP_APPNAME_SIZE
+#define APP_APPNAME_SIZE                         256
+#endif
+
+#ifndef APP_FILE_NAME_SIZE
+#define APP_FILE_NAME_SIZE                       256
+#endif
+
+#define APP_MAX_PIPELINE_ARGS                    PIPELINE_MAX_ARGS
+
+#define APP_PARAM_NAME_SIZE                      PIPELINE_NAME_SIZE
+
+#ifndef APP_MAX_SCHED_SUBPORTS
+#define APP_MAX_SCHED_SUBPORTS                   8
+#endif
+
+#ifndef APP_MAX_SCHED_PIPES
+#define APP_MAX_SCHED_PIPES                      4096
+#endif
+
+#ifndef APP_EAL_ARG_BUFFER_SIZE
+#define APP_EAL_ARG_BUFFER_SIZE                  1024
+#endif
+
+#ifndef APP_EAL_ARGC
+#define APP_EAL_ARGC                             16
+#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_MAX_THREAD_PIPELINES
+#define APP_MAX_THREAD_PIPELINES                 16
+#endif
+
+#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
+
+#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
+
+#define APP_PARAM_VALID(obj) ((obj)->name[0] != '\0')
+
+#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 ++;                                          \
+}
+
+/**
+ * Find object of name *name* in *obj_array* which is constant size array of
+ * elements that have field *name*.
+ *
+ * @param obj_array
+ *  Constant size array
+ * @param name
+ *  name of object to find.
+ * @return
+ *  Pointer to object in *obj_array* or NULL if not found.
+ */
+#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 "%u", 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 "%u", &id);                        \
+while (0)                                                       \
+
+/**
+ * Set name of first free slot in *obj_array* to *obj_name*, which makes it
+ * valid. If object already exist return index to existing object.
+ *
+ * @param obj_array
+ *  Constant size array of objects.
+ * @param obj_name
+ *  Name of ne object.
+ * @return
+ *  ssize_t - Index to object or negative error code:
+ *  -EINVAL *name* is too long
+ *  -ENOMEM no free slot found.
+ */
+#define APP_PARAM_ADD(obj_array, obj_name)                         \
+({                                                                 \
+	ssize_t obj_idx;                                               \
+	ssize_t ret;                                                   \
+	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) {                                 \
+			ret = snprintf((obj_array)[obj_idx].name,              \
+					RTE_DIM((obj_array)[obj_idx].name),            \
+					"%s", obj_name);                               \
+			if (ret > (ssize_t)RTE_DIM((obj_array)[obj_idx].name)) \
+				obj_idx = -EINVAL;                                 \
+		} else                                                     \
+			obj_idx = -ENOMEM;                                     \
+	}                                                              \
+	obj_idx;                                                       \
+})
+
+
+#define APP_HWQ_IN_IDX(link, q) ((link) * APP_LINK_MAX_HWQ_IN + (q))
+#define APP_HWQ_OUT_IDX(link, q) ((link) * APP_LINK_MAX_HWQ_OUT + (q))
+
+#define APP_HWQ_IN_LINK_ID(id) (id / APP_LINK_MAX_HWQ_IN)
+#define APP_HWQ_IN_QUEUE_ID(id) (id % APP_LINK_MAX_HWQ_IN)
+
+#define APP_HWQ_OUT_LINK_ID(id) (id / APP_LINK_MAX_HWQ_OUT)
+#define APP_HWQ_OUT_QUEUE_ID(id) (id % APP_LINK_MAX_HWQ_OUT)
+
+
+struct app_mempool_params {
+	char name[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	uint32_t buffer_size;
+	uint32_t pool_size;
+	uint32_t cache_size;
+	uint32_t cpu_socket_id;
+};
+
+struct app_link_params {
+	char name[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	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 (packets go to default queue 0) */
+	uint32_t ip_local_q; /* 0 = Disabled (packets go to default queue 0) */
+	uint32_t tcp_local_q; /* 0 = Disabled (packets go to default queue 0) */
+	uint32_t udp_local_q; /* 0 = Disabled (packets go to default queue 0) */
+	uint32_t sctp_local_q; /* 0 = Disabled (packets 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[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	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[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	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[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	uint32_t size;
+	uint32_t burst_read;
+	uint32_t burst_write;
+	uint32_t dropless;
+	uint64_t n_retries;
+	uint32_t cpu_socket_id;
+};
+
+struct app_pktq_tm_params {
+	char name[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	char file_name[APP_FILE_NAME_SIZE];
+	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[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	uint32_t mempool_id; /* Position in the app->mempool_params array */
+	uint32_t burst;
+};
+
+struct app_pktq_sink_params {
+	char name[APP_PARAM_NAME_SIZE];
+	uint8_t parsed; /* Used to check if object was parsed or only referenced */
+};
+
+struct app_msgq_params {
+	char name[APP_PARAM_NAME_SIZE];
+	uint32_t parsed; /* Used to check if object was parsed or only referenced */
+	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 */
+};
+
+struct app_pipeline_params {
+	char name[APP_PARAM_NAME_SIZE];
+	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_op_run f_run;
+	pipeline_op_timer f_timer;
+	uint64_t timer_period;
+	uint64_t deadline;
+};
+
+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_params {
+	/* Config */
+	char app_name[APP_APPNAME_SIZE];
+	char config_file[APP_FILE_NAME_SIZE];
+	char script_file[APP_FILE_NAME_SIZE];
+
+	char *eal_argv[1 + APP_EAL_ARGC];
+	int eal_argc;
+
+	uint64_t port_mask;
+
+	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 */
+	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];
+
+	uint32_t n_pipeline_types;
+	uint32_t n_cmds;
+};
+
+static inline uint32_t
+app_n_hwq_in_get(struct app_params *app, uint32_t link_id)
+{
+	uint32_t max_hwq_in = 0;
+	uint32_t i;
+
+	for (i = 0; i < APP_LINK_MAX_HWQ_IN; i++) {
+		struct app_pktq_hwq_in_params *p = &app->hwq_in_params[i];
+		uint32_t rxq_link_id, rxq_queue_id;
+
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		sscanf(p->name, "RXQ%u.%u", &rxq_link_id, &rxq_queue_id);
+		if (rxq_link_id != link_id)
+			continue;
+
+		max_hwq_in++;
+	}
+
+	return max_hwq_in;
+}
+
+static inline uint32_t
+app_n_hwq_out_get(struct app_params *app, uint32_t link_id)
+{
+	uint32_t max_hwq_out = 0;
+	uint32_t i;
+
+	for (i = 0; i < APP_LINK_MAX_HWQ_OUT; i++) {
+		struct app_pktq_hwq_out_params *p = &app->hwq_out_params[i];
+		uint32_t txq_link_id, txq_queue_id;
+
+		if (!APP_PARAM_VALID(p))
+			continue;
+
+		sscanf(p->name, "TXQ%u.%u", &txq_link_id, &txq_queue_id);
+		if (txq_link_id != link_id)
+			continue;
+
+		max_hwq_out++;
+	}
+
+	return max_hwq_out;
+}
+
+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%u.%u", &rxq_link_id, &rxq_queue_id);
+	sprintf(link_name, "LINK%u", 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%u.%u", &txq_link_id, &txq_queue_id);
+	sprintf(link_name, "LINK%u", 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%u", &link_id);
+	sprintf(link_name, "LINK%u", 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);
+
+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..d6242da
--- /dev/null
+++ b/examples/ip_pipeline/config_parse.c
@@ -0,0 +1,2071 @@ 
+/*-
+ *   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 <rte_errno.h>
+#include <rte_cfgfile.h>
+#include <rte_string_fns.h>
+
+#include "app.h"
+
+/**
+ * Default config values
+ **/
+
+#define DEFAULT_CONFIG_FILE                      "./config/ip_pipeline.cfg"
+
+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 length */
+			.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,
+	.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 \n"
+	"\n"
+	"Arguments:\n"
+	"\t-f CONFIG_FILE: Default config file is " DEFAULT_CONFIG_FILE "\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"
+	"\n";
+
+static void
+app_print_usage(char *prgname)
+{
+	rte_exit(0, app_usage, prgname);
+}
+
+#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)
+{
+	int val;
+	p = skip_white_spaces(p);
+	if (strstr(p, "yes") == p) {
+		val = 1;
+		p += 3;
+	} else if (strstr(p, "no") == p) {
+		val = 0;
+		p += 2;
+	} else
+		return -EINVAL;
+
+	p = skip_white_spaces(p);
+	if (*p != '\0')
+		return -EINVAL;
+
+	return val;
+}
+
+static const struct rte_cfgfile_entry2 **
+parser_get_section_entries(struct rte_cfgfile *cfg, const char *sect_name)
+{
+	const struct rte_cfgfile_entry2 **entries;
+	int ent_cnt, ret;
+
+	ent_cnt = rte_cfgfile_section_num_entries(cfg, sect_name);
+	APP_CHECK(ent_cnt > 0, "CFG: [%s] not entires (res=%d)", sect_name, -ent_cnt);
+
+	entries = malloc((ent_cnt + 1) * sizeof(struct rte_cfgfile_entry2 *));
+	APP_CHECK(entries != NULL, "CFG: [%s] Out of memory", sect_name);
+
+	ret = rte_cfgfile_section_entries_v21(cfg, sect_name, entries, ent_cnt);
+	APP_CHECK(ret == ent_cnt, "CFG: [%s] failed to get entries", sect_name);
+
+	entries[ent_cnt] = NULL;
+	return entries;
+}
+
+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 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) {
+			idx = APP_PARAM_ADD(app->msgq_params, name);
+		} else
+			return -EINVAL;
+
+		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) {
+			idx = APP_PARAM_ADD(app->msgq_params, name);
+		} else
+			return -EINVAL;
+
+		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 *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	char name[CFG_NAME_LEN];
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_pipeline_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->pipeline_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->pipeline_params, sect_name);
+
+	param = &app->pipeline_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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", sect_name);
+
+			param->n_args++;
+			ret = 0;
+		}
+
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	snprintf(name, sizeof(name), "MSGQ-REQ-%s", sect_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", sect_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 *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_mempool_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->mempool_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->mempool_params, sect_name);
+
+	param = &app->mempool_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_link(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_link_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->link_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->link_params, sect_name);
+
+	param = &app->link_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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, "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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_rxq(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_pktq_hwq_in_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->hwq_in_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->hwq_in_params, sect_name);
+
+	param = &app->hwq_in_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		ent = entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "mempool") == 0) {
+			ssize_t idx;
+
+			APP_CHECK((validate_name(ent->value, "MEMPOOL", 1) == 0),
+				"CFG: [%s] entry '%s': invalid mempool\n", sect_name, ent->name);
+
+			idx = APP_PARAM_ADD(app->mempool_params, ent->value);
+			PARSER_IMPLICIT_PARAM_ADD_CHECK(idx, sect_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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_txq(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_pktq_hwq_out_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->hwq_out_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->hwq_out_params, sect_name);
+
+	param = &app->hwq_out_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_swq(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_pktq_swq_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->swq_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->swq_params, sect_name);
+
+	param = &app->swq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+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", "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 %d 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 %d 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 %d 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 %d 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 %d", 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 %d", 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);
+			const struct rte_cfgfile_entry2 *entries[n_entries];
+
+			rte_cfgfile_section_entries_v21(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 %d", 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 mtu, i;
+
+	mtu = tm->sched_port_params.mtu;
+	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];
+	tm->sched_port_params.mtu = mtu;
+
+	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;
+}
+
+static void
+parse_tm(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_pktq_tm_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->tm_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->tm_params, sect_name);
+
+	param = &app->tm_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		ent = entries[i];
+
+		ret = -ESRCH;
+		if(strcmp(ent->name, "cfg") == 0) {
+			int status;
+
+			ret = 0;
+			status = snprintf(param->file_name,
+				sizeof(param->file_name), "%s", ent->value);
+			if (status >= (int) sizeof(param->file_name))
+				ret = -EINVAL;
+			else {
+				status = tm_cfgfile_load(param);
+				if (status)
+					ret = -EBADF;
+			}
+		} 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",
+				sect_name, ent->name);
+		APP_CHECK(ret != -EBADF, "CFG: [%s] entry '%s': TM cfg parse error '%s'\n",
+				sect_name, ent->name, ent->value);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_source(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_pktq_source_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->source_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->source_params, sect_name);
+
+	param = &app->source_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		ent = entries[i];
+
+		ret = -ESRCH;
+		if (strcmp(ent->name, "mempool") == 0) {
+			ssize_t idx;
+
+			APP_CHECK((validate_name(ent->value, "MEMPOOL", 1) == 0),
+				"CFG: [%s] entry '%s': invalid mempool\n", sect_name, ent->name);
+
+			idx = APP_PARAM_ADD(app->mempool_params, ent->value);
+			PARSER_IMPLICIT_PARAM_ADD_CHECK(idx, sect_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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_msgq_req_pipeline(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_msgq_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->msgq_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->msgq_params, sect_name);
+
+	param = &app->msgq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_msgq_rsp_pipeline(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_msgq_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->msgq_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->msgq_params, sect_name);
+
+	param = &app->msgq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+static void
+parse_msgq(struct app_params *app, const char *sect_name,
+		struct rte_cfgfile *cfg)
+{
+	int ret, i;
+	ssize_t param_idx;
+
+	const struct rte_cfgfile_entry2 **entries, *ent;
+	struct app_msgq_params *param;
+
+	entries = parser_get_section_entries(cfg, sect_name);
+
+	param_idx = APP_PARAM_ADD(app->msgq_params, sect_name);
+	PARSER_PARAM_ADD_CHECK(param_idx, app->msgq_params, sect_name);
+
+	param = &app->msgq_params[param_idx];
+	param->parsed = 1;
+
+	for (i = 0; entries[i] != NULL; i++) {
+		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",
+				sect_name, ent->name);
+		APP_CHECK(ret == 0, "CFG: [%s] entry '%s': Invalid value '%s'\n",
+				sect_name, ent->name, ent->value);
+	}
+
+	free(entries);
+}
+
+struct config_section {
+	const char prefix[CFG_NAME_LEN];
+	int numbers;
+	void (*load)(struct app_params *p, const char *sect_name,
+			struct rte_cfgfile *cfg);
+};
+
+static const struct config_section cfg_file_scheme[] = {
+		{ "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%u", 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];
+
+	char **sect_names;
+	struct rte_cfgfile *cfg;
+
+	const struct config_section *sch_s;
+	int i, j, sect_count;
+
+	create_implicit_mempools(app);
+
+	parse_port_mask(app, app->port_mask);
+
+	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);
+	sect_names = malloc(sect_count * sizeof(char *));
+	for (i = 0; i < sect_count; i++)
+		sect_names[i] = malloc(CFG_NAME_LEN);
+
+	rte_cfgfile_sections(cfg, sect_names, sect_count);
+
+	for (i = 0; i < sect_count; i++) {
+		int len, cfg_name_len;
+
+		cfg_name_len = strlen(sect_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 ex:
+			 * if shc_s if sch_s->name is "ABC"
+			 * cfg_sect_name "ABC0.0" match but
+			 * cfg_sect_name is "ABCDEF" does not match
+			 */
+			if (sect_names[i][len] != '\0' && !isdigit(sect_names[i][len]))
+				continue;
+
+			if (strncmp(sch_s->prefix, sect_names[i], len) == 0)
+				break;
+		}
+
+		APP_CHECK(j < (int)RTE_DIM(cfg_file_scheme), "Unknown section %s",
+				sect_names[i]);
+
+		APP_CHECK(validate_name(sect_names[i], sch_s->prefix, sch_s->numbers) == 0,
+				"Invalid section name '%s'", sect_names[i]);
+
+		sch_s->load(app, sect_names[i], cfg);
+	}
+
+	for (i = 0; i < sect_count; i++)
+		free(sect_names[i]);
+
+	free(sect_names);
+
+	rte_cfgfile_close(cfg);
+
+	snprintf(config_file_out, APP_FILE_NAME_SIZE, "%s.out", app->config_file);
+	app_config_save(app, config_file_out);
+
+	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);
+
+	return 0;
+}
+
+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->arp_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 = %u\n", "burst_read", p->burst_read);
+		fprintf(f, "%s = %u\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 = %u\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%uc%u%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 = %u\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 = fopen(file_name, "w");
+
+	APP_CHECK(file != NULL, "Failed to save config to file '%s'", file_name);
+
+	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);
+}
+
+int
+app_config_init(struct app_params *app)
+{
+	size_t i;
+
+	memset(app, 0, sizeof(struct app_params));
+
+	strcpy(app->config_file, DEFAULT_CONFIG_FILE);
+
+	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, p_present, f_present, s_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);
+
+	p_present = 0;
+	f_present = 0;
+	s_present = 0;
+
+	while ((opt = getopt_long(argc, argv, "f:s:p:", lgopts,
+			&option_index)) != EOF) {
+		switch (opt) {
+		case 'p':
+			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");
+
+			if (p_present)
+				rte_panic("Error: PORT_MASK is provided more than once\n");
+
+			p_present = 1;
+			break;
+
+		case 'f':
+			snprintf(app->config_file, sizeof(app->config_file), "%s", optarg);
+
+			if (!strlen(app->config_file))
+				rte_panic("Error: Config file name is null\n");
+
+			if (f_present)
+				rte_panic("Error: Config file is provided more than once\n");
+
+			f_present = 1;
+			break;
+
+		case 's':
+			snprintf(app->script_file, sizeof(app->script_file), "%s", optarg);
+
+			if (!strlen(app->script_file))
+				rte_panic("Error: Script file name is null\n");
+
+			if (s_present)
+				rte_panic("Error: Script file is provided more than once\n");
+
+			s_present = 1;
+			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/cpu_core_map.c b/examples/ip_pipeline/cpu_core_map.c
new file mode 100644
index 0000000..0d83879
--- /dev/null
+++ b/examples/ip_pipeline/cpu_core_map.c
@@ -0,0 +1,465 @@ 
+/*-
+ *   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 <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%u/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%u/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)
+{
+#if 1
+	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 %u:\n", socket_id);
+		
+		for (core_id = 0; core_id < map->n_cores_per_socket; core_id++) {
+			printf("[%2u] = [", 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 = lcore_config[lcore_id].core_id;
+				uint32_t core_id_noncontig = cpu_core_map_get_core_id_linux(lcore_id);
+
+				printf(" %2d (%2u) ", lcore_id, core_id_noncontig);
+			}
+			
+			printf("]\n");
+		}
+	}
+#else
+	uint32_t size, i;
+
+	if (map == NULL)
+		return;
+
+	size = map->n_max_sockets * map->n_max_cores_per_socket * map->n_max_ht_per_core;
+	printf("(n_sockets = %u, n_cores_per_socket = %u, n_ht_per_core = %u) [",
+		map->n_sockets, map->n_cores_per_socket, map->n_ht_per_core);
+	for (i = 0; i < size; i++)
+		printf("%d  ", map->map[i]);
+	printf("]\n");
+#endif
+}
+
+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..de85f82
--- /dev/null
+++ b/examples/ip_pipeline/pipeline.h
@@ -0,0 +1,79 @@ 
+/*-
+ *   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_ops.h"
+
+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;
+};
+
+struct pipeline_type {
+	const char *name;
+	
+	/* pipeline back-end */
+	struct pipeline_ops *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_ops.h b/examples/ip_pipeline/pipeline_ops.h
new file mode 100644
index 0000000..ae98dc7
--- /dev/null
+++ b/examples/ip_pipeline/pipeline_ops.h
@@ -0,0 +1,247 @@ 
+/*-
+ *   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_OPS_H__
+#define __INCLUDE_PIPELINE_OPS_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;
+};
+
+typedef void* (*pipeline_op_init)(struct pipeline_params *params, void *arg);
+
+typedef int (*pipeline_op_free)(void *pipeline);
+
+typedef int (*pipeline_op_run)(void *pipeline);
+
+typedef int (*pipeline_op_timer)(void *pipeline);
+
+typedef int (*pipeline_op_track)(void *pipeline, uint32_t port_in, uint32_t *port_out);
+
+struct pipeline_ops {
+	pipeline_op_init f_init;
+	pipeline_op_free f_free;
+	pipeline_op_run f_run;
+	pipeline_op_timer f_timer;
+	pipeline_op_track f_track;
+};
+
+#endif