DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH] ip_pipeline: add more functions to routing-pipeline
@ 2015-09-30 15:20 Jasvinder Singh
  2015-09-30 15:38 ` Dumitrescu, Cristian
  0 siblings, 1 reply; 11+ messages in thread
From: Jasvinder Singh @ 2015-09-30 15:20 UTC (permalink / raw)
  To: dev

This patch adds following features to the
routing-pipeline to enable it for various NFV
use-cases;

1.Fast-path ARP table enable/disable
2.Double-tagged VLAN (Q-in-Q) packet enacapsulation
for the next-hop
3.MPLS encapsulation for the next-hop
4.Add colour (Traffic-class for QoS) to the MPLS tag
5.Classification action to select the input queue
of the hierarchical scehdular (QoS)

The above proposed features can be enabled
(or disabled) through the parameters specified
in configuration file as below;

[PIPELINE0]
type = ROUTING
core = 1
pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
n_routes = 4096
n_arp_entries = 1024
ip_hdr_offset = 142
arp_key_offset = 64
l2 = qinq
qinq_sched = no

The LPM table entries might include additional
fields depending upon the packet encapsulation
(Q-in-Q, MPLS)for the next-hop. Therefore, the
CLI commands for adding or deleting such entries
to LPM table have been implemented. The key
functions such as QinQ and MPLS encapsulation,
classification action to select the input queue
of the hierarchical schedular(QoS) and adding
colour (Traffic-class for QoS) to the MPLS
tag have been implemented as action handlers.

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
---
 examples/ip_pipeline/pipeline/pipeline_routing.c   |  795 ++++++++++-
 examples/ip_pipeline/pipeline/pipeline_routing.h   |    8 +-
 .../ip_pipeline/pipeline/pipeline_routing_be.c     | 1393 ++++++++++++++++++--
 .../ip_pipeline/pipeline/pipeline_routing_be.h     |   62 +-
 4 files changed, 2074 insertions(+), 184 deletions(-)

diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline/pipeline_routing.c
index beec982..8ba1e25 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.c
@@ -43,7 +43,7 @@
 
 struct app_pipeline_routing_route {
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params params;
+	struct pipeline_routing_route_data data;
 	void *entry_ptr;
 
 	TAILQ_ENTRY(app_pipeline_routing_route) node;
@@ -196,12 +196,44 @@ print_route(const struct app_pipeline_routing_route *route)
 			key->ip & 0xFF,
 
 			key->depth,
-			route->params.port_id,
+			route->data.port_id);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_ARP)
+			printf(
+				", Next Hop IP = %" PRIu32 ".%" PRIu32
+				".%" PRIu32 ".%" PRIu32,
+				(route->data.ethernet.ip >> 24) & 0xFF,
+				(route->data.ethernet.ip >> 16) & 0xFF,
+				(route->data.ethernet.ip >> 8) & 0xFF,
+				route->data.ethernet.ip & 0xFF);
+		else
+			printf(
+				", Next Hop HWaddress = %02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32,
+				route->data.ethernet.macaddr.addr_bytes[0],
+				route->data.ethernet.macaddr.addr_bytes[1],
+				route->data.ethernet.macaddr.addr_bytes[2],
+				route->data.ethernet.macaddr.addr_bytes[3],
+				route->data.ethernet.macaddr.addr_bytes[4],
+				route->data.ethernet.macaddr.addr_bytes[5]);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)
+			printf(", QinQ SVLAN = %" PRIu32 " CVLAN = %" PRIu32,
+				route->data.l2.qinq.svlan,
+				route->data.l2.qinq.cvlan);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) {
+			uint32_t i;
+
+			printf(", MPLS labels");
+			for (i = 0; i < route->data.l2.mpls.n_labels; i++)
+				printf(" %" PRIu32,
+					route->data.l2.mpls.labels[i]);
+		}
 
-			(route->params.ip >> 24) & 0xFF,
-			(route->params.ip >> 16) & 0xFF,
-			(route->params.ip >> 8) & 0xFF,
-			route->params.ip & 0xFF);
+		printf(")\n");
 	}
 }
 
@@ -212,6 +244,8 @@ print_arp_entry(const struct app_pipeline_routing_arp_entry *entry)
 		".%" PRIu32 ".%" PRIu32 ") => "
 		"HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
 		":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
+
+
 		entry->key.key.ipv4.port_id,
 		(entry->key.key.ipv4.ip >> 24) & 0xFF,
 		(entry->key.key.ipv4.ip >> 16) & 0xFF,
@@ -253,7 +287,7 @@ int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params)
+	struct pipeline_routing_route_data *data)
 {
 	struct pipeline_routing *p;
 
@@ -267,7 +301,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	/* Check input arguments */
 	if ((app == NULL) ||
 		(key == NULL) ||
-		(route_params == NULL))
+		(data == NULL))
 		return -1;
 
 	p = app_pipeline_data_fe(app, pipeline_id);
@@ -287,8 +321,8 @@ app_pipeline_routing_add_route(struct app_params *app,
 		netmask = (~0) << (32 - depth);
 		key->key.ipv4.ip &= netmask;
 
-		/* route params */
-		if (route_params->port_id >= p->n_ports_out)
+		/* data */
+		if (data->port_id >= p->n_ports_out)
 			return -1;
 	}
 	break;
@@ -318,9 +352,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	req->type = PIPELINE_MSG_REQ_CUSTOM;
 	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD;
 	memcpy(&req->key, key, sizeof(*key));
-	req->flags = route_params->flags;
-	req->port_id = route_params->port_id;
-	req->ip = route_params->ip;
+	memcpy(&req->data, data, sizeof(*data));
 
 	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
 	if (rsp == NULL) {
@@ -341,7 +373,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	}
 
 	memcpy(&entry->key, key, sizeof(*key));
-	memcpy(&entry->params, route_params, sizeof(*route_params));
+	memcpy(&entry->data, data, sizeof(*data));
 	entry->entry_ptr = rsp->entry_ptr;
 
 	/* Commit entry */
@@ -820,31 +852,174 @@ app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
 	return 0;
 }
 
+static int
+parse_labels(char *string, uint32_t *labels, uint32_t *n_labels)
+{
+	uint32_t n_max_labels = *n_labels, count = 0;
+
+	/* Check for void list of labels */
+	if (strcmp(string, "<void>") == 0) {
+		*n_labels = 0;
+		return 0;
+	}
+
+	/* At least one label should be present */
+	for ( ; (*string != '\0'); ) {
+		char *next;
+		int value;
+
+		if (count >= n_max_labels)
+			return -1;
+
+		if (count > 0) {
+			if (string[0] != ':')
+				return -1;
+
+			string++;
+		}
+
+		value = strtol(string, &next, 10);
+		if (next == string)
+			return -1;
+		string = next;
+
+		labels[count++] = (uint32_t) value;
+	}
+
+	*n_labels = count;
+	return 0;
+}
+
+/*
+ * route add (mpls = no, qinq = no, arp = no)
+ */
+
+struct cmd_route_add1_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+};
+
+static void
+cmd_route_add1_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add1_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = 0;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add1_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add1_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add1_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add1_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add1_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add1_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add1_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add1_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add1_result, macaddr);
+
+static cmdline_parse_inst_t cmd_route_add1 = {
+	.f = cmd_route_add1_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add1_p_string,
+		(void *)&cmd_route_add1_p,
+		(void *)&cmd_route_add1_route_string,
+		(void *)&cmd_route_add1_add_string,
+		(void *)&cmd_route_add1_ip,
+		(void *)&cmd_route_add1_depth,
+		(void *)&cmd_route_add1_port_string,
+		(void *)&cmd_route_add1_port,
+		(void *)&cmd_route_add1_ether_string,
+		(void *)&cmd_route_add1_macaddr,
+		NULL,
+	},
+};
+
 /*
- * route add
+ * route add (mpls = no, qinq = no, arp = yes)
  */
 
-struct cmd_route_add_result {
+struct cmd_route_add2_result {
 	cmdline_fixed_string_t p_string;
 	uint32_t p;
 	cmdline_fixed_string_t route_string;
 	cmdline_fixed_string_t add_string;
 	cmdline_ipaddr_t ip;
 	uint32_t depth;
+	cmdline_fixed_string_t port_string;
 	uint32_t port;
+	cmdline_fixed_string_t ether_string;
 	cmdline_ipaddr_t nh_ip;
 };
 
 static void
-cmd_route_add_parsed(
+cmd_route_add2_parsed(
 	void *parsed_result,
 	__rte_unused struct cmdline *cl,
 	void *data)
 {
-	struct cmd_route_add_result *params = parsed_result;
+	struct cmd_route_add2_result *params = parsed_result;
 	struct app_params *app = data;
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params rt_params;
+	struct pipeline_routing_route_data route_data;
 	int status;
 
 	/* Create route */
@@ -852,14 +1027,15 @@ cmd_route_add_parsed(
 	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
 	key.key.ipv4.depth = params->depth;
 
-	rt_params.flags = 0; /* remote route */
-	rt_params.port_id = params->port;
-	rt_params.ip = rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.flags = PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
 
 	status = app_pipeline_routing_add_route(app,
 		params->p,
 		&key,
-		&rt_params);
+		&route_data);
 
 	if (status != 0) {
 		printf("Command failed\n");
@@ -867,46 +1043,558 @@ cmd_route_add_parsed(
 	}
 }
 
-static cmdline_parse_token_string_t cmd_route_add_p_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, p_string,
+static cmdline_parse_token_string_t cmd_route_add2_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, p_string,
 	"p");
 
-static cmdline_parse_token_num_t cmd_route_add_p =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, p, UINT32);
+static cmdline_parse_token_num_t cmd_route_add2_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, p, UINT32);
 
-static cmdline_parse_token_string_t cmd_route_add_route_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, route_string,
+static cmdline_parse_token_string_t cmd_route_add2_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, route_string,
 	"route");
 
-static cmdline_parse_token_string_t cmd_route_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, add_string,
+static cmdline_parse_token_string_t cmd_route_add2_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, add_string,
 	"add");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, ip);
+static cmdline_parse_token_ipaddr_t cmd_route_add2_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add2_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add2_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add2_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, nh_ip);
+
+static cmdline_parse_inst_t cmd_route_add2 = {
+	.f = cmd_route_add2_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add2_p_string,
+		(void *)&cmd_route_add2_p,
+		(void *)&cmd_route_add2_route_string,
+		(void *)&cmd_route_add2_add_string,
+		(void *)&cmd_route_add2_ip,
+		(void *)&cmd_route_add2_depth,
+		(void *)&cmd_route_add2_port_string,
+		(void *)&cmd_route_add2_port,
+		(void *)&cmd_route_add2_ether_string,
+		(void *)&cmd_route_add2_nh_ip,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = no)
+ */
+
+struct cmd_route_add3_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add3_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add3_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add3_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add3_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add3_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add3_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add3_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add3_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add3_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add3_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add3_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add3_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add3_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add3_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add3 = {
+	.f = cmd_route_add3_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add3_p_string,
+		(void *)&cmd_route_add3_p,
+		(void *)&cmd_route_add3_route_string,
+		(void *)&cmd_route_add3_add_string,
+		(void *)&cmd_route_add3_ip,
+		(void *)&cmd_route_add3_depth,
+		(void *)&cmd_route_add3_port_string,
+		(void *)&cmd_route_add3_port,
+		(void *)&cmd_route_add3_ether_string,
+		(void *)&cmd_route_add3_macaddr,
+		(void *)&cmd_route_add3_qinq_string,
+		(void *)&cmd_route_add3_svlan,
+		(void *)&cmd_route_add3_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = yes)
+ */
+
+struct cmd_route_add4_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add4_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add4_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add4_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add4_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add4_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add4_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add4_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, nh_ip);
+
+static cmdline_parse_token_string_t cmd_route_add4_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add4_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add4_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add4 = {
+	.f = cmd_route_add4_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add4_p_string,
+		(void *)&cmd_route_add4_p,
+		(void *)&cmd_route_add4_route_string,
+		(void *)&cmd_route_add4_add_string,
+		(void *)&cmd_route_add4_ip,
+		(void *)&cmd_route_add4_depth,
+		(void *)&cmd_route_add4_port_string,
+		(void *)&cmd_route_add4_port,
+		(void *)&cmd_route_add4_ether_string,
+		(void *)&cmd_route_add4_nh_ip,
+		(void *)&cmd_route_add4_qinq_string,
+		(void *)&cmd_route_add4_svlan,
+		(void *)&cmd_route_add4_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = no)
+ */
+
+struct cmd_route_add5_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add5_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add5_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add5_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add5_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add5_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add5_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add5_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add5_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add5_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add5_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add5_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_string,
+	"mpls");
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_labels,
+	NULL);
+
+static cmdline_parse_inst_t cmd_route_add5 = {
+	.f = cmd_route_add5_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add5_p_string,
+		(void *)&cmd_route_add5_p,
+		(void *)&cmd_route_add5_route_string,
+		(void *)&cmd_route_add5_add_string,
+		(void *)&cmd_route_add5_ip,
+		(void *)&cmd_route_add5_depth,
+		(void *)&cmd_route_add5_port_string,
+		(void *)&cmd_route_add5_port,
+		(void *)&cmd_route_add5_ether_string,
+		(void *)&cmd_route_add5_macaddr,
+		(void *)&cmd_route_add5_mpls_string,
+		(void *)&cmd_route_add5_mpls_labels,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = yes)
+ */
+
+struct cmd_route_add6_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add6_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add6_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add6_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add6_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add6_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add6_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add6_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add6_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, ether_string,
+	"ether");
 
-static cmdline_parse_token_num_t cmd_route_add_depth =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, depth, UINT32);
+static cmdline_parse_token_ipaddr_t cmd_route_add6_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, nh_ip);
 
-static cmdline_parse_token_num_t cmd_route_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, port, UINT32);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_string,
+	"mpls");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_nh_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, nh_ip);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_labels,
+	NULL);
 
-static cmdline_parse_inst_t cmd_route_add = {
-	.f = cmd_route_add_parsed,
+static cmdline_parse_inst_t cmd_route_add6 = {
+	.f = cmd_route_add6_parsed,
 	.data = NULL,
-	.help_str = "Route add",
+	.help_str = "Route add (mpls = yes, arp = yes)",
 	.tokens = {
-		(void *)&cmd_route_add_p_string,
-		(void *)&cmd_route_add_p,
-		(void *)&cmd_route_add_route_string,
-		(void *)&cmd_route_add_add_string,
-		(void *)&cmd_route_add_ip,
-		(void *)&cmd_route_add_depth,
-		(void *)&cmd_route_add_port,
-		(void *)&cmd_route_add_nh_ip,
+		(void *)&cmd_route_add6_p_string,
+		(void *)&cmd_route_add6_p,
+		(void *)&cmd_route_add6_route_string,
+		(void *)&cmd_route_add6_add_string,
+		(void *)&cmd_route_add6_ip,
+		(void *)&cmd_route_add6_depth,
+		(void *)&cmd_route_add6_port_string,
+		(void *)&cmd_route_add6_port,
+		(void *)&cmd_route_add6_ether_string,
+		(void *)&cmd_route_add6_nh_ip,
+		(void *)&cmd_route_add6_mpls_string,
+		(void *)&cmd_route_add6_mpls_labels,
 		NULL,
 	},
 };
@@ -1519,7 +2207,12 @@ static cmdline_parse_inst_t cmd_arp_ls = {
 };
 
 static cmdline_parse_ctx_t pipeline_cmds[] = {
-	(cmdline_parse_inst_t *)&cmd_route_add,
+	(cmdline_parse_inst_t *)&cmd_route_add1,
+	(cmdline_parse_inst_t *)&cmd_route_add2,
+	(cmdline_parse_inst_t *)&cmd_route_add3,
+	(cmdline_parse_inst_t *)&cmd_route_add4,
+	(cmdline_parse_inst_t *)&cmd_route_add5,
+	(cmdline_parse_inst_t *)&cmd_route_add6,
 	(cmdline_parse_inst_t *)&cmd_route_del,
 	(cmdline_parse_inst_t *)&cmd_route_add_default,
 	(cmdline_parse_inst_t *)&cmd_route_del_default,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.h b/examples/ip_pipeline/pipeline/pipeline_routing.h
index e1016f9..fa41642 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.h
@@ -41,17 +41,11 @@
  * Route
  */
 
-struct app_pipeline_routing_route_params {
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* IP address for the next hop (only for remote routes) */
-};
-
 int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params);
+	struct pipeline_routing_route_data *data);
 
 int
 app_pipeline_routing_delete_route(struct app_params *app,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.c b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
index 1e817dd..14e6ce8 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
@@ -49,16 +49,29 @@
 #include "pipeline_actions_common.h"
 #include "hash_func.h"
 
+#define MPLS_LABEL(label, exp, s, ttl)					\
+	(((((uint64_t) (label)) & 0xFFFFFLLU) << 12) |		\
+	((((uint64_t) (exp)) & 0x7LLU) << 9) |				\
+	((((uint64_t) (s)) & 0x1LLU) << 8) |				\
+	(((uint64_t) (ttl)) & 0xFFLU))
+
+#define RTE_SCHED_PORT_HIERARCHY(subport, pipe,		\
+	traffic_class, queue, color)					\
+	((((uint32_t) (queue)) & 0x3) |					\
+	((((uint32_t) (traffic_class)) & 0x3) << 2) |		\
+	((((uint32_t) (pipe)) & 0xFFFFF) << 4) |			\
+	((((uint32_t) (subport)) & 0x3F) << 24) |			\
+	((((uint32_t) (color)) & 0x3) << 30))
+
 struct pipeline_routing {
 	struct pipeline p;
+	struct pipeline_routing_params params;
 	pipeline_msg_req_handler custom_handlers[PIPELINE_ROUTING_MSG_REQS];
-
-	uint32_t n_routes;
-	uint32_t n_arp_entries;
-	uint32_t ip_da_offset;
-	uint32_t arp_key_offset;
 } __rte_cache_aligned;
 
+/*
+ * Message handlers
+ */
 static void *
 pipeline_routing_msg_req_custom_handler(struct pipeline *p, void *msg);
 
@@ -138,35 +151,152 @@ struct routing_table_entry {
 	uint32_t flags;
 	uint32_t port_id; /* Output port ID */
 	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+
+	/* ether_l2 */
+	uint16_t data_offset;
+	uint16_t ether_l2_length;
+	uint64_t slab[4];
+	uint16_t slab_offset[4];
 };
 
-static inline void
+static inline __attribute__((always_inline)) void
 pkt_work_routing(
 	struct rte_mbuf *pkt,
 	struct rte_pipeline_table_entry *table_entry,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry =
 		(struct routing_table_entry *) table_entry;
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkt, p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->arp_key_offset);
-	uint32_t ip = RTE_MBUF_METADATA_UINT32(pkt, p_rt->ip_da_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr, *slab1_ptr, *slab2_ptr, *slab3_ptr;
+	uint32_t ip_da, nh_ip, port_id, sched;
+	uint16_t total_length, data_offset, ether_l2_length;
+
+	/* Read */
+	total_length = rte_bswap16(ip->total_length);
+	ip_da = ip->dst_addr;
+	data_offset = entry->data_offset;
+	ether_l2_length = entry->ether_l2_length;
+	slab0_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[0]);
+	slab1_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[1]);
+	slab2_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[2]);
+	slab3_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[3]);
+
+	if (arp) {
+		port_id = entry->port_id;
+		nh_ip = entry->ip;
+		if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip = ip_da;
+	}
+
+	/* Compute */
+	total_length += ether_l2_length;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp = ip->type_of_service >> 2;
+		uint32_t svlan, cvlan, tc, tc_q;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq = rte_bswap64(entry->slab[0]);
+
+			svlan = (slab_qinq >> 48) & 0xFFF;
+			cvlan = (slab_qinq >> 16) & 0xFFF;
+			tc = (dscp >> 2) & 0x3;
+			tc_q = dscp & 0x3;
+		} else {
+			uint32_t ip_src = rte_bswap32(ip->src_addr);
+
+			svlan = 0;
+			cvlan = (ip_src >> 16) & 0xFFF;
+			tc = (ip_src >> 2) & 0x3;
+			tc_q = ip_src & 0x3;
+		}
+
+		sched = RTE_SCHED_PORT_HIERARCHY(svlan,
+			cvlan,
+			tc,
+			tc_q,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkt->data_off = data_offset;
+	pkt->data_len = total_length;
+	pkt->pkt_len = total_length;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr = entry->slab[0];
+
+		if (arp == 0)
+			*slab1_ptr = entry->slab[1];
+	}
+
+	if (qinq) {
+		*slab0_ptr = entry->slab[0];
+		*slab1_ptr = entry->slab[1];
+
+		if (arp == 0)
+			*slab2_ptr = entry->slab[2];
+
+		if (qinq_sched)
+			pkt->hash.sched = sched;
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp = rte_bswap64(
+				(MPLS_LABEL(0, pkt_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt_color, 0, 0));
+
+			*slab0_ptr = entry->slab[0] | mpls_exp;
+			*slab1_ptr = entry->slab[1] | mpls_exp;
+			*slab2_ptr = entry->slab[2];
+		} else {
+			*slab0_ptr = entry->slab[0];
+			*slab1_ptr = entry->slab[1];
+			*slab2_ptr = entry->slab[2];
+		}
+
+		if (arp == 0)
+			*slab3_ptr = entry->slab[3];
+	}
 
-	arp_key->port_id = entry->port_id;
-	arp_key->ip = entry->ip;
-	if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key->ip = ip;
+	if (arp) {
+		arp_key->port_id = port_id;
+		arp_key->ip = nh_ip;
+	}
 }
 
-static inline void
+static inline __attribute__((always_inline)) void
 pkt4_work_routing(
 	struct rte_mbuf **pkts,
 	struct rte_pipeline_table_entry **table_entries,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry0 =
 		(struct routing_table_entry *) table_entries[0];
 	struct routing_table_entry *entry1 =
@@ -175,51 +305,692 @@ pkt4_work_routing(
 		(struct routing_table_entry *) table_entries[2];
 	struct routing_table_entry *entry3 =
 		(struct routing_table_entry *) table_entries[3];
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip0 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip1 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip2 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip3 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt0_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->params.color_offset);
+	enum rte_meter_color pkt1_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->params.color_offset);
+	enum rte_meter_color pkt2_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->params.color_offset);
+	enum rte_meter_color pkt3_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key0 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[0], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key1 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[1], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key2 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[2], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key3 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[3], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr0, *slab1_ptr0, *slab2_ptr0, *slab3_ptr0;
+	uint64_t *slab0_ptr1, *slab1_ptr1, *slab2_ptr1, *slab3_ptr1;
+	uint64_t *slab0_ptr2, *slab1_ptr2, *slab2_ptr2, *slab3_ptr2;
+	uint64_t *slab0_ptr3, *slab1_ptr3, *slab2_ptr3, *slab3_ptr3;
+
+	uint32_t ip_da0, nh_ip0, port_id0, sched0;
+	uint32_t ip_da1, nh_ip1, port_id1, sched1;
+	uint32_t ip_da2, nh_ip2, port_id2, sched2;
+	uint32_t ip_da3, nh_ip3, port_id3, sched3;
+
+	uint16_t total_length0, data_offset0, ether_l2_length0;
+	uint16_t total_length1, data_offset1, ether_l2_length1;
+	uint16_t total_length2, data_offset2, ether_l2_length2;
+	uint16_t total_length3, data_offset3, ether_l2_length3;
+
+	/* Read */
+	total_length0 = rte_bswap16(ip0->total_length);
+	total_length1 = rte_bswap16(ip1->total_length);
+	total_length2 = rte_bswap16(ip2->total_length);
+	total_length3 = rte_bswap16(ip3->total_length);
+
+	ip_da0 = ip0->dst_addr;
+	ip_da1 = ip1->dst_addr;
+	ip_da2 = ip2->dst_addr;
+	ip_da3 = ip3->dst_addr;
+
+	data_offset0 = entry0->data_offset;
+	data_offset1 = entry1->data_offset;
+	data_offset2 = entry2->data_offset;
+	data_offset3 = entry3->data_offset;
+
+	ether_l2_length0 = entry0->ether_l2_length;
+	ether_l2_length1 = entry1->ether_l2_length;
+	ether_l2_length2 = entry2->ether_l2_length;
+	ether_l2_length3 = entry3->ether_l2_length;
+
+	slab0_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[0]);
+	slab1_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[1]);
+	slab2_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[2]);
+	slab3_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[3]);
+
+	slab0_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[0]);
+	slab1_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[1]);
+	slab2_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[2]);
+	slab3_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[3]);
+
+	slab0_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[0]);
+	slab1_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[1]);
+	slab2_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[2]);
+	slab3_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[3]);
+
+	slab0_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[0]);
+	slab1_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[1]);
+	slab2_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[2]);
+	slab3_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[3]);
+
+	if (arp) {
+		port_id0 = entry0->port_id;
+		nh_ip0 = entry0->ip;
+		if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip0 = ip_da0;
+
+		port_id1 = entry1->port_id;
+		nh_ip1 = entry1->ip;
+		if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip1 = ip_da1;
+
+		port_id2 = entry2->port_id;
+		nh_ip2 = entry2->ip;
+		if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip2 = ip_da2;
+
+		port_id3 = entry3->port_id;
+		nh_ip3 = entry3->ip;
+		if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip3 = ip_da3;
+	}
+
+	/* Compute */
+	total_length0 += ether_l2_length0;
+	total_length1 += ether_l2_length1;
+	total_length2 += ether_l2_length2;
+	total_length3 += ether_l2_length3;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp0 = ip0->type_of_service >> 2;
+		uint32_t dscp1 = ip1->type_of_service >> 2;
+		uint32_t dscp2 = ip2->type_of_service >> 2;
+		uint32_t dscp3 = ip3->type_of_service >> 2;
+		uint32_t svlan0, cvlan0, tc0, tc_q0;
+		uint32_t svlan1, cvlan1, tc1, tc_q1;
+		uint32_t svlan2, cvlan2, tc2, tc_q2;
+		uint32_t svlan3, cvlan3, tc3, tc_q3;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq0 = rte_bswap64(entry0->slab[0]);
+			uint64_t slab_qinq1 = rte_bswap64(entry1->slab[0]);
+			uint64_t slab_qinq2 = rte_bswap64(entry2->slab[0]);
+			uint64_t slab_qinq3 = rte_bswap64(entry3->slab[0]);
+
+			svlan0 = (slab_qinq0 >> 48) & 0xFFF;
+			svlan1 = (slab_qinq1 >> 48) & 0xFFF;
+			svlan2 = (slab_qinq2 >> 48) & 0xFFF;
+			svlan3 = (slab_qinq3 >> 48) & 0xFFF;
+
+			cvlan0 = (slab_qinq0 >> 16) & 0xFFF;
+			cvlan1 = (slab_qinq1 >> 16) & 0xFFF;
+			cvlan2 = (slab_qinq2 >> 16) & 0xFFF;
+			cvlan3 = (slab_qinq3 >> 16) & 0xFFF;
+
+			tc0 = (dscp0 >> 2) & 0x3;
+			tc1 = (dscp1 >> 2) & 0x3;
+			tc2 = (dscp2 >> 2) & 0x3;
+			tc3 = (dscp3 >> 2) & 0x3;
+
+			tc_q0 = dscp0 & 0x3;
+			tc_q1 = dscp1 & 0x3;
+			tc_q2 = dscp2 & 0x3;
+			tc_q3 = dscp3 & 0x3;
+		} else {
+			uint32_t ip_src0 = rte_bswap32(ip0->src_addr);
+			uint32_t ip_src1 = rte_bswap32(ip1->src_addr);
+			uint32_t ip_src2 = rte_bswap32(ip2->src_addr);
+			uint32_t ip_src3 = rte_bswap32(ip3->src_addr);
+
+			svlan0 = 0;
+			svlan1 = 0;
+			svlan2 = 0;
+			svlan3 = 0;
+
+			cvlan0 = (ip_src0 >> 16) & 0xFFF;
+			cvlan1 = (ip_src1 >> 16) & 0xFFF;
+			cvlan2 = (ip_src2 >> 16) & 0xFFF;
+			cvlan3 = (ip_src3 >> 16) & 0xFFF;
+
+			tc0 = (ip_src0 >> 2) & 0x3;
+			tc1 = (ip_src1 >> 2) & 0x3;
+			tc2 = (ip_src2 >> 2) & 0x3;
+			tc3 = (ip_src3 >> 2) & 0x3;
+
+			tc_q0 = ip_src0 & 0x3;
+			tc_q1 = ip_src1 & 0x3;
+			tc_q2 = ip_src2 & 0x3;
+			tc_q3 = ip_src3 & 0x3;
+		}
+
+		sched0 = RTE_SCHED_PORT_HIERARCHY(svlan0,
+			cvlan0,
+			tc0,
+			tc_q0,
+			e_RTE_METER_GREEN);
+		sched1 = RTE_SCHED_PORT_HIERARCHY(svlan1,
+			cvlan1,
+			tc1,
+			tc_q1,
+			e_RTE_METER_GREEN);
+		sched2 = RTE_SCHED_PORT_HIERARCHY(svlan2,
+			cvlan2,
+			tc2,
+			tc_q2,
+			e_RTE_METER_GREEN);
+		sched3 = RTE_SCHED_PORT_HIERARCHY(svlan3,
+			cvlan3,
+			tc3,
+			tc_q3,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkts[0]->data_off = data_offset0;
+	pkts[1]->data_off = data_offset1;
+	pkts[2]->data_off = data_offset2;
+	pkts[3]->data_off = data_offset3;
+
+	pkts[0]->data_len = total_length0;
+	pkts[1]->data_len = total_length1;
+	pkts[2]->data_len = total_length2;
+	pkts[3]->data_len = total_length3;
+
+	pkts[0]->pkt_len = total_length0;
+	pkts[1]->pkt_len = total_length1;
+	pkts[2]->pkt_len = total_length2;
+	pkts[3]->pkt_len = total_length3;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		if (arp == 0) {
+			*slab1_ptr0 = entry0->slab[1];
+			*slab1_ptr1 = entry1->slab[1];
+			*slab1_ptr2 = entry2->slab[1];
+			*slab1_ptr3 = entry3->slab[1];
+		}
+	}
+
+	if (qinq) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		*slab1_ptr0 = entry0->slab[1];
+		*slab1_ptr1 = entry1->slab[1];
+		*slab1_ptr2 = entry2->slab[1];
+		*slab1_ptr3 = entry3->slab[1];
+
+		if (arp == 0) {
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		}
+
+		if (qinq_sched) {
+			pkts[0]->hash.sched = sched0;
+			pkts[1]->hash.sched = sched1;
+			pkts[2]->hash.sched = sched2;
+			pkts[3]->hash.sched = sched3;
+		}
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp0 = rte_bswap64(
+				(MPLS_LABEL(0, pkt0_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt0_color, 0, 0));
+			uint64_t mpls_exp1 = rte_bswap64(
+				(MPLS_LABEL(0, pkt1_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt1_color, 0, 0));
+			uint64_t mpls_exp2 = rte_bswap64(
+				(MPLS_LABEL(0, pkt2_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt2_color, 0, 0));
+			uint64_t mpls_exp3 = rte_bswap64(
+				(MPLS_LABEL(0, pkt3_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt3_color, 0, 0));
+
+			*slab0_ptr0 = entry0->slab[0] | mpls_exp0;
+			*slab0_ptr1 = entry1->slab[0] | mpls_exp1;
+			*slab0_ptr2 = entry2->slab[0] | mpls_exp2;
+			*slab0_ptr3 = entry3->slab[0] | mpls_exp3;
+
+			*slab1_ptr0 = entry0->slab[1] | mpls_exp0;
+			*slab1_ptr1 = entry1->slab[1] | mpls_exp1;
+			*slab1_ptr2 = entry2->slab[1] | mpls_exp2;
+			*slab1_ptr3 = entry3->slab[1] | mpls_exp3;
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		} else {
+			*slab0_ptr0 = entry0->slab[0];
+			*slab0_ptr1 = entry1->slab[0];
+			*slab0_ptr2 = entry2->slab[0];
+			*slab0_ptr3 = entry3->slab[0];
+
+			*slab1_ptr0 = entry0->slab[1];
+			*slab1_ptr1 = entry1->slab[1];
+			*slab1_ptr2 = entry2->slab[1];
+			*slab1_ptr3 = entry3->slab[1];
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		}
+
+		if (arp == 0) {
+			*slab3_ptr0 = entry0->slab[3];
+			*slab3_ptr1 = entry1->slab[3];
+			*slab3_ptr2 = entry2->slab[3];
+			*slab3_ptr3 = entry3->slab[3];
+		}
+	}
+
+	if (arp) {
+		arp_key0->port_id = port_id0;
+		arp_key1->port_id = port_id1;
+		arp_key2->port_id = port_id2;
+		arp_key3->port_id = port_id3;
+
+		arp_key0->ip = nh_ip0;
+		arp_key1->ip = nh_ip1;
+		arp_key2->ip = nh_ip2;
+		arp_key3->ip = nh_ip3;
+	}
+}
+
+static inline void
+pkt_work_routing_ether_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 0, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 0, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched0_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 1, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched0_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 1, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched1_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 1, 1, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched1_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 1, 1, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched2_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 1, 2, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched2_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 1, 2, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color0_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 0, 0, 1, 0);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color0_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 0, 0, 1, 0);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color1_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 0, 0, 1, 1);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color1_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 0, 0, 1, 1);
+}
+
+static inline void
+pkt4_work_routing_ether_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 0, 0, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 0, 0, 0, 0);
+}
 
-	uint32_t ip0 = RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->ip_da_offset);
-	uint32_t ip1 = RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->ip_da_offset);
-	uint32_t ip2 = RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->ip_da_offset);
-	uint32_t ip3 = RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->ip_da_offset);
+static inline void
+pkt4_work_routing_ether_qinq_sched0_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 1, 0, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched0_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 1, 0, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched1_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 1, 1, 0, 0);
+}
 
-	arp_key0->port_id = entry0->port_id;
-	arp_key1->port_id = entry1->port_id;
-	arp_key2->port_id = entry2->port_id;
-	arp_key3->port_id = entry3->port_id;
+static inline void
+pkt4_work_routing_ether_qinq_sched1_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 1, 1, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched2_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 1, 2, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched2_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 1, 2, 0, 0);
+}
 
-	arp_key0->ip = entry0->ip;
-	if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key0->ip = ip0;
+static inline void
+pkt4_work_routing_ether_mpls_color0_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 0, 0, 1, 0);
+}
 
-	arp_key1->ip = entry1->ip;
-	if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key1->ip = ip1;
+static inline void
+pkt4_work_routing_ether_mpls_color0_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 0, 0, 1, 0);
+}
 
-	arp_key2->ip = entry2->ip;
-	if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key2->ip = ip2;
+static inline void
+pkt4_work_routing_ether_mpls_color1_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 0, 0, 1, 1);
+}
 
-	arp_key3->ip = entry3->ip;
-	if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key3->ip = ip3;
+static inline void
+pkt4_work_routing_ether_mpls_color1_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 0, 0, 1, 1);
 }
 
-PIPELINE_TABLE_AH_HIT(routing_table_ah_hit,
-	pkt_work_routing,
-	pkt4_work_routing);
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_arp0,
+	pkt_work_routing_ether_arp0,
+	pkt4_work_routing_ether_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_arp1,
+	pkt_work_routing_ether_arp1,
+	pkt4_work_routing_ether_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched0_arp0,
+	pkt_work_routing_ether_qinq_sched0_arp0,
+	pkt4_work_routing_ether_qinq_sched0_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched0_arp1,
+	pkt_work_routing_ether_qinq_sched0_arp1,
+	pkt4_work_routing_ether_qinq_sched0_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched1_arp0,
+	pkt_work_routing_ether_qinq_sched1_arp0,
+	pkt4_work_routing_ether_qinq_sched1_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched1_arp1,
+	pkt_work_routing_ether_qinq_sched1_arp1,
+	pkt4_work_routing_ether_qinq_sched1_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched2_arp0,
+	pkt_work_routing_ether_qinq_sched2_arp0,
+	pkt4_work_routing_ether_qinq_sched2_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched2_arp1,
+	pkt_work_routing_ether_qinq_sched2_arp1,
+	pkt4_work_routing_ether_qinq_sched2_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color0_arp0,
+	pkt_work_routing_ether_mpls_color0_arp0,
+	pkt4_work_routing_ether_mpls_color0_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color0_arp1,
+	pkt_work_routing_ether_mpls_color0_arp1,
+	pkt4_work_routing_ether_mpls_color0_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color1_arp0,
+	pkt_work_routing_ether_mpls_color1_arp0,
+	pkt4_work_routing_ether_mpls_color1_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color1_arp1,
+	pkt_work_routing_ether_mpls_color1_arp1,
+	pkt4_work_routing_ether_mpls_color1_arp1);
+
+static rte_pipeline_table_action_handler_hit
+get_routing_table_ah_hit(struct pipeline_routing *p)
+{
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_arp0;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_arp1;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_qinq_sched0_arp0;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_qinq_sched0_arp1;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 1) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_qinq_sched1_arp0;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 1) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_qinq_sched1_arp1;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 2) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_qinq_sched2_arp0;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 2) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_qinq_sched2_arp1;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_mpls_color0_arp0;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_mpls_color0_arp1;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 1) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_mpls_color1_arp0;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 1) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_mpls_color1_arp1;
+
+	return NULL;
+}
 
 /*
  * ARP table
@@ -229,6 +1000,9 @@ struct arp_table_entry {
 	uint64_t macaddr;
 };
 
+/**
+ * ARP table AH
+ */
 static inline void
 pkt_work_arp(
 	struct rte_mbuf *pkt,
@@ -237,20 +1011,15 @@ pkt_work_arp(
 {
 	struct arp_table_entry *entry = (struct arp_table_entry *) table_entry;
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw = rte_pktmbuf_mtod(pkt, uint8_t *);
+	/* Read */
+	uint64_t macaddr_dst = entry->macaddr;
+	uint64_t *slab_ptr = (uint64_t *) ((char *) pkt->buf_addr +
+		(pkt->data_off - 2));
 
-	/* Read: table entry */
-	uint64_t mac_addr_dst = entry->macaddr;
-	uint64_t mac_addr_src = 0;
+	/* Compute */
 
-	/* Compute: Ethernet header */
-	uint64_t slab0 = mac_addr_dst | (mac_addr_src << 48);
-	uint32_t slab1 = mac_addr_src >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw) = slab0;
-	*((uint32_t *) (raw + 8)) = slab1;
+	/* Write */
+	*slab_ptr = macaddr_dst;
 }
 
 static inline void
@@ -268,59 +1037,56 @@ pkt4_work_arp(
 	struct arp_table_entry *entry3 =
 		(struct arp_table_entry *) table_entries[3];
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw0 = rte_pktmbuf_mtod(pkts[0], uint8_t *);
-	uint8_t *raw1 = rte_pktmbuf_mtod(pkts[1], uint8_t *);
-	uint8_t *raw2 = rte_pktmbuf_mtod(pkts[2], uint8_t *);
-	uint8_t *raw3 = rte_pktmbuf_mtod(pkts[3], uint8_t *);
-
-	/* Read: table entry */
-	uint64_t mac_addr_dst0 = entry0->macaddr;
-	uint64_t mac_addr_dst1 = entry1->macaddr;
-	uint64_t mac_addr_dst2 = entry2->macaddr;
-	uint64_t mac_addr_dst3 = entry3->macaddr;
-
-	uint64_t mac_addr_src0 = 0;
-	uint64_t mac_addr_src1 = 0;
-	uint64_t mac_addr_src2 = 0;
-	uint64_t mac_addr_src3 = 0;
-
-	/* Compute: Ethernet header */
-	uint64_t pkt0_slab0 = mac_addr_dst0 | (mac_addr_src0 << 48);
-	uint64_t pkt1_slab0 = mac_addr_dst1 | (mac_addr_src1 << 48);
-	uint64_t pkt2_slab0 = mac_addr_dst2 | (mac_addr_src2 << 48);
-	uint64_t pkt3_slab0 = mac_addr_dst3 | (mac_addr_src3 << 48);
-
-	uint32_t pkt0_slab1 = mac_addr_src0 >> 16;
-	uint32_t pkt1_slab1 = mac_addr_src1 >> 16;
-	uint32_t pkt2_slab1 = mac_addr_src2 >> 16;
-	uint32_t pkt3_slab1 = mac_addr_src3 >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw0) = pkt0_slab0;
-	*((uint32_t *) (raw0 + 8)) = pkt0_slab1;
-	*((uint64_t *) raw1) = pkt1_slab0;
-	*((uint32_t *) (raw1 + 8)) = pkt1_slab1;
-	*((uint64_t *) raw2) = pkt2_slab0;
-	*((uint32_t *) (raw2 + 8)) = pkt2_slab1;
-	*((uint64_t *) raw3) = pkt3_slab0;
-	*((uint32_t *) (raw3 + 8)) = pkt3_slab1;
+	/* Read */
+	uint64_t macaddr_dst0 = entry0->macaddr;
+	uint64_t macaddr_dst1 = entry1->macaddr;
+	uint64_t macaddr_dst2 = entry2->macaddr;
+	uint64_t macaddr_dst3 = entry3->macaddr;
+
+	uint64_t *slab_ptr0 = (uint64_t *) ((char *) pkts[0]->buf_addr +
+		(pkts[0]->data_off - 2));
+	uint64_t *slab_ptr1 = (uint64_t *) ((char *) pkts[1]->buf_addr +
+		(pkts[1]->data_off - 2));
+	uint64_t *slab_ptr2 = (uint64_t *) ((char *) pkts[2]->buf_addr +
+		(pkts[2]->data_off - 2));
+	uint64_t *slab_ptr3 = (uint64_t *) ((char *) pkts[3]->buf_addr +
+		(pkts[3]->data_off - 2));
+
+	/* Compute */
+
+	/* Write */
+	*slab_ptr0 = macaddr_dst0;
+	*slab_ptr1 = macaddr_dst1;
+	*slab_ptr2 = macaddr_dst2;
+	*slab_ptr3 = macaddr_dst3;
 }
 
 PIPELINE_TABLE_AH_HIT(arp_table_ah_hit,
 	pkt_work_arp,
 	pkt4_work_arp);
 
-static int
-pipeline_routing_parse_args(struct pipeline_routing *p,
+/*
+ * Argument parsing
+ */
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
 	struct pipeline_params *params)
 {
 	uint32_t n_routes_present = 0;
 	uint32_t n_arp_entries_present = 0;
-	uint32_t ip_da_offset_present = 0;
+	uint32_t ip_hdr_offset_present = 0;
 	uint32_t arp_key_offset_present = 0;
+	uint32_t l2_present = 0;
+	uint32_t qinq_sched_present = 0;
+	uint32_t mpls_color_mark_present = 0;
+	uint32_t color_offset_present = 0;
 	uint32_t i;
 
+	/* default values */
+	p->arp = 0;
+	p->qinq_sched = 0;
+	p->mpls_color_mark = 0;
+
 	for (i = 0; i < params->n_args; i++) {
 		char *arg_name = params->args_name[i];
 		char *arg_value = params->args_value[i];
@@ -338,12 +1104,26 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* ip_hdr_offset */
+		if (strcmp(arg_name, "ip_hdr_offset") == 0) {
+			if (ip_hdr_offset_present)
+				return -1;
+			ip_hdr_offset_present = 1;
+
+			p->ip_hdr_offset = atoi(arg_value);
+			p->ip_da_offset = p->ip_hdr_offset +
+				__builtin_offsetof(struct ipv4_hdr, dst_addr);
+
+			continue;
+		}
+
 		/* n_arp_entries */
 		if (strcmp(arg_name, "n_arp_entries") == 0) {
 			if (n_arp_entries_present)
 				return -1;
 			n_arp_entries_present = 1;
 
+			p->arp = 1;
 			p->n_arp_entries = atoi(arg_value);
 			if (p->n_arp_entries == 0)
 				return -1;
@@ -351,24 +1131,84 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
-		/* ip_da_offset */
-		if (strcmp(arg_name, "ip_da_offset") == 0) {
-			if (ip_da_offset_present)
+		/* arp_key_offset */
+		if (strcmp(arg_name, "arp_key_offset") == 0) {
+			if (arp_key_offset_present)
 				return -1;
-			ip_da_offset_present = 1;
+			arp_key_offset_present = 1;
 
-			p->ip_da_offset = atoi(arg_value);
+			p->arp = 1;
+			p->arp_key_offset = atoi(arg_value);
 
 			continue;
 		}
 
-		/* arp_key_offset */
-		if (strcmp(arg_name, "arp_key_offset") == 0) {
-			if (arp_key_offset_present)
+		/* l2 */
+		if (strcmp(arg_name, "l2") == 0) {
+			if (l2_present)
 				return -1;
-			arp_key_offset_present = 1;
+			l2_present = 1;
+
+			/* qinq */
+			if (strcmp(arg_value, "qinq") == 0) {
+				p->qinq = 1;
+				continue;
+			}
+
+			/* mpls */
+			if (strcmp(arg_value, "mpls") == 0) {
+				p->mpls = 1;
+				continue;
+			}
+
+			/* any other */
+			return -1;
+		}
 
-			p->arp_key_offset = atoi(arg_value);
+		/* qinq_sched */
+		if (strcmp(arg_name, "qinq_sched") == 0) {
+			if (qinq_sched_present)
+				return -1;
+
+			qinq_sched_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->qinq_sched = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->qinq_sched = 1;
+			else if (strcmp(arg_value, "test") == 0)
+				p->qinq_sched = 2;
+			else
+				return -1;
+
+			continue;
+		}
+
+		/* mpls_color_mark */
+		if (strcmp(arg_name, "mpls_color_mark") == 0) {
+			if (mpls_color_mark_present)
+				return -1;
+
+			mpls_color_mark_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->mpls_color_mark = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->mpls_color_mark = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
+		/* color_offset */
+		if (strcmp(arg_name, "color_offset") == 0) {
+			if (color_offset_present)
+				return -1;
+
+			color_offset_present = 1;
+
+			p->color_offset = atoi(arg_value);
 
 			continue;
 		}
@@ -379,9 +1219,19 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 
 	/* Check that mandatory arguments are present */
 	if ((n_routes_present == 0) ||
-		(n_arp_entries_present == 0) ||
-		(ip_da_offset_present == 0) ||
-		(n_arp_entries_present && (arp_key_offset_present == 0)))
+		(ip_hdr_offset_present == 0) ||
+		(n_arp_entries_present && (arp_key_offset_present == 0)) ||
+		((n_arp_entries_present == 0) && arp_key_offset_present))
+		return -1;
+
+	/* Check relations between arguments */
+	if ((p->qinq == 0) && qinq_sched_present)
+		return -1;
+
+	if (((p->mpls == 0) && (mpls_color_mark_present ||
+		color_offset_present)) ||
+		((mpls_color_mark_present == 0) && color_offset_present) ||
+		(mpls_color_mark_present && (color_offset_present == 0)))
 		return -1;
 
 	return 0;
@@ -414,7 +1264,7 @@ pipeline_routing_init(struct pipeline_params *params,
 	PLOG(p, HIGH, "Routing");
 
 	/* Parse arguments */
-	if (pipeline_routing_parse_args(p_rt, params))
+	if (pipeline_routing_parse_args(&p_rt->params, params))
 		return NULL;
 
 	/* Pipeline */
@@ -484,15 +1334,15 @@ pipeline_routing_init(struct pipeline_params *params,
 	p->n_tables = 1;
 	{
 		struct rte_table_lpm_params table_lpm_params = {
-			.n_rules = p_rt->n_routes,
+			.n_rules = p_rt->params.n_routes,
 			.entry_unique_size = sizeof(struct routing_table_entry),
-			.offset = p_rt->ip_da_offset,
+			.offset = p_rt->params.ip_da_offset,
 		};
 
 		struct rte_pipeline_table_params table_params = {
 				.ops = &rte_table_lpm_ops,
 				.arg_create = &table_lpm_params,
-				.f_action_hit = routing_table_ah_hit,
+				.f_action_hit = get_routing_table_ah_hit(p_rt),
 				.f_action_miss = NULL,
 				.arg_ah = p_rt,
 				.action_data_size =
@@ -514,14 +1364,14 @@ pipeline_routing_init(struct pipeline_params *params,
 	}
 
 	/* ARP table configuration */
-	if (p_rt->n_arp_entries) {
+	if (p_rt->params.arp) {
 		struct rte_table_hash_key8_ext_params table_arp_params = {
-			.n_entries = p_rt->n_arp_entries,
-			.n_entries_ext = p_rt->n_arp_entries,
+			.n_entries = p_rt->params.n_arp_entries,
+			.n_entries_ext = p_rt->params.n_arp_entries,
 			.f_hash = hash_default_key8,
 			.seed = 0,
 			.signature_offset = 0, /* Unused */
-			.key_offset = p_rt->arp_key_offset,
+			.key_offset = p_rt->params.arp_key_offset,
 		};
 
 		struct rte_pipeline_table_params table_params = {
@@ -665,6 +1515,7 @@ pipeline_routing_msg_req_custom_handler(struct pipeline *p,
 void *
 pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 {
+	struct pipeline_routing *p_rt = (struct pipeline_routing *) p;
 	struct pipeline_routing_route_add_msg_req *req = msg;
 	struct pipeline_routing_route_add_msg_rsp *rsp = msg;
 
@@ -673,26 +1524,321 @@ pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 		.depth = req->key.key.ipv4.depth,
 	};
 
-	struct routing_table_entry entry = {
+	struct routing_table_entry entry_arp0 = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->data.port_id]},
+		},
+
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = 0,
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
+	};
+
+	struct routing_table_entry entry_arp1 = {
 		.head = {
 			.action = RTE_PIPELINE_ACTION_TABLE,
 			{.table_id = p->table_id[1]},
 		},
 
-		.flags = req->flags,
-		.port_id = req->port_id,
-		.ip = rte_bswap32(req->ip),
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = rte_bswap32(req->data.ethernet.ip),
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
 	};
 
-	if (req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) {
+	struct rte_pipeline_table_entry *entry = (p_rt->params.arp) ?
+		(struct rte_pipeline_table_entry *) &entry_arp1 :
+		(struct rte_pipeline_table_entry *) &entry_arp0;
+
+	if ((req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) ||
+		((p_rt->params.arp == 0) &&
+		(req->data.flags & PIPELINE_ROUTING_ROUTE_ARP)) ||
+		(p_rt->params.arp &&
+		((req->data.flags & PIPELINE_ROUTING_ROUTE_ARP) == 0)) ||
+		((p_rt->params.qinq == 0) &&
+		(req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)) ||
+		(p_rt->params.qinq &&
+		((req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ) == 0)) ||
+		((p_rt->params.mpls == 0) &&
+		(req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS)) ||
+		(p_rt->params.mpls &&
+		((req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) == 0))) {
+
 		rsp->status = -1;
 		return rsp;
 	}
 
+	/* Ether - ARP off */
+	if ((p_rt->params.qinq == 0) &&
+		(p_rt->params.mpls == 0) &&
+		(p_rt->params.arp == 0)) {
+
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t macaddr_dst;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] =
+			rte_bswap64((macaddr_src << 16) | ethertype);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[1] + 2;
+		entry_arp0.ether_l2_length = 14;
+	}
+
+	/* Ether - ARP on */
+	if ((p_rt->params.qinq == 0) && (p_rt->params.mpls == 0) &&
+		p_rt->params.arp) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		entry_arp1.slab[0] = rte_bswap64((macaddr_src << 16) |
+			ethertype);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[0] - 6;
+		entry_arp1.ether_l2_length = 14;
+	}
+
+	/* Ether QinQ - ARP off */
+	if ((p_rt->params.qinq == 1) && (p_rt->params.mpls == 0) &&
+		(p_rt->params.arp == 0)) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.slab[2] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset - 3 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[2] + 2;
+		entry_arp0.ether_l2_length = 22;
+	}
+
+	/* Ether QinQ - ARP on */
+	if ((p_rt->params.qinq == 1) && (p_rt->params.mpls == 0) &&
+		p_rt->params.arp) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		entry_arp1.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp1.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[1] - 6;
+		entry_arp1.ether_l2_length = 22;
+	}
+
+	/* Ether MPLS - ARP off */
+	if ((p_rt->params.qinq == 0) &&
+		(p_rt->params.mpls == 1) &&
+		(p_rt->params.arp == 0)) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		switch (n_labels) {
+		case 1:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp0.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp0.slab[3] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[3] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 2 * 8);
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[3] + 2;
+		entry_arp0.ether_l2_length = n_labels * 4 + 14;
+	}
+
+	/* Ether MPLS - ARP on */
+	if ((p_rt->params.qinq == 0) &&
+		(p_rt->params.mpls == 1) &&
+		p_rt->params.arp) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		switch (n_labels) {
+		case 1:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp1.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp1.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[2] - 6;
+		entry_arp1.ether_l2_length = n_labels * 4 + 14;
+	}
+
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[0],
 		&key,
-		(struct rte_pipeline_table_entry *) &entry,
+		entry,
 		&rsp->key_found,
 		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
 
@@ -789,6 +1935,7 @@ pipeline_routing_msg_req_arp_add_handler(struct pipeline *p, void *msg)
 	}
 
 	*((struct ether_addr *) &entry.macaddr) = req->macaddr;
+	entry.macaddr = entry.macaddr << 16;
 
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[1],
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.h b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
index 45f37a1..7848dc1 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
@@ -39,6 +39,34 @@
 #include "pipeline_common_be.h"
 
 /*
+ * Pipeline argument parsing
+ */
+struct pipeline_routing_params {
+	/* routing */
+	uint32_t n_routes;
+
+	/* arp */
+	uint32_t n_arp_entries;
+	uint32_t arp;
+
+	/* l2 */
+	uint32_t qinq;
+	uint32_t qinq_sched;
+	uint32_t mpls;
+	uint32_t mpls_color_mark;
+
+	/* packet buffer offsets */
+	uint32_t ip_hdr_offset;
+	uint32_t ip_da_offset;
+	uint32_t arp_key_offset;
+	uint32_t color_offset;
+};
+
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
+	struct pipeline_params *params);
+
+/*
  * Route
  */
 enum pipeline_routing_route_key_type {
@@ -59,6 +87,36 @@ struct pipeline_routing_route_key {
 
 enum pipeline_routing_route_flags {
 	PIPELINE_ROUTING_ROUTE_LOCAL = 1 << 0, /* 0 = remote; 1 = local */
+	PIPELINE_ROUTING_ROUTE_ARP = 1 << 1, /* 0 = ARP OFF; 1 = ARP ON */
+	PIPELINE_ROUTING_ROUTE_QINQ = 1 << 2, /* 0 = QINQ OFF; 1 = QINQ ON */
+	PIPELINE_ROUTING_ROUTE_MPLS = 1 << 3, /* 0 = MPLS OFF; 1 = MPLS ON */
+};
+
+#define PIPELINE_ROUTING_MPLS_LABELS_MAX         4
+
+struct pipeline_routing_route_data {
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+
+	union {
+		/* Next hop IP (valid only when ARP is enabled) */
+		uint32_t ip;
+
+		/* Next hop MAC address (valid only when ARP disabled */
+		struct ether_addr macaddr;
+	} ethernet;
+
+	union {
+		struct {
+			uint16_t svlan;
+			uint16_t cvlan;
+		} qinq;
+
+		struct {
+			uint32_t labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+			uint32_t n_labels;
+		} mpls;
+	} l2;
 };
 
 /*
@@ -106,9 +164,7 @@ struct pipeline_routing_route_add_msg_req {
 	struct pipeline_routing_route_key key;
 
 	/* data */
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+	struct pipeline_routing_route_data data;
 };
 
 struct pipeline_routing_route_add_msg_rsp {
-- 
2.1.0

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

* Re: [dpdk-dev] [PATCH] ip_pipeline: add more functions to routing-pipeline
  2015-09-30 15:20 [dpdk-dev] [PATCH] ip_pipeline: add more functions to routing-pipeline Jasvinder Singh
@ 2015-09-30 15:38 ` Dumitrescu, Cristian
  2015-10-01  9:05   ` [dpdk-dev] [PATCH v2] " Jasvinder Singh
  0 siblings, 1 reply; 11+ messages in thread
From: Dumitrescu, Cristian @ 2015-09-30 15:38 UTC (permalink / raw)
  To: Singh, Jasvinder, dev



> -----Original Message-----
> From: Singh, Jasvinder
> Sent: Wednesday, September 30, 2015 4:21 PM
> To: dev@dpdk.org
> Cc: Dumitrescu, Cristian
> Subject: [PATCH] ip_pipeline: add more functions to routing-pipeline
> 
> This patch adds following features to the
> routing-pipeline to enable it for various NFV
> use-cases;
> 
> 1.Fast-path ARP table enable/disable
> 2.Double-tagged VLAN (Q-in-Q) packet enacapsulation
> for the next-hop
> 3.MPLS encapsulation for the next-hop
> 4.Add colour (Traffic-class for QoS) to the MPLS tag
> 5.Classification action to select the input queue
> of the hierarchical scehdular (QoS)
> 

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

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

* [dpdk-dev] [PATCH v2] ip_pipeline: add more functions to routing-pipeline
  2015-09-30 15:38 ` Dumitrescu, Cristian
@ 2015-10-01  9:05   ` Jasvinder Singh
  2015-10-01  9:09     ` Dumitrescu, Cristian
  2015-10-01 11:00     ` Neil Horman
  0 siblings, 2 replies; 11+ messages in thread
From: Jasvinder Singh @ 2015-10-01  9:05 UTC (permalink / raw)
  To: dev

This patch adds following features to the
routing-pipeline to enable it for various NFV
use-cases;

1.Fast-path ARP table enable/disable
2.Double-tagged VLAN (Q-in-Q) packet enacapsulation
for the next-hop
3.MPLS encapsulation for the next-hop
4.Add colour (Traffic-class for QoS) to the MPLS tag
5.Classification action to select the input queue
of the hierarchical scehdular (QoS)

The above proposed features can be enabled
(or disabled) through the parameters specified
in configuration file as below;

[PIPELINE0]
type = ROUTING
core = 1
pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
n_routes = 4096
n_arp_entries = 1024
ip_hdr_offset = 142
arp_key_offset = 64
l2 = qinq
qinq_sched = no

The LPM table entries might include additional
fields depending upon the packet encapsulation
(Q-in-Q, MPLS)for the next-hop. The CLI
commands for adding or deleting such entries
to LPM table have been implemented. Action
handlers for QinQ and MPLS encapsulation,
classification action to select the input queue
of the hierarchical schedular(QoS) and adding
colour (Traffic-class for QoS) to the MPLS
tag have been implemented.

*v2 changes:
- fixed bug in print_route

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
---
 examples/ip_pipeline/pipeline/pipeline_routing.c   |  800 ++++++++++-
 examples/ip_pipeline/pipeline/pipeline_routing.h   |    8 +-
 .../ip_pipeline/pipeline/pipeline_routing_be.c     | 1393 ++++++++++++++++++--
 .../ip_pipeline/pipeline/pipeline_routing_be.h     |   62 +-
 4 files changed, 2076 insertions(+), 187 deletions(-)

diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline/pipeline_routing.c
index beec982..f6d1f0e 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.c
@@ -43,7 +43,7 @@
 
 struct app_pipeline_routing_route {
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params params;
+	struct pipeline_routing_route_data data;
 	void *entry_ptr;
 
 	TAILQ_ENTRY(app_pipeline_routing_route) node;
@@ -187,21 +187,52 @@ print_route(const struct app_pipeline_routing_route *route)
 				&route->key.key.ipv4;
 
 		printf("IP Prefix = %" PRIu32 ".%" PRIu32
-			".%" PRIu32 ".%" PRIu32 "/%" PRIu32 " => "
-			"(Port = %" PRIu32 ", Next Hop IP = "
-			"%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ")\n",
+			".%" PRIu32 ".%" PRIu32 "/%" PRIu32
+			" => (Port = %" PRIu32,
 			(key->ip >> 24) & 0xFF,
 			(key->ip >> 16) & 0xFF,
 			(key->ip >> 8) & 0xFF,
 			key->ip & 0xFF,
 
 			key->depth,
-			route->params.port_id,
+			route->data.port_id);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_ARP)
+			printf(
+				", Next Hop IP = %" PRIu32 ".%" PRIu32
+				".%" PRIu32 ".%" PRIu32,
+				(route->data.ethernet.ip >> 24) & 0xFF,
+				(route->data.ethernet.ip >> 16) & 0xFF,
+				(route->data.ethernet.ip >> 8) & 0xFF,
+				route->data.ethernet.ip & 0xFF);
+		else
+			printf(
+				", Next Hop HWaddress = %02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32,
+				route->data.ethernet.macaddr.addr_bytes[0],
+				route->data.ethernet.macaddr.addr_bytes[1],
+				route->data.ethernet.macaddr.addr_bytes[2],
+				route->data.ethernet.macaddr.addr_bytes[3],
+				route->data.ethernet.macaddr.addr_bytes[4],
+				route->data.ethernet.macaddr.addr_bytes[5]);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)
+			printf(", QinQ SVLAN = %" PRIu32 " CVLAN = %" PRIu32,
+				route->data.l2.qinq.svlan,
+				route->data.l2.qinq.cvlan);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) {
+			uint32_t i;
+
+			printf(", MPLS labels");
+			for (i = 0; i < route->data.l2.mpls.n_labels; i++)
+				printf(" %" PRIu32,
+					route->data.l2.mpls.labels[i]);
+		}
 
-			(route->params.ip >> 24) & 0xFF,
-			(route->params.ip >> 16) & 0xFF,
-			(route->params.ip >> 8) & 0xFF,
-			route->params.ip & 0xFF);
+		printf(")\n");
 	}
 }
 
@@ -212,6 +243,8 @@ print_arp_entry(const struct app_pipeline_routing_arp_entry *entry)
 		".%" PRIu32 ".%" PRIu32 ") => "
 		"HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
 		":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
+
+
 		entry->key.key.ipv4.port_id,
 		(entry->key.key.ipv4.ip >> 24) & 0xFF,
 		(entry->key.key.ipv4.ip >> 16) & 0xFF,
@@ -253,7 +286,7 @@ int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params)
+	struct pipeline_routing_route_data *data)
 {
 	struct pipeline_routing *p;
 
@@ -267,7 +300,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	/* Check input arguments */
 	if ((app == NULL) ||
 		(key == NULL) ||
-		(route_params == NULL))
+		(data == NULL))
 		return -1;
 
 	p = app_pipeline_data_fe(app, pipeline_id);
@@ -287,8 +320,8 @@ app_pipeline_routing_add_route(struct app_params *app,
 		netmask = (~0) << (32 - depth);
 		key->key.ipv4.ip &= netmask;
 
-		/* route params */
-		if (route_params->port_id >= p->n_ports_out)
+		/* data */
+		if (data->port_id >= p->n_ports_out)
 			return -1;
 	}
 	break;
@@ -318,9 +351,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	req->type = PIPELINE_MSG_REQ_CUSTOM;
 	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD;
 	memcpy(&req->key, key, sizeof(*key));
-	req->flags = route_params->flags;
-	req->port_id = route_params->port_id;
-	req->ip = route_params->ip;
+	memcpy(&req->data, data, sizeof(*data));
 
 	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
 	if (rsp == NULL) {
@@ -341,7 +372,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	}
 
 	memcpy(&entry->key, key, sizeof(*key));
-	memcpy(&entry->params, route_params, sizeof(*route_params));
+	memcpy(&entry->data, data, sizeof(*data));
 	entry->entry_ptr = rsp->entry_ptr;
 
 	/* Commit entry */
@@ -820,31 +851,174 @@ app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
 	return 0;
 }
 
+static int
+parse_labels(char *string, uint32_t *labels, uint32_t *n_labels)
+{
+	uint32_t n_max_labels = *n_labels, count = 0;
+
+	/* Check for void list of labels */
+	if (strcmp(string, "<void>") == 0) {
+		*n_labels = 0;
+		return 0;
+	}
+
+	/* At least one label should be present */
+	for ( ; (*string != '\0'); ) {
+		char *next;
+		int value;
+
+		if (count >= n_max_labels)
+			return -1;
+
+		if (count > 0) {
+			if (string[0] != ':')
+				return -1;
+
+			string++;
+		}
+
+		value = strtol(string, &next, 10);
+		if (next == string)
+			return -1;
+		string = next;
+
+		labels[count++] = (uint32_t) value;
+	}
+
+	*n_labels = count;
+	return 0;
+}
+
+/*
+ * route add (mpls = no, qinq = no, arp = no)
+ */
+
+struct cmd_route_add1_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+};
+
+static void
+cmd_route_add1_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add1_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = 0;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add1_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add1_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add1_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add1_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add1_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add1_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add1_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add1_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add1_result, macaddr);
+
+static cmdline_parse_inst_t cmd_route_add1 = {
+	.f = cmd_route_add1_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add1_p_string,
+		(void *)&cmd_route_add1_p,
+		(void *)&cmd_route_add1_route_string,
+		(void *)&cmd_route_add1_add_string,
+		(void *)&cmd_route_add1_ip,
+		(void *)&cmd_route_add1_depth,
+		(void *)&cmd_route_add1_port_string,
+		(void *)&cmd_route_add1_port,
+		(void *)&cmd_route_add1_ether_string,
+		(void *)&cmd_route_add1_macaddr,
+		NULL,
+	},
+};
+
 /*
- * route add
+ * route add (mpls = no, qinq = no, arp = yes)
  */
 
-struct cmd_route_add_result {
+struct cmd_route_add2_result {
 	cmdline_fixed_string_t p_string;
 	uint32_t p;
 	cmdline_fixed_string_t route_string;
 	cmdline_fixed_string_t add_string;
 	cmdline_ipaddr_t ip;
 	uint32_t depth;
+	cmdline_fixed_string_t port_string;
 	uint32_t port;
+	cmdline_fixed_string_t ether_string;
 	cmdline_ipaddr_t nh_ip;
 };
 
 static void
-cmd_route_add_parsed(
+cmd_route_add2_parsed(
 	void *parsed_result,
 	__rte_unused struct cmdline *cl,
 	void *data)
 {
-	struct cmd_route_add_result *params = parsed_result;
+	struct cmd_route_add2_result *params = parsed_result;
 	struct app_params *app = data;
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params rt_params;
+	struct pipeline_routing_route_data route_data;
 	int status;
 
 	/* Create route */
@@ -852,14 +1026,15 @@ cmd_route_add_parsed(
 	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
 	key.key.ipv4.depth = params->depth;
 
-	rt_params.flags = 0; /* remote route */
-	rt_params.port_id = params->port;
-	rt_params.ip = rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.flags = PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
 
 	status = app_pipeline_routing_add_route(app,
 		params->p,
 		&key,
-		&rt_params);
+		&route_data);
 
 	if (status != 0) {
 		printf("Command failed\n");
@@ -867,46 +1042,558 @@ cmd_route_add_parsed(
 	}
 }
 
-static cmdline_parse_token_string_t cmd_route_add_p_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, p_string,
+static cmdline_parse_token_string_t cmd_route_add2_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, p_string,
 	"p");
 
-static cmdline_parse_token_num_t cmd_route_add_p =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, p, UINT32);
+static cmdline_parse_token_num_t cmd_route_add2_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, p, UINT32);
 
-static cmdline_parse_token_string_t cmd_route_add_route_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, route_string,
+static cmdline_parse_token_string_t cmd_route_add2_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, route_string,
 	"route");
 
-static cmdline_parse_token_string_t cmd_route_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, add_string,
+static cmdline_parse_token_string_t cmd_route_add2_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, add_string,
 	"add");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, ip);
+static cmdline_parse_token_ipaddr_t cmd_route_add2_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add2_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add2_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add2_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, nh_ip);
+
+static cmdline_parse_inst_t cmd_route_add2 = {
+	.f = cmd_route_add2_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add2_p_string,
+		(void *)&cmd_route_add2_p,
+		(void *)&cmd_route_add2_route_string,
+		(void *)&cmd_route_add2_add_string,
+		(void *)&cmd_route_add2_ip,
+		(void *)&cmd_route_add2_depth,
+		(void *)&cmd_route_add2_port_string,
+		(void *)&cmd_route_add2_port,
+		(void *)&cmd_route_add2_ether_string,
+		(void *)&cmd_route_add2_nh_ip,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = no)
+ */
+
+struct cmd_route_add3_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add3_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add3_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add3_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add3_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add3_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add3_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add3_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add3_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add3_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add3_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add3_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add3_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add3_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add3_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add3 = {
+	.f = cmd_route_add3_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add3_p_string,
+		(void *)&cmd_route_add3_p,
+		(void *)&cmd_route_add3_route_string,
+		(void *)&cmd_route_add3_add_string,
+		(void *)&cmd_route_add3_ip,
+		(void *)&cmd_route_add3_depth,
+		(void *)&cmd_route_add3_port_string,
+		(void *)&cmd_route_add3_port,
+		(void *)&cmd_route_add3_ether_string,
+		(void *)&cmd_route_add3_macaddr,
+		(void *)&cmd_route_add3_qinq_string,
+		(void *)&cmd_route_add3_svlan,
+		(void *)&cmd_route_add3_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = yes)
+ */
+
+struct cmd_route_add4_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add4_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add4_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add4_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add4_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add4_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add4_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add4_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, nh_ip);
+
+static cmdline_parse_token_string_t cmd_route_add4_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add4_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add4_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add4 = {
+	.f = cmd_route_add4_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add4_p_string,
+		(void *)&cmd_route_add4_p,
+		(void *)&cmd_route_add4_route_string,
+		(void *)&cmd_route_add4_add_string,
+		(void *)&cmd_route_add4_ip,
+		(void *)&cmd_route_add4_depth,
+		(void *)&cmd_route_add4_port_string,
+		(void *)&cmd_route_add4_port,
+		(void *)&cmd_route_add4_ether_string,
+		(void *)&cmd_route_add4_nh_ip,
+		(void *)&cmd_route_add4_qinq_string,
+		(void *)&cmd_route_add4_svlan,
+		(void *)&cmd_route_add4_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = no)
+ */
+
+struct cmd_route_add5_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add5_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add5_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add5_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add5_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add5_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add5_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add5_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add5_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add5_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add5_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add5_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_string,
+	"mpls");
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_labels,
+	NULL);
+
+static cmdline_parse_inst_t cmd_route_add5 = {
+	.f = cmd_route_add5_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add5_p_string,
+		(void *)&cmd_route_add5_p,
+		(void *)&cmd_route_add5_route_string,
+		(void *)&cmd_route_add5_add_string,
+		(void *)&cmd_route_add5_ip,
+		(void *)&cmd_route_add5_depth,
+		(void *)&cmd_route_add5_port_string,
+		(void *)&cmd_route_add5_port,
+		(void *)&cmd_route_add5_ether_string,
+		(void *)&cmd_route_add5_macaddr,
+		(void *)&cmd_route_add5_mpls_string,
+		(void *)&cmd_route_add5_mpls_labels,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = yes)
+ */
+
+struct cmd_route_add6_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add6_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add6_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add6_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add6_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add6_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add6_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add6_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add6_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, ether_string,
+	"ether");
 
-static cmdline_parse_token_num_t cmd_route_add_depth =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, depth, UINT32);
+static cmdline_parse_token_ipaddr_t cmd_route_add6_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, nh_ip);
 
-static cmdline_parse_token_num_t cmd_route_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, port, UINT32);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_string,
+	"mpls");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_nh_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, nh_ip);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_labels,
+	NULL);
 
-static cmdline_parse_inst_t cmd_route_add = {
-	.f = cmd_route_add_parsed,
+static cmdline_parse_inst_t cmd_route_add6 = {
+	.f = cmd_route_add6_parsed,
 	.data = NULL,
-	.help_str = "Route add",
+	.help_str = "Route add (mpls = yes, arp = yes)",
 	.tokens = {
-		(void *)&cmd_route_add_p_string,
-		(void *)&cmd_route_add_p,
-		(void *)&cmd_route_add_route_string,
-		(void *)&cmd_route_add_add_string,
-		(void *)&cmd_route_add_ip,
-		(void *)&cmd_route_add_depth,
-		(void *)&cmd_route_add_port,
-		(void *)&cmd_route_add_nh_ip,
+		(void *)&cmd_route_add6_p_string,
+		(void *)&cmd_route_add6_p,
+		(void *)&cmd_route_add6_route_string,
+		(void *)&cmd_route_add6_add_string,
+		(void *)&cmd_route_add6_ip,
+		(void *)&cmd_route_add6_depth,
+		(void *)&cmd_route_add6_port_string,
+		(void *)&cmd_route_add6_port,
+		(void *)&cmd_route_add6_ether_string,
+		(void *)&cmd_route_add6_nh_ip,
+		(void *)&cmd_route_add6_mpls_string,
+		(void *)&cmd_route_add6_mpls_labels,
 		NULL,
 	},
 };
@@ -1519,7 +2206,12 @@ static cmdline_parse_inst_t cmd_arp_ls = {
 };
 
 static cmdline_parse_ctx_t pipeline_cmds[] = {
-	(cmdline_parse_inst_t *)&cmd_route_add,
+	(cmdline_parse_inst_t *)&cmd_route_add1,
+	(cmdline_parse_inst_t *)&cmd_route_add2,
+	(cmdline_parse_inst_t *)&cmd_route_add3,
+	(cmdline_parse_inst_t *)&cmd_route_add4,
+	(cmdline_parse_inst_t *)&cmd_route_add5,
+	(cmdline_parse_inst_t *)&cmd_route_add6,
 	(cmdline_parse_inst_t *)&cmd_route_del,
 	(cmdline_parse_inst_t *)&cmd_route_add_default,
 	(cmdline_parse_inst_t *)&cmd_route_del_default,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.h b/examples/ip_pipeline/pipeline/pipeline_routing.h
index e1016f9..fa41642 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.h
@@ -41,17 +41,11 @@
  * Route
  */
 
-struct app_pipeline_routing_route_params {
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* IP address for the next hop (only for remote routes) */
-};
-
 int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params);
+	struct pipeline_routing_route_data *data);
 
 int
 app_pipeline_routing_delete_route(struct app_params *app,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.c b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
index 1e817dd..14e6ce8 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
@@ -49,16 +49,29 @@
 #include "pipeline_actions_common.h"
 #include "hash_func.h"
 
+#define MPLS_LABEL(label, exp, s, ttl)					\
+	(((((uint64_t) (label)) & 0xFFFFFLLU) << 12) |		\
+	((((uint64_t) (exp)) & 0x7LLU) << 9) |				\
+	((((uint64_t) (s)) & 0x1LLU) << 8) |				\
+	(((uint64_t) (ttl)) & 0xFFLU))
+
+#define RTE_SCHED_PORT_HIERARCHY(subport, pipe,		\
+	traffic_class, queue, color)					\
+	((((uint32_t) (queue)) & 0x3) |					\
+	((((uint32_t) (traffic_class)) & 0x3) << 2) |		\
+	((((uint32_t) (pipe)) & 0xFFFFF) << 4) |			\
+	((((uint32_t) (subport)) & 0x3F) << 24) |			\
+	((((uint32_t) (color)) & 0x3) << 30))
+
 struct pipeline_routing {
 	struct pipeline p;
+	struct pipeline_routing_params params;
 	pipeline_msg_req_handler custom_handlers[PIPELINE_ROUTING_MSG_REQS];
-
-	uint32_t n_routes;
-	uint32_t n_arp_entries;
-	uint32_t ip_da_offset;
-	uint32_t arp_key_offset;
 } __rte_cache_aligned;
 
+/*
+ * Message handlers
+ */
 static void *
 pipeline_routing_msg_req_custom_handler(struct pipeline *p, void *msg);
 
@@ -138,35 +151,152 @@ struct routing_table_entry {
 	uint32_t flags;
 	uint32_t port_id; /* Output port ID */
 	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+
+	/* ether_l2 */
+	uint16_t data_offset;
+	uint16_t ether_l2_length;
+	uint64_t slab[4];
+	uint16_t slab_offset[4];
 };
 
-static inline void
+static inline __attribute__((always_inline)) void
 pkt_work_routing(
 	struct rte_mbuf *pkt,
 	struct rte_pipeline_table_entry *table_entry,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry =
 		(struct routing_table_entry *) table_entry;
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkt, p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->arp_key_offset);
-	uint32_t ip = RTE_MBUF_METADATA_UINT32(pkt, p_rt->ip_da_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr, *slab1_ptr, *slab2_ptr, *slab3_ptr;
+	uint32_t ip_da, nh_ip, port_id, sched;
+	uint16_t total_length, data_offset, ether_l2_length;
+
+	/* Read */
+	total_length = rte_bswap16(ip->total_length);
+	ip_da = ip->dst_addr;
+	data_offset = entry->data_offset;
+	ether_l2_length = entry->ether_l2_length;
+	slab0_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[0]);
+	slab1_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[1]);
+	slab2_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[2]);
+	slab3_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[3]);
+
+	if (arp) {
+		port_id = entry->port_id;
+		nh_ip = entry->ip;
+		if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip = ip_da;
+	}
+
+	/* Compute */
+	total_length += ether_l2_length;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp = ip->type_of_service >> 2;
+		uint32_t svlan, cvlan, tc, tc_q;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq = rte_bswap64(entry->slab[0]);
+
+			svlan = (slab_qinq >> 48) & 0xFFF;
+			cvlan = (slab_qinq >> 16) & 0xFFF;
+			tc = (dscp >> 2) & 0x3;
+			tc_q = dscp & 0x3;
+		} else {
+			uint32_t ip_src = rte_bswap32(ip->src_addr);
+
+			svlan = 0;
+			cvlan = (ip_src >> 16) & 0xFFF;
+			tc = (ip_src >> 2) & 0x3;
+			tc_q = ip_src & 0x3;
+		}
+
+		sched = RTE_SCHED_PORT_HIERARCHY(svlan,
+			cvlan,
+			tc,
+			tc_q,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkt->data_off = data_offset;
+	pkt->data_len = total_length;
+	pkt->pkt_len = total_length;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr = entry->slab[0];
+
+		if (arp == 0)
+			*slab1_ptr = entry->slab[1];
+	}
+
+	if (qinq) {
+		*slab0_ptr = entry->slab[0];
+		*slab1_ptr = entry->slab[1];
+
+		if (arp == 0)
+			*slab2_ptr = entry->slab[2];
+
+		if (qinq_sched)
+			pkt->hash.sched = sched;
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp = rte_bswap64(
+				(MPLS_LABEL(0, pkt_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt_color, 0, 0));
+
+			*slab0_ptr = entry->slab[0] | mpls_exp;
+			*slab1_ptr = entry->slab[1] | mpls_exp;
+			*slab2_ptr = entry->slab[2];
+		} else {
+			*slab0_ptr = entry->slab[0];
+			*slab1_ptr = entry->slab[1];
+			*slab2_ptr = entry->slab[2];
+		}
+
+		if (arp == 0)
+			*slab3_ptr = entry->slab[3];
+	}
 
-	arp_key->port_id = entry->port_id;
-	arp_key->ip = entry->ip;
-	if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key->ip = ip;
+	if (arp) {
+		arp_key->port_id = port_id;
+		arp_key->ip = nh_ip;
+	}
 }
 
-static inline void
+static inline __attribute__((always_inline)) void
 pkt4_work_routing(
 	struct rte_mbuf **pkts,
 	struct rte_pipeline_table_entry **table_entries,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry0 =
 		(struct routing_table_entry *) table_entries[0];
 	struct routing_table_entry *entry1 =
@@ -175,51 +305,692 @@ pkt4_work_routing(
 		(struct routing_table_entry *) table_entries[2];
 	struct routing_table_entry *entry3 =
 		(struct routing_table_entry *) table_entries[3];
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip0 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip1 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip2 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip3 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt0_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->params.color_offset);
+	enum rte_meter_color pkt1_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->params.color_offset);
+	enum rte_meter_color pkt2_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->params.color_offset);
+	enum rte_meter_color pkt3_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key0 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[0], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key1 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[1], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key2 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[2], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key3 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[3], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr0, *slab1_ptr0, *slab2_ptr0, *slab3_ptr0;
+	uint64_t *slab0_ptr1, *slab1_ptr1, *slab2_ptr1, *slab3_ptr1;
+	uint64_t *slab0_ptr2, *slab1_ptr2, *slab2_ptr2, *slab3_ptr2;
+	uint64_t *slab0_ptr3, *slab1_ptr3, *slab2_ptr3, *slab3_ptr3;
+
+	uint32_t ip_da0, nh_ip0, port_id0, sched0;
+	uint32_t ip_da1, nh_ip1, port_id1, sched1;
+	uint32_t ip_da2, nh_ip2, port_id2, sched2;
+	uint32_t ip_da3, nh_ip3, port_id3, sched3;
+
+	uint16_t total_length0, data_offset0, ether_l2_length0;
+	uint16_t total_length1, data_offset1, ether_l2_length1;
+	uint16_t total_length2, data_offset2, ether_l2_length2;
+	uint16_t total_length3, data_offset3, ether_l2_length3;
+
+	/* Read */
+	total_length0 = rte_bswap16(ip0->total_length);
+	total_length1 = rte_bswap16(ip1->total_length);
+	total_length2 = rte_bswap16(ip2->total_length);
+	total_length3 = rte_bswap16(ip3->total_length);
+
+	ip_da0 = ip0->dst_addr;
+	ip_da1 = ip1->dst_addr;
+	ip_da2 = ip2->dst_addr;
+	ip_da3 = ip3->dst_addr;
+
+	data_offset0 = entry0->data_offset;
+	data_offset1 = entry1->data_offset;
+	data_offset2 = entry2->data_offset;
+	data_offset3 = entry3->data_offset;
+
+	ether_l2_length0 = entry0->ether_l2_length;
+	ether_l2_length1 = entry1->ether_l2_length;
+	ether_l2_length2 = entry2->ether_l2_length;
+	ether_l2_length3 = entry3->ether_l2_length;
+
+	slab0_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[0]);
+	slab1_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[1]);
+	slab2_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[2]);
+	slab3_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[3]);
+
+	slab0_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[0]);
+	slab1_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[1]);
+	slab2_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[2]);
+	slab3_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[3]);
+
+	slab0_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[0]);
+	slab1_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[1]);
+	slab2_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[2]);
+	slab3_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[3]);
+
+	slab0_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[0]);
+	slab1_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[1]);
+	slab2_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[2]);
+	slab3_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[3]);
+
+	if (arp) {
+		port_id0 = entry0->port_id;
+		nh_ip0 = entry0->ip;
+		if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip0 = ip_da0;
+
+		port_id1 = entry1->port_id;
+		nh_ip1 = entry1->ip;
+		if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip1 = ip_da1;
+
+		port_id2 = entry2->port_id;
+		nh_ip2 = entry2->ip;
+		if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip2 = ip_da2;
+
+		port_id3 = entry3->port_id;
+		nh_ip3 = entry3->ip;
+		if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip3 = ip_da3;
+	}
+
+	/* Compute */
+	total_length0 += ether_l2_length0;
+	total_length1 += ether_l2_length1;
+	total_length2 += ether_l2_length2;
+	total_length3 += ether_l2_length3;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp0 = ip0->type_of_service >> 2;
+		uint32_t dscp1 = ip1->type_of_service >> 2;
+		uint32_t dscp2 = ip2->type_of_service >> 2;
+		uint32_t dscp3 = ip3->type_of_service >> 2;
+		uint32_t svlan0, cvlan0, tc0, tc_q0;
+		uint32_t svlan1, cvlan1, tc1, tc_q1;
+		uint32_t svlan2, cvlan2, tc2, tc_q2;
+		uint32_t svlan3, cvlan3, tc3, tc_q3;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq0 = rte_bswap64(entry0->slab[0]);
+			uint64_t slab_qinq1 = rte_bswap64(entry1->slab[0]);
+			uint64_t slab_qinq2 = rte_bswap64(entry2->slab[0]);
+			uint64_t slab_qinq3 = rte_bswap64(entry3->slab[0]);
+
+			svlan0 = (slab_qinq0 >> 48) & 0xFFF;
+			svlan1 = (slab_qinq1 >> 48) & 0xFFF;
+			svlan2 = (slab_qinq2 >> 48) & 0xFFF;
+			svlan3 = (slab_qinq3 >> 48) & 0xFFF;
+
+			cvlan0 = (slab_qinq0 >> 16) & 0xFFF;
+			cvlan1 = (slab_qinq1 >> 16) & 0xFFF;
+			cvlan2 = (slab_qinq2 >> 16) & 0xFFF;
+			cvlan3 = (slab_qinq3 >> 16) & 0xFFF;
+
+			tc0 = (dscp0 >> 2) & 0x3;
+			tc1 = (dscp1 >> 2) & 0x3;
+			tc2 = (dscp2 >> 2) & 0x3;
+			tc3 = (dscp3 >> 2) & 0x3;
+
+			tc_q0 = dscp0 & 0x3;
+			tc_q1 = dscp1 & 0x3;
+			tc_q2 = dscp2 & 0x3;
+			tc_q3 = dscp3 & 0x3;
+		} else {
+			uint32_t ip_src0 = rte_bswap32(ip0->src_addr);
+			uint32_t ip_src1 = rte_bswap32(ip1->src_addr);
+			uint32_t ip_src2 = rte_bswap32(ip2->src_addr);
+			uint32_t ip_src3 = rte_bswap32(ip3->src_addr);
+
+			svlan0 = 0;
+			svlan1 = 0;
+			svlan2 = 0;
+			svlan3 = 0;
+
+			cvlan0 = (ip_src0 >> 16) & 0xFFF;
+			cvlan1 = (ip_src1 >> 16) & 0xFFF;
+			cvlan2 = (ip_src2 >> 16) & 0xFFF;
+			cvlan3 = (ip_src3 >> 16) & 0xFFF;
+
+			tc0 = (ip_src0 >> 2) & 0x3;
+			tc1 = (ip_src1 >> 2) & 0x3;
+			tc2 = (ip_src2 >> 2) & 0x3;
+			tc3 = (ip_src3 >> 2) & 0x3;
+
+			tc_q0 = ip_src0 & 0x3;
+			tc_q1 = ip_src1 & 0x3;
+			tc_q2 = ip_src2 & 0x3;
+			tc_q3 = ip_src3 & 0x3;
+		}
+
+		sched0 = RTE_SCHED_PORT_HIERARCHY(svlan0,
+			cvlan0,
+			tc0,
+			tc_q0,
+			e_RTE_METER_GREEN);
+		sched1 = RTE_SCHED_PORT_HIERARCHY(svlan1,
+			cvlan1,
+			tc1,
+			tc_q1,
+			e_RTE_METER_GREEN);
+		sched2 = RTE_SCHED_PORT_HIERARCHY(svlan2,
+			cvlan2,
+			tc2,
+			tc_q2,
+			e_RTE_METER_GREEN);
+		sched3 = RTE_SCHED_PORT_HIERARCHY(svlan3,
+			cvlan3,
+			tc3,
+			tc_q3,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkts[0]->data_off = data_offset0;
+	pkts[1]->data_off = data_offset1;
+	pkts[2]->data_off = data_offset2;
+	pkts[3]->data_off = data_offset3;
+
+	pkts[0]->data_len = total_length0;
+	pkts[1]->data_len = total_length1;
+	pkts[2]->data_len = total_length2;
+	pkts[3]->data_len = total_length3;
+
+	pkts[0]->pkt_len = total_length0;
+	pkts[1]->pkt_len = total_length1;
+	pkts[2]->pkt_len = total_length2;
+	pkts[3]->pkt_len = total_length3;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		if (arp == 0) {
+			*slab1_ptr0 = entry0->slab[1];
+			*slab1_ptr1 = entry1->slab[1];
+			*slab1_ptr2 = entry2->slab[1];
+			*slab1_ptr3 = entry3->slab[1];
+		}
+	}
+
+	if (qinq) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		*slab1_ptr0 = entry0->slab[1];
+		*slab1_ptr1 = entry1->slab[1];
+		*slab1_ptr2 = entry2->slab[1];
+		*slab1_ptr3 = entry3->slab[1];
+
+		if (arp == 0) {
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		}
+
+		if (qinq_sched) {
+			pkts[0]->hash.sched = sched0;
+			pkts[1]->hash.sched = sched1;
+			pkts[2]->hash.sched = sched2;
+			pkts[3]->hash.sched = sched3;
+		}
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp0 = rte_bswap64(
+				(MPLS_LABEL(0, pkt0_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt0_color, 0, 0));
+			uint64_t mpls_exp1 = rte_bswap64(
+				(MPLS_LABEL(0, pkt1_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt1_color, 0, 0));
+			uint64_t mpls_exp2 = rte_bswap64(
+				(MPLS_LABEL(0, pkt2_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt2_color, 0, 0));
+			uint64_t mpls_exp3 = rte_bswap64(
+				(MPLS_LABEL(0, pkt3_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt3_color, 0, 0));
+
+			*slab0_ptr0 = entry0->slab[0] | mpls_exp0;
+			*slab0_ptr1 = entry1->slab[0] | mpls_exp1;
+			*slab0_ptr2 = entry2->slab[0] | mpls_exp2;
+			*slab0_ptr3 = entry3->slab[0] | mpls_exp3;
+
+			*slab1_ptr0 = entry0->slab[1] | mpls_exp0;
+			*slab1_ptr1 = entry1->slab[1] | mpls_exp1;
+			*slab1_ptr2 = entry2->slab[1] | mpls_exp2;
+			*slab1_ptr3 = entry3->slab[1] | mpls_exp3;
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		} else {
+			*slab0_ptr0 = entry0->slab[0];
+			*slab0_ptr1 = entry1->slab[0];
+			*slab0_ptr2 = entry2->slab[0];
+			*slab0_ptr3 = entry3->slab[0];
+
+			*slab1_ptr0 = entry0->slab[1];
+			*slab1_ptr1 = entry1->slab[1];
+			*slab1_ptr2 = entry2->slab[1];
+			*slab1_ptr3 = entry3->slab[1];
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		}
+
+		if (arp == 0) {
+			*slab3_ptr0 = entry0->slab[3];
+			*slab3_ptr1 = entry1->slab[3];
+			*slab3_ptr2 = entry2->slab[3];
+			*slab3_ptr3 = entry3->slab[3];
+		}
+	}
+
+	if (arp) {
+		arp_key0->port_id = port_id0;
+		arp_key1->port_id = port_id1;
+		arp_key2->port_id = port_id2;
+		arp_key3->port_id = port_id3;
+
+		arp_key0->ip = nh_ip0;
+		arp_key1->ip = nh_ip1;
+		arp_key2->ip = nh_ip2;
+		arp_key3->ip = nh_ip3;
+	}
+}
+
+static inline void
+pkt_work_routing_ether_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 0, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 0, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched0_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 1, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched0_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 1, 0, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched1_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 1, 1, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched1_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 1, 1, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched2_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 1, 2, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_qinq_sched2_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 1, 2, 0, 0);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color0_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 0, 0, 1, 0);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color0_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 0, 0, 1, 0);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color1_arp0(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 0, 0, 0, 1, 1);
+}
+
+static inline void
+pkt_work_routing_ether_mpls_color1_arp1(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg)
+{
+	pkt_work_routing(pkt, table_entry, arg, 1, 0, 0, 1, 1);
+}
+
+static inline void
+pkt4_work_routing_ether_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 0, 0, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 0, 0, 0, 0);
+}
 
-	uint32_t ip0 = RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->ip_da_offset);
-	uint32_t ip1 = RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->ip_da_offset);
-	uint32_t ip2 = RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->ip_da_offset);
-	uint32_t ip3 = RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->ip_da_offset);
+static inline void
+pkt4_work_routing_ether_qinq_sched0_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 1, 0, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched0_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 1, 0, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched1_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 1, 1, 0, 0);
+}
 
-	arp_key0->port_id = entry0->port_id;
-	arp_key1->port_id = entry1->port_id;
-	arp_key2->port_id = entry2->port_id;
-	arp_key3->port_id = entry3->port_id;
+static inline void
+pkt4_work_routing_ether_qinq_sched1_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 1, 1, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched2_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 1, 2, 0, 0);
+}
+
+static inline void
+pkt4_work_routing_ether_qinq_sched2_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 1, 2, 0, 0);
+}
 
-	arp_key0->ip = entry0->ip;
-	if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key0->ip = ip0;
+static inline void
+pkt4_work_routing_ether_mpls_color0_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 0, 0, 1, 0);
+}
 
-	arp_key1->ip = entry1->ip;
-	if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key1->ip = ip1;
+static inline void
+pkt4_work_routing_ether_mpls_color0_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 0, 0, 1, 0);
+}
 
-	arp_key2->ip = entry2->ip;
-	if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key2->ip = ip2;
+static inline void
+pkt4_work_routing_ether_mpls_color1_arp0(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 0, 0, 0, 1, 1);
+}
 
-	arp_key3->ip = entry3->ip;
-	if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key3->ip = ip3;
+static inline void
+pkt4_work_routing_ether_mpls_color1_arp1(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg)
+{
+	pkt4_work_routing(pkts, table_entries, arg, 1, 0, 0, 1, 1);
 }
 
-PIPELINE_TABLE_AH_HIT(routing_table_ah_hit,
-	pkt_work_routing,
-	pkt4_work_routing);
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_arp0,
+	pkt_work_routing_ether_arp0,
+	pkt4_work_routing_ether_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_arp1,
+	pkt_work_routing_ether_arp1,
+	pkt4_work_routing_ether_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched0_arp0,
+	pkt_work_routing_ether_qinq_sched0_arp0,
+	pkt4_work_routing_ether_qinq_sched0_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched0_arp1,
+	pkt_work_routing_ether_qinq_sched0_arp1,
+	pkt4_work_routing_ether_qinq_sched0_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched1_arp0,
+	pkt_work_routing_ether_qinq_sched1_arp0,
+	pkt4_work_routing_ether_qinq_sched1_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched1_arp1,
+	pkt_work_routing_ether_qinq_sched1_arp1,
+	pkt4_work_routing_ether_qinq_sched1_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched2_arp0,
+	pkt_work_routing_ether_qinq_sched2_arp0,
+	pkt4_work_routing_ether_qinq_sched2_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched2_arp1,
+	pkt_work_routing_ether_qinq_sched2_arp1,
+	pkt4_work_routing_ether_qinq_sched2_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color0_arp0,
+	pkt_work_routing_ether_mpls_color0_arp0,
+	pkt4_work_routing_ether_mpls_color0_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color0_arp1,
+	pkt_work_routing_ether_mpls_color0_arp1,
+	pkt4_work_routing_ether_mpls_color0_arp1);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color1_arp0,
+	pkt_work_routing_ether_mpls_color1_arp0,
+	pkt4_work_routing_ether_mpls_color1_arp0);
+
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color1_arp1,
+	pkt_work_routing_ether_mpls_color1_arp1,
+	pkt4_work_routing_ether_mpls_color1_arp1);
+
+static rte_pipeline_table_action_handler_hit
+get_routing_table_ah_hit(struct pipeline_routing *p)
+{
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_arp0;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_arp1;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_qinq_sched0_arp0;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 0) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_qinq_sched0_arp1;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 1) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_qinq_sched1_arp0;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 1) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_qinq_sched1_arp1;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 2) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_qinq_sched2_arp0;
+
+	if ((p->params.qinq == 1) &&
+		(p->params.qinq_sched == 2) &&
+		(p->params.mpls == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_qinq_sched2_arp1;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 0) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_mpls_color0_arp0;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 0) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_mpls_color0_arp1;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 1) &&
+		(p->params.arp == 0))
+		return routing_table_ah_hit_ether_mpls_color1_arp0;
+
+	if ((p->params.qinq == 0) &&
+		(p->params.mpls == 1) &&
+		(p->params.mpls_color_mark == 1) &&
+		(p->params.arp == 1))
+		return routing_table_ah_hit_ether_mpls_color1_arp1;
+
+	return NULL;
+}
 
 /*
  * ARP table
@@ -229,6 +1000,9 @@ struct arp_table_entry {
 	uint64_t macaddr;
 };
 
+/**
+ * ARP table AH
+ */
 static inline void
 pkt_work_arp(
 	struct rte_mbuf *pkt,
@@ -237,20 +1011,15 @@ pkt_work_arp(
 {
 	struct arp_table_entry *entry = (struct arp_table_entry *) table_entry;
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw = rte_pktmbuf_mtod(pkt, uint8_t *);
+	/* Read */
+	uint64_t macaddr_dst = entry->macaddr;
+	uint64_t *slab_ptr = (uint64_t *) ((char *) pkt->buf_addr +
+		(pkt->data_off - 2));
 
-	/* Read: table entry */
-	uint64_t mac_addr_dst = entry->macaddr;
-	uint64_t mac_addr_src = 0;
+	/* Compute */
 
-	/* Compute: Ethernet header */
-	uint64_t slab0 = mac_addr_dst | (mac_addr_src << 48);
-	uint32_t slab1 = mac_addr_src >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw) = slab0;
-	*((uint32_t *) (raw + 8)) = slab1;
+	/* Write */
+	*slab_ptr = macaddr_dst;
 }
 
 static inline void
@@ -268,59 +1037,56 @@ pkt4_work_arp(
 	struct arp_table_entry *entry3 =
 		(struct arp_table_entry *) table_entries[3];
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw0 = rte_pktmbuf_mtod(pkts[0], uint8_t *);
-	uint8_t *raw1 = rte_pktmbuf_mtod(pkts[1], uint8_t *);
-	uint8_t *raw2 = rte_pktmbuf_mtod(pkts[2], uint8_t *);
-	uint8_t *raw3 = rte_pktmbuf_mtod(pkts[3], uint8_t *);
-
-	/* Read: table entry */
-	uint64_t mac_addr_dst0 = entry0->macaddr;
-	uint64_t mac_addr_dst1 = entry1->macaddr;
-	uint64_t mac_addr_dst2 = entry2->macaddr;
-	uint64_t mac_addr_dst3 = entry3->macaddr;
-
-	uint64_t mac_addr_src0 = 0;
-	uint64_t mac_addr_src1 = 0;
-	uint64_t mac_addr_src2 = 0;
-	uint64_t mac_addr_src3 = 0;
-
-	/* Compute: Ethernet header */
-	uint64_t pkt0_slab0 = mac_addr_dst0 | (mac_addr_src0 << 48);
-	uint64_t pkt1_slab0 = mac_addr_dst1 | (mac_addr_src1 << 48);
-	uint64_t pkt2_slab0 = mac_addr_dst2 | (mac_addr_src2 << 48);
-	uint64_t pkt3_slab0 = mac_addr_dst3 | (mac_addr_src3 << 48);
-
-	uint32_t pkt0_slab1 = mac_addr_src0 >> 16;
-	uint32_t pkt1_slab1 = mac_addr_src1 >> 16;
-	uint32_t pkt2_slab1 = mac_addr_src2 >> 16;
-	uint32_t pkt3_slab1 = mac_addr_src3 >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw0) = pkt0_slab0;
-	*((uint32_t *) (raw0 + 8)) = pkt0_slab1;
-	*((uint64_t *) raw1) = pkt1_slab0;
-	*((uint32_t *) (raw1 + 8)) = pkt1_slab1;
-	*((uint64_t *) raw2) = pkt2_slab0;
-	*((uint32_t *) (raw2 + 8)) = pkt2_slab1;
-	*((uint64_t *) raw3) = pkt3_slab0;
-	*((uint32_t *) (raw3 + 8)) = pkt3_slab1;
+	/* Read */
+	uint64_t macaddr_dst0 = entry0->macaddr;
+	uint64_t macaddr_dst1 = entry1->macaddr;
+	uint64_t macaddr_dst2 = entry2->macaddr;
+	uint64_t macaddr_dst3 = entry3->macaddr;
+
+	uint64_t *slab_ptr0 = (uint64_t *) ((char *) pkts[0]->buf_addr +
+		(pkts[0]->data_off - 2));
+	uint64_t *slab_ptr1 = (uint64_t *) ((char *) pkts[1]->buf_addr +
+		(pkts[1]->data_off - 2));
+	uint64_t *slab_ptr2 = (uint64_t *) ((char *) pkts[2]->buf_addr +
+		(pkts[2]->data_off - 2));
+	uint64_t *slab_ptr3 = (uint64_t *) ((char *) pkts[3]->buf_addr +
+		(pkts[3]->data_off - 2));
+
+	/* Compute */
+
+	/* Write */
+	*slab_ptr0 = macaddr_dst0;
+	*slab_ptr1 = macaddr_dst1;
+	*slab_ptr2 = macaddr_dst2;
+	*slab_ptr3 = macaddr_dst3;
 }
 
 PIPELINE_TABLE_AH_HIT(arp_table_ah_hit,
 	pkt_work_arp,
 	pkt4_work_arp);
 
-static int
-pipeline_routing_parse_args(struct pipeline_routing *p,
+/*
+ * Argument parsing
+ */
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
 	struct pipeline_params *params)
 {
 	uint32_t n_routes_present = 0;
 	uint32_t n_arp_entries_present = 0;
-	uint32_t ip_da_offset_present = 0;
+	uint32_t ip_hdr_offset_present = 0;
 	uint32_t arp_key_offset_present = 0;
+	uint32_t l2_present = 0;
+	uint32_t qinq_sched_present = 0;
+	uint32_t mpls_color_mark_present = 0;
+	uint32_t color_offset_present = 0;
 	uint32_t i;
 
+	/* default values */
+	p->arp = 0;
+	p->qinq_sched = 0;
+	p->mpls_color_mark = 0;
+
 	for (i = 0; i < params->n_args; i++) {
 		char *arg_name = params->args_name[i];
 		char *arg_value = params->args_value[i];
@@ -338,12 +1104,26 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* ip_hdr_offset */
+		if (strcmp(arg_name, "ip_hdr_offset") == 0) {
+			if (ip_hdr_offset_present)
+				return -1;
+			ip_hdr_offset_present = 1;
+
+			p->ip_hdr_offset = atoi(arg_value);
+			p->ip_da_offset = p->ip_hdr_offset +
+				__builtin_offsetof(struct ipv4_hdr, dst_addr);
+
+			continue;
+		}
+
 		/* n_arp_entries */
 		if (strcmp(arg_name, "n_arp_entries") == 0) {
 			if (n_arp_entries_present)
 				return -1;
 			n_arp_entries_present = 1;
 
+			p->arp = 1;
 			p->n_arp_entries = atoi(arg_value);
 			if (p->n_arp_entries == 0)
 				return -1;
@@ -351,24 +1131,84 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
-		/* ip_da_offset */
-		if (strcmp(arg_name, "ip_da_offset") == 0) {
-			if (ip_da_offset_present)
+		/* arp_key_offset */
+		if (strcmp(arg_name, "arp_key_offset") == 0) {
+			if (arp_key_offset_present)
 				return -1;
-			ip_da_offset_present = 1;
+			arp_key_offset_present = 1;
 
-			p->ip_da_offset = atoi(arg_value);
+			p->arp = 1;
+			p->arp_key_offset = atoi(arg_value);
 
 			continue;
 		}
 
-		/* arp_key_offset */
-		if (strcmp(arg_name, "arp_key_offset") == 0) {
-			if (arp_key_offset_present)
+		/* l2 */
+		if (strcmp(arg_name, "l2") == 0) {
+			if (l2_present)
 				return -1;
-			arp_key_offset_present = 1;
+			l2_present = 1;
+
+			/* qinq */
+			if (strcmp(arg_value, "qinq") == 0) {
+				p->qinq = 1;
+				continue;
+			}
+
+			/* mpls */
+			if (strcmp(arg_value, "mpls") == 0) {
+				p->mpls = 1;
+				continue;
+			}
+
+			/* any other */
+			return -1;
+		}
 
-			p->arp_key_offset = atoi(arg_value);
+		/* qinq_sched */
+		if (strcmp(arg_name, "qinq_sched") == 0) {
+			if (qinq_sched_present)
+				return -1;
+
+			qinq_sched_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->qinq_sched = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->qinq_sched = 1;
+			else if (strcmp(arg_value, "test") == 0)
+				p->qinq_sched = 2;
+			else
+				return -1;
+
+			continue;
+		}
+
+		/* mpls_color_mark */
+		if (strcmp(arg_name, "mpls_color_mark") == 0) {
+			if (mpls_color_mark_present)
+				return -1;
+
+			mpls_color_mark_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->mpls_color_mark = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->mpls_color_mark = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
+		/* color_offset */
+		if (strcmp(arg_name, "color_offset") == 0) {
+			if (color_offset_present)
+				return -1;
+
+			color_offset_present = 1;
+
+			p->color_offset = atoi(arg_value);
 
 			continue;
 		}
@@ -379,9 +1219,19 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 
 	/* Check that mandatory arguments are present */
 	if ((n_routes_present == 0) ||
-		(n_arp_entries_present == 0) ||
-		(ip_da_offset_present == 0) ||
-		(n_arp_entries_present && (arp_key_offset_present == 0)))
+		(ip_hdr_offset_present == 0) ||
+		(n_arp_entries_present && (arp_key_offset_present == 0)) ||
+		((n_arp_entries_present == 0) && arp_key_offset_present))
+		return -1;
+
+	/* Check relations between arguments */
+	if ((p->qinq == 0) && qinq_sched_present)
+		return -1;
+
+	if (((p->mpls == 0) && (mpls_color_mark_present ||
+		color_offset_present)) ||
+		((mpls_color_mark_present == 0) && color_offset_present) ||
+		(mpls_color_mark_present && (color_offset_present == 0)))
 		return -1;
 
 	return 0;
@@ -414,7 +1264,7 @@ pipeline_routing_init(struct pipeline_params *params,
 	PLOG(p, HIGH, "Routing");
 
 	/* Parse arguments */
-	if (pipeline_routing_parse_args(p_rt, params))
+	if (pipeline_routing_parse_args(&p_rt->params, params))
 		return NULL;
 
 	/* Pipeline */
@@ -484,15 +1334,15 @@ pipeline_routing_init(struct pipeline_params *params,
 	p->n_tables = 1;
 	{
 		struct rte_table_lpm_params table_lpm_params = {
-			.n_rules = p_rt->n_routes,
+			.n_rules = p_rt->params.n_routes,
 			.entry_unique_size = sizeof(struct routing_table_entry),
-			.offset = p_rt->ip_da_offset,
+			.offset = p_rt->params.ip_da_offset,
 		};
 
 		struct rte_pipeline_table_params table_params = {
 				.ops = &rte_table_lpm_ops,
 				.arg_create = &table_lpm_params,
-				.f_action_hit = routing_table_ah_hit,
+				.f_action_hit = get_routing_table_ah_hit(p_rt),
 				.f_action_miss = NULL,
 				.arg_ah = p_rt,
 				.action_data_size =
@@ -514,14 +1364,14 @@ pipeline_routing_init(struct pipeline_params *params,
 	}
 
 	/* ARP table configuration */
-	if (p_rt->n_arp_entries) {
+	if (p_rt->params.arp) {
 		struct rte_table_hash_key8_ext_params table_arp_params = {
-			.n_entries = p_rt->n_arp_entries,
-			.n_entries_ext = p_rt->n_arp_entries,
+			.n_entries = p_rt->params.n_arp_entries,
+			.n_entries_ext = p_rt->params.n_arp_entries,
 			.f_hash = hash_default_key8,
 			.seed = 0,
 			.signature_offset = 0, /* Unused */
-			.key_offset = p_rt->arp_key_offset,
+			.key_offset = p_rt->params.arp_key_offset,
 		};
 
 		struct rte_pipeline_table_params table_params = {
@@ -665,6 +1515,7 @@ pipeline_routing_msg_req_custom_handler(struct pipeline *p,
 void *
 pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 {
+	struct pipeline_routing *p_rt = (struct pipeline_routing *) p;
 	struct pipeline_routing_route_add_msg_req *req = msg;
 	struct pipeline_routing_route_add_msg_rsp *rsp = msg;
 
@@ -673,26 +1524,321 @@ pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 		.depth = req->key.key.ipv4.depth,
 	};
 
-	struct routing_table_entry entry = {
+	struct routing_table_entry entry_arp0 = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->data.port_id]},
+		},
+
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = 0,
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
+	};
+
+	struct routing_table_entry entry_arp1 = {
 		.head = {
 			.action = RTE_PIPELINE_ACTION_TABLE,
 			{.table_id = p->table_id[1]},
 		},
 
-		.flags = req->flags,
-		.port_id = req->port_id,
-		.ip = rte_bswap32(req->ip),
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = rte_bswap32(req->data.ethernet.ip),
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
 	};
 
-	if (req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) {
+	struct rte_pipeline_table_entry *entry = (p_rt->params.arp) ?
+		(struct rte_pipeline_table_entry *) &entry_arp1 :
+		(struct rte_pipeline_table_entry *) &entry_arp0;
+
+	if ((req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) ||
+		((p_rt->params.arp == 0) &&
+		(req->data.flags & PIPELINE_ROUTING_ROUTE_ARP)) ||
+		(p_rt->params.arp &&
+		((req->data.flags & PIPELINE_ROUTING_ROUTE_ARP) == 0)) ||
+		((p_rt->params.qinq == 0) &&
+		(req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)) ||
+		(p_rt->params.qinq &&
+		((req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ) == 0)) ||
+		((p_rt->params.mpls == 0) &&
+		(req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS)) ||
+		(p_rt->params.mpls &&
+		((req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) == 0))) {
+
 		rsp->status = -1;
 		return rsp;
 	}
 
+	/* Ether - ARP off */
+	if ((p_rt->params.qinq == 0) &&
+		(p_rt->params.mpls == 0) &&
+		(p_rt->params.arp == 0)) {
+
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t macaddr_dst;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] =
+			rte_bswap64((macaddr_src << 16) | ethertype);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[1] + 2;
+		entry_arp0.ether_l2_length = 14;
+	}
+
+	/* Ether - ARP on */
+	if ((p_rt->params.qinq == 0) && (p_rt->params.mpls == 0) &&
+		p_rt->params.arp) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		entry_arp1.slab[0] = rte_bswap64((macaddr_src << 16) |
+			ethertype);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[0] - 6;
+		entry_arp1.ether_l2_length = 14;
+	}
+
+	/* Ether QinQ - ARP off */
+	if ((p_rt->params.qinq == 1) && (p_rt->params.mpls == 0) &&
+		(p_rt->params.arp == 0)) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.slab[2] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset - 3 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[2] + 2;
+		entry_arp0.ether_l2_length = 22;
+	}
+
+	/* Ether QinQ - ARP on */
+	if ((p_rt->params.qinq == 1) && (p_rt->params.mpls == 0) &&
+		p_rt->params.arp) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		entry_arp1.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp1.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[1] - 6;
+		entry_arp1.ether_l2_length = 22;
+	}
+
+	/* Ether MPLS - ARP off */
+	if ((p_rt->params.qinq == 0) &&
+		(p_rt->params.mpls == 1) &&
+		(p_rt->params.arp == 0)) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		switch (n_labels) {
+		case 1:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp0.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp0.slab[3] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[3] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 2 * 8);
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[3] + 2;
+		entry_arp0.ether_l2_length = n_labels * 4 + 14;
+	}
+
+	/* Ether MPLS - ARP on */
+	if ((p_rt->params.qinq == 0) &&
+		(p_rt->params.mpls == 1) &&
+		p_rt->params.arp) {
+		uint64_t macaddr_src = 0x112233445566;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		switch (n_labels) {
+		case 1:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp1.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp1.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[2] - 6;
+		entry_arp1.ether_l2_length = n_labels * 4 + 14;
+	}
+
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[0],
 		&key,
-		(struct rte_pipeline_table_entry *) &entry,
+		entry,
 		&rsp->key_found,
 		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
 
@@ -789,6 +1935,7 @@ pipeline_routing_msg_req_arp_add_handler(struct pipeline *p, void *msg)
 	}
 
 	*((struct ether_addr *) &entry.macaddr) = req->macaddr;
+	entry.macaddr = entry.macaddr << 16;
 
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[1],
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.h b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
index 45f37a1..7848dc1 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
@@ -39,6 +39,34 @@
 #include "pipeline_common_be.h"
 
 /*
+ * Pipeline argument parsing
+ */
+struct pipeline_routing_params {
+	/* routing */
+	uint32_t n_routes;
+
+	/* arp */
+	uint32_t n_arp_entries;
+	uint32_t arp;
+
+	/* l2 */
+	uint32_t qinq;
+	uint32_t qinq_sched;
+	uint32_t mpls;
+	uint32_t mpls_color_mark;
+
+	/* packet buffer offsets */
+	uint32_t ip_hdr_offset;
+	uint32_t ip_da_offset;
+	uint32_t arp_key_offset;
+	uint32_t color_offset;
+};
+
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
+	struct pipeline_params *params);
+
+/*
  * Route
  */
 enum pipeline_routing_route_key_type {
@@ -59,6 +87,36 @@ struct pipeline_routing_route_key {
 
 enum pipeline_routing_route_flags {
 	PIPELINE_ROUTING_ROUTE_LOCAL = 1 << 0, /* 0 = remote; 1 = local */
+	PIPELINE_ROUTING_ROUTE_ARP = 1 << 1, /* 0 = ARP OFF; 1 = ARP ON */
+	PIPELINE_ROUTING_ROUTE_QINQ = 1 << 2, /* 0 = QINQ OFF; 1 = QINQ ON */
+	PIPELINE_ROUTING_ROUTE_MPLS = 1 << 3, /* 0 = MPLS OFF; 1 = MPLS ON */
+};
+
+#define PIPELINE_ROUTING_MPLS_LABELS_MAX         4
+
+struct pipeline_routing_route_data {
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+
+	union {
+		/* Next hop IP (valid only when ARP is enabled) */
+		uint32_t ip;
+
+		/* Next hop MAC address (valid only when ARP disabled */
+		struct ether_addr macaddr;
+	} ethernet;
+
+	union {
+		struct {
+			uint16_t svlan;
+			uint16_t cvlan;
+		} qinq;
+
+		struct {
+			uint32_t labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+			uint32_t n_labels;
+		} mpls;
+	} l2;
 };
 
 /*
@@ -106,9 +164,7 @@ struct pipeline_routing_route_add_msg_req {
 	struct pipeline_routing_route_key key;
 
 	/* data */
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+	struct pipeline_routing_route_data data;
 };
 
 struct pipeline_routing_route_add_msg_rsp {
-- 
2.1.0

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

* Re: [dpdk-dev] [PATCH v2] ip_pipeline: add more functions to routing-pipeline
  2015-10-01  9:05   ` [dpdk-dev] [PATCH v2] " Jasvinder Singh
@ 2015-10-01  9:09     ` Dumitrescu, Cristian
  2015-10-01 11:00     ` Neil Horman
  1 sibling, 0 replies; 11+ messages in thread
From: Dumitrescu, Cristian @ 2015-10-01  9:09 UTC (permalink / raw)
  To: Singh, Jasvinder, dev



> -----Original Message-----
> From: Singh, Jasvinder
> Sent: Thursday, October 1, 2015 10:05 AM
> To: dev@dpdk.org
> Cc: Dumitrescu, Cristian
> Subject: [PATCH v2] ip_pipeline: add more functions to routing-pipeline
> 
> *v2 changes:
> - fixed bug in print_route
> 
> Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
> ---

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

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

* Re: [dpdk-dev] [PATCH v2] ip_pipeline: add more functions to routing-pipeline
  2015-10-01  9:05   ` [dpdk-dev] [PATCH v2] " Jasvinder Singh
  2015-10-01  9:09     ` Dumitrescu, Cristian
@ 2015-10-01 11:00     ` Neil Horman
  2015-10-01 12:37       ` Dumitrescu, Cristian
  1 sibling, 1 reply; 11+ messages in thread
From: Neil Horman @ 2015-10-01 11:00 UTC (permalink / raw)
  To: Jasvinder Singh; +Cc: dev

>  
>  /*
> @@ -106,9 +164,7 @@ struct pipeline_routing_route_add_msg_req {
>  	struct pipeline_routing_route_key key;
>  
>  	/* data */
> -	uint32_t flags;
> -	uint32_t port_id; /* Output port ID */
> -	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
> +	struct pipeline_routing_route_data data;
>  };
>  

The example that you modifying appears to directly set the structure fields that
you removing above.  As such these appear to be ABI breaking changes and need to
go through the ABI process

Neil

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

* Re: [dpdk-dev] [PATCH v2] ip_pipeline: add more functions to routing-pipeline
  2015-10-01 11:00     ` Neil Horman
@ 2015-10-01 12:37       ` Dumitrescu, Cristian
  2015-10-01 17:18         ` Neil Horman
  0 siblings, 1 reply; 11+ messages in thread
From: Dumitrescu, Cristian @ 2015-10-01 12:37 UTC (permalink / raw)
  To: Neil Horman, Singh, Jasvinder; +Cc: dev



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Neil Horman
> Sent: Thursday, October 1, 2015 12:01 PM
> To: Singh, Jasvinder
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v2] ip_pipeline: add more functions to
> routing-pipeline
> 
> >
> >  /*
> > @@ -106,9 +164,7 @@ struct pipeline_routing_route_add_msg_req {
> >  	struct pipeline_routing_route_key key;
> >
> >  	/* data */
> > -	uint32_t flags;
> > -	uint32_t port_id; /* Output port ID */
> > -	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
> > +	struct pipeline_routing_route_data data;
> >  };
> >
> 
> The example that you modifying appears to directly set the structure fields
> that
> you removing above.  As such these appear to be ABI breaking changes and
> need to
> go through the ABI process
> 
> Neil

Hi Neil,

This patch only changes application code (in DPDK examples/ip_pipeline folder), it does not change any library code (in DPDK lib folder). There is no ABI versioning required for the example applications, so I don't think the ABI restrictions are applicable here.

The pipelines in the ip_pipeline application are provided only as examples to encourage people to create their own pipelines, and their implementation is evolving as new features are added.  They are intended to support only a limited set of protocols and features; for example, in this case of the routing pipeline, there is no intention to have them support an exhaustive list of routing protocols (as this would be virtually impossible). Therefore, there is no plan to standardize them and make them library code, where the API/ABI preservation is required.

The code where we are committed to keep the API compatibility and apply the ABI change process rigorously is the library code (e.g. librte_port, librte_table, librte_pipeline) and we are consistently doing this.

Thank you for your comment!

Regards,
Cristian

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

* Re: [dpdk-dev] [PATCH v2] ip_pipeline: add more functions to routing-pipeline
  2015-10-01 12:37       ` Dumitrescu, Cristian
@ 2015-10-01 17:18         ` Neil Horman
  2015-10-19 10:59           ` [dpdk-dev] [PATCH v3] " Jasvinder Singh
  0 siblings, 1 reply; 11+ messages in thread
From: Neil Horman @ 2015-10-01 17:18 UTC (permalink / raw)
  To: Dumitrescu, Cristian; +Cc: dev

On Thu, Oct 01, 2015 at 12:37:51PM +0000, Dumitrescu, Cristian wrote:
> 
> 
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Neil Horman
> > Sent: Thursday, October 1, 2015 12:01 PM
> > To: Singh, Jasvinder
> > Cc: dev@dpdk.org
> > Subject: Re: [dpdk-dev] [PATCH v2] ip_pipeline: add more functions to
> > routing-pipeline
> > 
> > >
> > >  /*
> > > @@ -106,9 +164,7 @@ struct pipeline_routing_route_add_msg_req {
> > >  	struct pipeline_routing_route_key key;
> > >
> > >  	/* data */
> > > -	uint32_t flags;
> > > -	uint32_t port_id; /* Output port ID */
> > > -	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
> > > +	struct pipeline_routing_route_data data;
> > >  };
> > >
> > 
> > The example that you modifying appears to directly set the structure fields
> > that
> > you removing above.  As such these appear to be ABI breaking changes and
> > need to
> > go through the ABI process
> > 
> > Neil
> 
> Hi Neil,
> 
> This patch only changes application code (in DPDK examples/ip_pipeline folder), it does not change any library code (in DPDK lib folder). There is no ABI versioning required for the example applications, so I don't think the ABI restrictions are applicable here.
> 
> The pipelines in the ip_pipeline application are provided only as examples to encourage people to create their own pipelines, and their implementation is evolving as new features are added.  They are intended to support only a limited set of protocols and features; for example, in this case of the routing pipeline, there is no intention to have them support an exhaustive list of routing protocols (as this would be virtually impossible). Therefore, there is no plan to standardize them and make them library code, where the API/ABI preservation is required.
> 
> The code where we are committed to keep the API compatibility and apply the ABI change process rigorously is the library code (e.g. librte_port, librte_table, librte_pipeline) and we are consistently doing this.
> 
> Thank you for your comment!
> 
Yup, youre right, my bad, I looked at the header file and thought it was part of
one of the libraries

Neil

> Regards,
> Cristian
> 
> 

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

* [dpdk-dev] [PATCH v3] ip_pipeline: add more functions to routing-pipeline
  2015-10-01 17:18         ` Neil Horman
@ 2015-10-19 10:59           ` Jasvinder Singh
  2015-10-28 10:35             ` [dpdk-dev] [PATCH v4] " Jasvinder Singh
  0 siblings, 1 reply; 11+ messages in thread
From: Jasvinder Singh @ 2015-10-19 10:59 UTC (permalink / raw)
  To: dev

This patch adds following features to the
routing-pipeline to enable it for various NFV
use-cases;

1.Fast-path ARP table enable/disable
2.Double-tagged VLAN (Q-in-Q) packet enacapsulation
for the next-hop
3.MPLS encapsulation for the next-hop
4.Add colour (Traffic-class for QoS) to the MPLS tag
5.Classification action to select the input queue
of the hierarchical scehdular (QoS)

The above proposed features can be enabled
(or disabled) through the parameters specified
in configuration file as below;

[PIPELINE0]
type = ROUTING
core = 1
pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
n_routes = 4096
n_arp_entries = 1024
ip_hdr_offset = 142
arp_key_offset = 64
l2 = qinq
qinq_sched = no

The LPM table entries might include additional
fields depending upon the packet encapsulation
(Q-in-Q, MPLS)for the next-hop. The CLI
commands for adding or deleting such entries
to LPM table have been implemented. Action
handlers for QinQ and MPLS encapsulation,
classification action to select the input queue
of the hierarchical schedular(QoS) and adding
colour (Traffic-class for QoS) to the MPLS
tag have been implemented.

v2:
*fixed bug in print_route

v3:
*replaced config file "l2 = qinq/mpls" with
"encap = ethernet/ethernet_qinq/ethernet_mpls"

*added config file option "dbg_ah_disable=yes/no"
to disable table action handlers for routing and
arp, which is a quick way to disable packet
encapsulation.

*compacted routing table action handlers

*writing 6 bytes for the macaddr_dst instead of
8 bytes during encapsulation, as the additional
2 bytes are located on previous cache line

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>

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

---
 examples/ip_pipeline/pipeline/pipeline_routing.c   |  806 ++++++++++++-
 examples/ip_pipeline/pipeline/pipeline_routing.h   |    8 +-
 .../ip_pipeline/pipeline/pipeline_routing_be.c     | 1256 ++++++++++++++++++--
 .../ip_pipeline/pipeline/pipeline_routing_be.h     |   72 +-
 4 files changed, 1954 insertions(+), 188 deletions(-)

diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline/pipeline_routing.c
index beec982..4f6ff81 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.c
@@ -43,7 +43,7 @@
 
 struct app_pipeline_routing_route {
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params params;
+	struct pipeline_routing_route_data data;
 	void *entry_ptr;
 
 	TAILQ_ENTRY(app_pipeline_routing_route) node;
@@ -187,21 +187,55 @@ print_route(const struct app_pipeline_routing_route *route)
 				&route->key.key.ipv4;
 
 		printf("IP Prefix = %" PRIu32 ".%" PRIu32
-			".%" PRIu32 ".%" PRIu32 "/%" PRIu32 " => "
-			"(Port = %" PRIu32 ", Next Hop IP = "
-			"%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ")\n",
+			".%" PRIu32 ".%" PRIu32 "/%" PRIu32
+			" => (Port = %" PRIu32,
+
 			(key->ip >> 24) & 0xFF,
 			(key->ip >> 16) & 0xFF,
 			(key->ip >> 8) & 0xFF,
 			key->ip & 0xFF,
 
 			key->depth,
-			route->params.port_id,
+			route->data.port_id);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_ARP)
+			printf(
+				", Next Hop IP = %" PRIu32 ".%" PRIu32
+				".%" PRIu32 ".%" PRIu32,
+
+				(route->data.ethernet.ip >> 24) & 0xFF,
+				(route->data.ethernet.ip >> 16) & 0xFF,
+				(route->data.ethernet.ip >> 8) & 0xFF,
+				route->data.ethernet.ip & 0xFF);
+		else
+			printf(
+				", Next Hop HWaddress = %02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32,
+
+				route->data.ethernet.macaddr.addr_bytes[0],
+				route->data.ethernet.macaddr.addr_bytes[1],
+				route->data.ethernet.macaddr.addr_bytes[2],
+				route->data.ethernet.macaddr.addr_bytes[3],
+				route->data.ethernet.macaddr.addr_bytes[4],
+				route->data.ethernet.macaddr.addr_bytes[5]);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)
+			printf(", QinQ SVLAN = %" PRIu32 " CVLAN = %" PRIu32,
+				route->data.l2.qinq.svlan,
+				route->data.l2.qinq.cvlan);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) {
+			uint32_t i;
+
+			printf(", MPLS labels");
+			for (i = 0; i < route->data.l2.mpls.n_labels; i++)
+				printf(" %" PRIu32,
+					route->data.l2.mpls.labels[i]);
+		}
 
-			(route->params.ip >> 24) & 0xFF,
-			(route->params.ip >> 16) & 0xFF,
-			(route->params.ip >> 8) & 0xFF,
-			route->params.ip & 0xFF);
+		printf(")\n");
 	}
 }
 
@@ -209,9 +243,10 @@ static void
 print_arp_entry(const struct app_pipeline_routing_arp_entry *entry)
 {
 	printf("(Port = %" PRIu32 ", IP = %" PRIu32 ".%" PRIu32
-		".%" PRIu32 ".%" PRIu32 ") => "
-		"HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
+		".%" PRIu32 ".%" PRIu32
+		") => HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
 		":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
+
 		entry->key.key.ipv4.port_id,
 		(entry->key.key.ipv4.ip >> 24) & 0xFF,
 		(entry->key.key.ipv4.ip >> 16) & 0xFF,
@@ -253,7 +288,7 @@ int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params)
+	struct pipeline_routing_route_data *data)
 {
 	struct pipeline_routing *p;
 
@@ -267,7 +302,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	/* Check input arguments */
 	if ((app == NULL) ||
 		(key == NULL) ||
-		(route_params == NULL))
+		(data == NULL))
 		return -1;
 
 	p = app_pipeline_data_fe(app, pipeline_id);
@@ -287,8 +322,8 @@ app_pipeline_routing_add_route(struct app_params *app,
 		netmask = (~0) << (32 - depth);
 		key->key.ipv4.ip &= netmask;
 
-		/* route params */
-		if (route_params->port_id >= p->n_ports_out)
+		/* data */
+		if (data->port_id >= p->n_ports_out)
 			return -1;
 	}
 	break;
@@ -318,9 +353,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	req->type = PIPELINE_MSG_REQ_CUSTOM;
 	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD;
 	memcpy(&req->key, key, sizeof(*key));
-	req->flags = route_params->flags;
-	req->port_id = route_params->port_id;
-	req->ip = route_params->ip;
+	memcpy(&req->data, data, sizeof(*data));
 
 	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
 	if (rsp == NULL) {
@@ -341,7 +374,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	}
 
 	memcpy(&entry->key, key, sizeof(*key));
-	memcpy(&entry->params, route_params, sizeof(*route_params));
+	memcpy(&entry->data, data, sizeof(*data));
 	entry->entry_ptr = rsp->entry_ptr;
 
 	/* Commit entry */
@@ -820,31 +853,174 @@ app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
 	return 0;
 }
 
+static int
+parse_labels(char *string, uint32_t *labels, uint32_t *n_labels)
+{
+	uint32_t n_max_labels = *n_labels, count = 0;
+
+	/* Check for void list of labels */
+	if (strcmp(string, "<void>") == 0) {
+		*n_labels = 0;
+		return 0;
+	}
+
+	/* At least one label should be present */
+	for ( ; (*string != '\0'); ) {
+		char *next;
+		int value;
+
+		if (count >= n_max_labels)
+			return -1;
+
+		if (count > 0) {
+			if (string[0] != ':')
+				return -1;
+
+			string++;
+		}
+
+		value = strtol(string, &next, 10);
+		if (next == string)
+			return -1;
+		string = next;
+
+		labels[count++] = (uint32_t) value;
+	}
+
+	*n_labels = count;
+	return 0;
+}
+
+/*
+ * route add (mpls = no, qinq = no, arp = no)
+ */
+
+struct cmd_route_add1_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+};
+
+static void
+cmd_route_add1_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add1_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = 0;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add1_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add1_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add1_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add1_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add1_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add1_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add1_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add1_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add1_result, macaddr);
+
+static cmdline_parse_inst_t cmd_route_add1 = {
+	.f = cmd_route_add1_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add1_p_string,
+		(void *)&cmd_route_add1_p,
+		(void *)&cmd_route_add1_route_string,
+		(void *)&cmd_route_add1_add_string,
+		(void *)&cmd_route_add1_ip,
+		(void *)&cmd_route_add1_depth,
+		(void *)&cmd_route_add1_port_string,
+		(void *)&cmd_route_add1_port,
+		(void *)&cmd_route_add1_ether_string,
+		(void *)&cmd_route_add1_macaddr,
+		NULL,
+	},
+};
+
 /*
- * route add
+ * route add (mpls = no, qinq = no, arp = yes)
  */
 
-struct cmd_route_add_result {
+struct cmd_route_add2_result {
 	cmdline_fixed_string_t p_string;
 	uint32_t p;
 	cmdline_fixed_string_t route_string;
 	cmdline_fixed_string_t add_string;
 	cmdline_ipaddr_t ip;
 	uint32_t depth;
+	cmdline_fixed_string_t port_string;
 	uint32_t port;
+	cmdline_fixed_string_t ether_string;
 	cmdline_ipaddr_t nh_ip;
 };
 
 static void
-cmd_route_add_parsed(
+cmd_route_add2_parsed(
 	void *parsed_result,
 	__rte_unused struct cmdline *cl,
 	void *data)
 {
-	struct cmd_route_add_result *params = parsed_result;
+	struct cmd_route_add2_result *params = parsed_result;
 	struct app_params *app = data;
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params rt_params;
+	struct pipeline_routing_route_data route_data;
 	int status;
 
 	/* Create route */
@@ -852,14 +1028,15 @@ cmd_route_add_parsed(
 	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
 	key.key.ipv4.depth = params->depth;
 
-	rt_params.flags = 0; /* remote route */
-	rt_params.port_id = params->port;
-	rt_params.ip = rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.flags = PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
 
 	status = app_pipeline_routing_add_route(app,
 		params->p,
 		&key,
-		&rt_params);
+		&route_data);
 
 	if (status != 0) {
 		printf("Command failed\n");
@@ -867,46 +1044,558 @@ cmd_route_add_parsed(
 	}
 }
 
-static cmdline_parse_token_string_t cmd_route_add_p_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, p_string,
+static cmdline_parse_token_string_t cmd_route_add2_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, p_string,
 	"p");
 
-static cmdline_parse_token_num_t cmd_route_add_p =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, p, UINT32);
+static cmdline_parse_token_num_t cmd_route_add2_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, p, UINT32);
 
-static cmdline_parse_token_string_t cmd_route_add_route_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, route_string,
+static cmdline_parse_token_string_t cmd_route_add2_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, route_string,
 	"route");
 
-static cmdline_parse_token_string_t cmd_route_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, add_string,
+static cmdline_parse_token_string_t cmd_route_add2_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, add_string,
 	"add");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, ip);
+static cmdline_parse_token_ipaddr_t cmd_route_add2_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add2_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add2_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add2_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, nh_ip);
+
+static cmdline_parse_inst_t cmd_route_add2 = {
+	.f = cmd_route_add2_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add2_p_string,
+		(void *)&cmd_route_add2_p,
+		(void *)&cmd_route_add2_route_string,
+		(void *)&cmd_route_add2_add_string,
+		(void *)&cmd_route_add2_ip,
+		(void *)&cmd_route_add2_depth,
+		(void *)&cmd_route_add2_port_string,
+		(void *)&cmd_route_add2_port,
+		(void *)&cmd_route_add2_ether_string,
+		(void *)&cmd_route_add2_nh_ip,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = no)
+ */
+
+struct cmd_route_add3_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add3_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add3_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add3_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add3_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add3_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add3_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add3_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add3_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add3_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add3_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add3_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add3_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add3_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add3_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add3 = {
+	.f = cmd_route_add3_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add3_p_string,
+		(void *)&cmd_route_add3_p,
+		(void *)&cmd_route_add3_route_string,
+		(void *)&cmd_route_add3_add_string,
+		(void *)&cmd_route_add3_ip,
+		(void *)&cmd_route_add3_depth,
+		(void *)&cmd_route_add3_port_string,
+		(void *)&cmd_route_add3_port,
+		(void *)&cmd_route_add3_ether_string,
+		(void *)&cmd_route_add3_macaddr,
+		(void *)&cmd_route_add3_qinq_string,
+		(void *)&cmd_route_add3_svlan,
+		(void *)&cmd_route_add3_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = yes)
+ */
+
+struct cmd_route_add4_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add4_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add4_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add4_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add4_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add4_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add4_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add4_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, nh_ip);
+
+static cmdline_parse_token_string_t cmd_route_add4_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add4_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add4_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add4 = {
+	.f = cmd_route_add4_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add4_p_string,
+		(void *)&cmd_route_add4_p,
+		(void *)&cmd_route_add4_route_string,
+		(void *)&cmd_route_add4_add_string,
+		(void *)&cmd_route_add4_ip,
+		(void *)&cmd_route_add4_depth,
+		(void *)&cmd_route_add4_port_string,
+		(void *)&cmd_route_add4_port,
+		(void *)&cmd_route_add4_ether_string,
+		(void *)&cmd_route_add4_nh_ip,
+		(void *)&cmd_route_add4_qinq_string,
+		(void *)&cmd_route_add4_svlan,
+		(void *)&cmd_route_add4_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = no)
+ */
+
+struct cmd_route_add5_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add5_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add5_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add5_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add5_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add5_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add5_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add5_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add5_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add5_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add5_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add5_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_string,
+	"mpls");
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_labels,
+	NULL);
+
+static cmdline_parse_inst_t cmd_route_add5 = {
+	.f = cmd_route_add5_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add5_p_string,
+		(void *)&cmd_route_add5_p,
+		(void *)&cmd_route_add5_route_string,
+		(void *)&cmd_route_add5_add_string,
+		(void *)&cmd_route_add5_ip,
+		(void *)&cmd_route_add5_depth,
+		(void *)&cmd_route_add5_port_string,
+		(void *)&cmd_route_add5_port,
+		(void *)&cmd_route_add5_ether_string,
+		(void *)&cmd_route_add5_macaddr,
+		(void *)&cmd_route_add5_mpls_string,
+		(void *)&cmd_route_add5_mpls_labels,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = yes)
+ */
+
+struct cmd_route_add6_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add6_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add6_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add6_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add6_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add6_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add6_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add6_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add6_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, ether_string,
+	"ether");
 
-static cmdline_parse_token_num_t cmd_route_add_depth =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, depth, UINT32);
+static cmdline_parse_token_ipaddr_t cmd_route_add6_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, nh_ip);
 
-static cmdline_parse_token_num_t cmd_route_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, port, UINT32);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_string,
+	"mpls");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_nh_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, nh_ip);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_labels,
+	NULL);
 
-static cmdline_parse_inst_t cmd_route_add = {
-	.f = cmd_route_add_parsed,
+static cmdline_parse_inst_t cmd_route_add6 = {
+	.f = cmd_route_add6_parsed,
 	.data = NULL,
-	.help_str = "Route add",
+	.help_str = "Route add (mpls = yes, arp = yes)",
 	.tokens = {
-		(void *)&cmd_route_add_p_string,
-		(void *)&cmd_route_add_p,
-		(void *)&cmd_route_add_route_string,
-		(void *)&cmd_route_add_add_string,
-		(void *)&cmd_route_add_ip,
-		(void *)&cmd_route_add_depth,
-		(void *)&cmd_route_add_port,
-		(void *)&cmd_route_add_nh_ip,
+		(void *)&cmd_route_add6_p_string,
+		(void *)&cmd_route_add6_p,
+		(void *)&cmd_route_add6_route_string,
+		(void *)&cmd_route_add6_add_string,
+		(void *)&cmd_route_add6_ip,
+		(void *)&cmd_route_add6_depth,
+		(void *)&cmd_route_add6_port_string,
+		(void *)&cmd_route_add6_port,
+		(void *)&cmd_route_add6_ether_string,
+		(void *)&cmd_route_add6_nh_ip,
+		(void *)&cmd_route_add6_mpls_string,
+		(void *)&cmd_route_add6_mpls_labels,
 		NULL,
 	},
 };
@@ -1519,7 +2208,12 @@ static cmdline_parse_inst_t cmd_arp_ls = {
 };
 
 static cmdline_parse_ctx_t pipeline_cmds[] = {
-	(cmdline_parse_inst_t *)&cmd_route_add,
+	(cmdline_parse_inst_t *)&cmd_route_add1,
+	(cmdline_parse_inst_t *)&cmd_route_add2,
+	(cmdline_parse_inst_t *)&cmd_route_add3,
+	(cmdline_parse_inst_t *)&cmd_route_add4,
+	(cmdline_parse_inst_t *)&cmd_route_add5,
+	(cmdline_parse_inst_t *)&cmd_route_add6,
 	(cmdline_parse_inst_t *)&cmd_route_del,
 	(cmdline_parse_inst_t *)&cmd_route_add_default,
 	(cmdline_parse_inst_t *)&cmd_route_del_default,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.h b/examples/ip_pipeline/pipeline/pipeline_routing.h
index e1016f9..fa41642 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.h
@@ -41,17 +41,11 @@
  * Route
  */
 
-struct app_pipeline_routing_route_params {
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* IP address for the next hop (only for remote routes) */
-};
-
 int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params);
+	struct pipeline_routing_route_data *data);
 
 int
 app_pipeline_routing_delete_route(struct app_params *app,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.c b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
index 1e817dd..e095afe 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
@@ -49,16 +49,31 @@
 #include "pipeline_actions_common.h"
 #include "hash_func.h"
 
+#define MPLS_LABEL(label, exp, s, ttl)					\
+	(((((uint64_t) (label)) & 0xFFFFFLLU) << 12) |		\
+	((((uint64_t) (exp)) & 0x7LLU) << 9) |				\
+	((((uint64_t) (s)) & 0x1LLU) << 8) |				\
+	(((uint64_t) (ttl)) & 0xFFLU))
+
+#define RTE_SCHED_PORT_HIERARCHY(subport, pipe,		\
+	traffic_class, queue, color)					\
+	((((uint32_t) (queue)) & 0x3) |					\
+	((((uint32_t) (traffic_class)) & 0x3) << 2) |		\
+	((((uint32_t) (pipe)) & 0xFFFFF) << 4) |			\
+	((((uint32_t) (subport)) & 0x3F) << 24) |			\
+	((((uint32_t) (color)) & 0x3) << 30))
+
+#define MAC_SRC_DEFAULT 0x112233445566
+
 struct pipeline_routing {
 	struct pipeline p;
+	struct pipeline_routing_params params;
 	pipeline_msg_req_handler custom_handlers[PIPELINE_ROUTING_MSG_REQS];
-
-	uint32_t n_routes;
-	uint32_t n_arp_entries;
-	uint32_t ip_da_offset;
-	uint32_t arp_key_offset;
 } __rte_cache_aligned;
 
+/*
+ * Message handlers
+ */
 static void *
 pipeline_routing_msg_req_custom_handler(struct pipeline *p, void *msg);
 
@@ -138,35 +153,167 @@ struct routing_table_entry {
 	uint32_t flags;
 	uint32_t port_id; /* Output port ID */
 	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+
+	/* ether_l2 */
+	uint16_t data_offset;
+	uint16_t ether_l2_length;
+	uint64_t slab[4];
+	uint16_t slab_offset[4];
 };
 
-static inline void
+struct layout {
+	uint16_t a;
+	uint32_t b;
+	uint16_t c;
+} __attribute__((__packed__));
+
+#define MACADDR_DST_WRITE(slab_ptr, slab)			\
+{								\
+	struct layout *dst = (struct layout *) (slab_ptr);	\
+	struct layout *src = (struct layout *) &(slab);		\
+								\
+	dst->b = src->b;					\
+	dst->c = src->c;					\
+}
+
+static inline __attribute__((always_inline)) void
 pkt_work_routing(
 	struct rte_mbuf *pkt,
 	struct rte_pipeline_table_entry *table_entry,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry =
 		(struct routing_table_entry *) table_entry;
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkt, p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->arp_key_offset);
-	uint32_t ip = RTE_MBUF_METADATA_UINT32(pkt, p_rt->ip_da_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr, *slab1_ptr, *slab2_ptr, *slab3_ptr;
+	uint32_t ip_da, nh_ip, port_id, sched;
+	uint16_t total_length, data_offset, ether_l2_length;
+
+	/* Read */
+	total_length = rte_bswap16(ip->total_length);
+	ip_da = ip->dst_addr;
+	data_offset = entry->data_offset;
+	ether_l2_length = entry->ether_l2_length;
+	slab0_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[0]);
+	slab1_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[1]);
+	slab2_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[2]);
+	slab3_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[3]);
+
+	if (arp) {
+		port_id = entry->port_id;
+		nh_ip = entry->ip;
+		if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip = ip_da;
+	}
+
+	/* Compute */
+	total_length += ether_l2_length;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp = ip->type_of_service >> 2;
+		uint32_t svlan, cvlan, tc, tc_q;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq = rte_bswap64(entry->slab[0]);
+
+			svlan = (slab_qinq >> 48) & 0xFFF;
+			cvlan = (slab_qinq >> 16) & 0xFFF;
+			tc = (dscp >> 2) & 0x3;
+			tc_q = dscp & 0x3;
+		} else {
+			uint32_t ip_src = rte_bswap32(ip->src_addr);
+
+			svlan = 0;
+			cvlan = (ip_src >> 16) & 0xFFF;
+			tc = (ip_src >> 2) & 0x3;
+			tc_q = ip_src & 0x3;
+		}
 
-	arp_key->port_id = entry->port_id;
-	arp_key->ip = entry->ip;
-	if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key->ip = ip;
+		sched = RTE_SCHED_PORT_HIERARCHY(svlan,
+			cvlan,
+			tc,
+			tc_q,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkt->data_off = data_offset;
+	pkt->data_len = total_length;
+	pkt->pkt_len = total_length;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr = entry->slab[0];
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab1_ptr, entry->slab[1]);
+	}
+
+	if (qinq) {
+		*slab0_ptr = entry->slab[0];
+		*slab1_ptr = entry->slab[1];
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab2_ptr, entry->slab[2]);
+
+		if (qinq_sched)
+			pkt->hash.sched = sched;
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp = rte_bswap64(
+				(MPLS_LABEL(0, pkt_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt_color, 0, 0));
+
+			*slab0_ptr = entry->slab[0] | mpls_exp;
+			*slab1_ptr = entry->slab[1] | mpls_exp;
+			*slab2_ptr = entry->slab[2];
+		} else {
+			*slab0_ptr = entry->slab[0];
+			*slab1_ptr = entry->slab[1];
+			*slab2_ptr = entry->slab[2];
+		}
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab3_ptr, entry->slab[3]);
+	}
+
+	if (arp) {
+		arp_key->port_id = port_id;
+		arp_key->ip = nh_ip;
+	}
 }
 
-static inline void
+static inline __attribute__((always_inline)) void
 pkt4_work_routing(
 	struct rte_mbuf **pkts,
 	struct rte_pipeline_table_entry **table_entries,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry0 =
 		(struct routing_table_entry *) table_entries[0];
 	struct routing_table_entry *entry1 =
@@ -175,51 +322,501 @@ pkt4_work_routing(
 		(struct routing_table_entry *) table_entries[2];
 	struct routing_table_entry *entry3 =
 		(struct routing_table_entry *) table_entries[3];
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip0 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip1 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip2 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip3 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt0_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->params.color_offset);
+	enum rte_meter_color pkt1_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->params.color_offset);
+	enum rte_meter_color pkt2_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->params.color_offset);
+	enum rte_meter_color pkt3_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key0 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[0], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key1 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[1], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key2 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[2], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key3 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[3], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr0, *slab1_ptr0, *slab2_ptr0, *slab3_ptr0;
+	uint64_t *slab0_ptr1, *slab1_ptr1, *slab2_ptr1, *slab3_ptr1;
+	uint64_t *slab0_ptr2, *slab1_ptr2, *slab2_ptr2, *slab3_ptr2;
+	uint64_t *slab0_ptr3, *slab1_ptr3, *slab2_ptr3, *slab3_ptr3;
+
+	uint32_t ip_da0, nh_ip0, port_id0, sched0;
+	uint32_t ip_da1, nh_ip1, port_id1, sched1;
+	uint32_t ip_da2, nh_ip2, port_id2, sched2;
+	uint32_t ip_da3, nh_ip3, port_id3, sched3;
+
+	uint16_t total_length0, data_offset0, ether_l2_length0;
+	uint16_t total_length1, data_offset1, ether_l2_length1;
+	uint16_t total_length2, data_offset2, ether_l2_length2;
+	uint16_t total_length3, data_offset3, ether_l2_length3;
+
+	/* Read */
+	total_length0 = rte_bswap16(ip0->total_length);
+	total_length1 = rte_bswap16(ip1->total_length);
+	total_length2 = rte_bswap16(ip2->total_length);
+	total_length3 = rte_bswap16(ip3->total_length);
+
+	ip_da0 = ip0->dst_addr;
+	ip_da1 = ip1->dst_addr;
+	ip_da2 = ip2->dst_addr;
+	ip_da3 = ip3->dst_addr;
+
+	data_offset0 = entry0->data_offset;
+	data_offset1 = entry1->data_offset;
+	data_offset2 = entry2->data_offset;
+	data_offset3 = entry3->data_offset;
+
+	ether_l2_length0 = entry0->ether_l2_length;
+	ether_l2_length1 = entry1->ether_l2_length;
+	ether_l2_length2 = entry2->ether_l2_length;
+	ether_l2_length3 = entry3->ether_l2_length;
+
+	slab0_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[0]);
+	slab1_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[1]);
+	slab2_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[2]);
+	slab3_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[3]);
+
+	slab0_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[0]);
+	slab1_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[1]);
+	slab2_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[2]);
+	slab3_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[3]);
+
+	slab0_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[0]);
+	slab1_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[1]);
+	slab2_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[2]);
+	slab3_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[3]);
+
+	slab0_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[0]);
+	slab1_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[1]);
+	slab2_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[2]);
+	slab3_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[3]);
+
+	if (arp) {
+		port_id0 = entry0->port_id;
+		nh_ip0 = entry0->ip;
+		if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip0 = ip_da0;
+
+		port_id1 = entry1->port_id;
+		nh_ip1 = entry1->ip;
+		if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip1 = ip_da1;
+
+		port_id2 = entry2->port_id;
+		nh_ip2 = entry2->ip;
+		if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip2 = ip_da2;
+
+		port_id3 = entry3->port_id;
+		nh_ip3 = entry3->ip;
+		if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip3 = ip_da3;
+	}
+
+	/* Compute */
+	total_length0 += ether_l2_length0;
+	total_length1 += ether_l2_length1;
+	total_length2 += ether_l2_length2;
+	total_length3 += ether_l2_length3;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp0 = ip0->type_of_service >> 2;
+		uint32_t dscp1 = ip1->type_of_service >> 2;
+		uint32_t dscp2 = ip2->type_of_service >> 2;
+		uint32_t dscp3 = ip3->type_of_service >> 2;
+		uint32_t svlan0, cvlan0, tc0, tc_q0;
+		uint32_t svlan1, cvlan1, tc1, tc_q1;
+		uint32_t svlan2, cvlan2, tc2, tc_q2;
+		uint32_t svlan3, cvlan3, tc3, tc_q3;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq0 = rte_bswap64(entry0->slab[0]);
+			uint64_t slab_qinq1 = rte_bswap64(entry1->slab[0]);
+			uint64_t slab_qinq2 = rte_bswap64(entry2->slab[0]);
+			uint64_t slab_qinq3 = rte_bswap64(entry3->slab[0]);
+
+			svlan0 = (slab_qinq0 >> 48) & 0xFFF;
+			svlan1 = (slab_qinq1 >> 48) & 0xFFF;
+			svlan2 = (slab_qinq2 >> 48) & 0xFFF;
+			svlan3 = (slab_qinq3 >> 48) & 0xFFF;
+
+			cvlan0 = (slab_qinq0 >> 16) & 0xFFF;
+			cvlan1 = (slab_qinq1 >> 16) & 0xFFF;
+			cvlan2 = (slab_qinq2 >> 16) & 0xFFF;
+			cvlan3 = (slab_qinq3 >> 16) & 0xFFF;
+
+			tc0 = (dscp0 >> 2) & 0x3;
+			tc1 = (dscp1 >> 2) & 0x3;
+			tc2 = (dscp2 >> 2) & 0x3;
+			tc3 = (dscp3 >> 2) & 0x3;
+
+			tc_q0 = dscp0 & 0x3;
+			tc_q1 = dscp1 & 0x3;
+			tc_q2 = dscp2 & 0x3;
+			tc_q3 = dscp3 & 0x3;
+		} else {
+			uint32_t ip_src0 = rte_bswap32(ip0->src_addr);
+			uint32_t ip_src1 = rte_bswap32(ip1->src_addr);
+			uint32_t ip_src2 = rte_bswap32(ip2->src_addr);
+			uint32_t ip_src3 = rte_bswap32(ip3->src_addr);
+
+			svlan0 = 0;
+			svlan1 = 0;
+			svlan2 = 0;
+			svlan3 = 0;
+
+			cvlan0 = (ip_src0 >> 16) & 0xFFF;
+			cvlan1 = (ip_src1 >> 16) & 0xFFF;
+			cvlan2 = (ip_src2 >> 16) & 0xFFF;
+			cvlan3 = (ip_src3 >> 16) & 0xFFF;
+
+			tc0 = (ip_src0 >> 2) & 0x3;
+			tc1 = (ip_src1 >> 2) & 0x3;
+			tc2 = (ip_src2 >> 2) & 0x3;
+			tc3 = (ip_src3 >> 2) & 0x3;
+
+			tc_q0 = ip_src0 & 0x3;
+			tc_q1 = ip_src1 & 0x3;
+			tc_q2 = ip_src2 & 0x3;
+			tc_q3 = ip_src3 & 0x3;
+		}
+
+		sched0 = RTE_SCHED_PORT_HIERARCHY(svlan0,
+			cvlan0,
+			tc0,
+			tc_q0,
+			e_RTE_METER_GREEN);
+		sched1 = RTE_SCHED_PORT_HIERARCHY(svlan1,
+			cvlan1,
+			tc1,
+			tc_q1,
+			e_RTE_METER_GREEN);
+		sched2 = RTE_SCHED_PORT_HIERARCHY(svlan2,
+			cvlan2,
+			tc2,
+			tc_q2,
+			e_RTE_METER_GREEN);
+		sched3 = RTE_SCHED_PORT_HIERARCHY(svlan3,
+			cvlan3,
+			tc3,
+			tc_q3,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkts[0]->data_off = data_offset0;
+	pkts[1]->data_off = data_offset1;
+	pkts[2]->data_off = data_offset2;
+	pkts[3]->data_off = data_offset3;
+
+	pkts[0]->data_len = total_length0;
+	pkts[1]->data_len = total_length1;
+	pkts[2]->data_len = total_length2;
+	pkts[3]->data_len = total_length3;
+
+	pkts[0]->pkt_len = total_length0;
+	pkts[1]->pkt_len = total_length1;
+	pkts[2]->pkt_len = total_length2;
+	pkts[3]->pkt_len = total_length3;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab1_ptr0, entry0->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr1, entry1->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr2, entry2->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr3, entry3->slab[1]);
+		}
+	}
+
+	if (qinq) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		*slab1_ptr0 = entry0->slab[1];
+		*slab1_ptr1 = entry1->slab[1];
+		*slab1_ptr2 = entry2->slab[1];
+		*slab1_ptr3 = entry3->slab[1];
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab2_ptr0, entry0->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr1, entry1->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr2, entry2->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr3, entry3->slab[2]);
+		}
+
+		if (qinq_sched) {
+			pkts[0]->hash.sched = sched0;
+			pkts[1]->hash.sched = sched1;
+			pkts[2]->hash.sched = sched2;
+			pkts[3]->hash.sched = sched3;
+		}
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp0 = rte_bswap64(
+				(MPLS_LABEL(0, pkt0_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt0_color, 0, 0));
+			uint64_t mpls_exp1 = rte_bswap64(
+				(MPLS_LABEL(0, pkt1_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt1_color, 0, 0));
+			uint64_t mpls_exp2 = rte_bswap64(
+				(MPLS_LABEL(0, pkt2_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt2_color, 0, 0));
+			uint64_t mpls_exp3 = rte_bswap64(
+				(MPLS_LABEL(0, pkt3_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt3_color, 0, 0));
+
+			*slab0_ptr0 = entry0->slab[0] | mpls_exp0;
+			*slab0_ptr1 = entry1->slab[0] | mpls_exp1;
+			*slab0_ptr2 = entry2->slab[0] | mpls_exp2;
+			*slab0_ptr3 = entry3->slab[0] | mpls_exp3;
+
+			*slab1_ptr0 = entry0->slab[1] | mpls_exp0;
+			*slab1_ptr1 = entry1->slab[1] | mpls_exp1;
+			*slab1_ptr2 = entry2->slab[1] | mpls_exp2;
+			*slab1_ptr3 = entry3->slab[1] | mpls_exp3;
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		} else {
+			*slab0_ptr0 = entry0->slab[0];
+			*slab0_ptr1 = entry1->slab[0];
+			*slab0_ptr2 = entry2->slab[0];
+			*slab0_ptr3 = entry3->slab[0];
+
+			*slab1_ptr0 = entry0->slab[1];
+			*slab1_ptr1 = entry1->slab[1];
+			*slab1_ptr2 = entry2->slab[1];
+			*slab1_ptr3 = entry3->slab[1];
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		}
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab3_ptr0, entry0->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr1, entry1->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr2, entry2->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr3, entry3->slab[3]);
+		}
+	}
+
+	if (arp) {
+		arp_key0->port_id = port_id0;
+		arp_key1->port_id = port_id1;
+		arp_key2->port_id = port_id2;
+		arp_key3->port_id = port_id3;
+
+		arp_key0->ip = nh_ip0;
+		arp_key1->ip = nh_ip1;
+		arp_key2->ip = nh_ip2;
+		arp_key3->ip = nh_ip3;
+	}
+}
 
-	uint32_t ip0 = RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->ip_da_offset);
-	uint32_t ip1 = RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->ip_da_offset);
-	uint32_t ip2 = RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->ip_da_offset);
-	uint32_t ip3 = RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->ip_da_offset);
+#define PKT_WORK_ROUTING_ETHERNET(arp)				\
+static inline void						\
+pkt_work_routing_ether_arp##arp(				\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 0, 0, 0, 0);\
+}
 
-	arp_key0->port_id = entry0->port_id;
-	arp_key1->port_id = entry1->port_id;
-	arp_key2->port_id = entry2->port_id;
-	arp_key3->port_id = entry3->port_id;
+#define PKT4_WORK_ROUTING_ETHERNET(arp)				\
+static inline void						\
+pkt4_work_routing_ether_arp##arp(				\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 0, 0, 0, 0);\
+}
 
-	arp_key0->ip = entry0->ip;
-	if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key0->ip = ip0;
+#define routing_table_ah_hit_ether(arp)				\
+PKT_WORK_ROUTING_ETHERNET(arp)					\
+PKT4_WORK_ROUTING_ETHERNET(arp)					\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_arp##arp,	\
+	pkt_work_routing_ether_arp##arp,			\
+	pkt4_work_routing_ether_arp##arp)
+
+routing_table_ah_hit_ether(0)
+routing_table_ah_hit_ether(1)
+
+#define PKT_WORK_ROUTING_ETHERNET_QINQ(sched, arp)		\
+static inline void						\
+pkt_work_routing_ether_qinq_sched##sched##_arp##arp(		\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 1, sched, 0, 0);\
+}
 
-	arp_key1->ip = entry1->ip;
-	if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key1->ip = ip1;
+#define PKT4_WORK_ROUTING_ETHERNET_QINQ(sched, arp)		\
+static inline void						\
+pkt4_work_routing_ether_qinq_sched##sched##_arp##arp(		\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 1, sched, 0, 0);\
+}
 
-	arp_key2->ip = entry2->ip;
-	if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key2->ip = ip2;
+#define routing_table_ah_hit_ether_qinq(sched, arp)		\
+PKT_WORK_ROUTING_ETHERNET_QINQ(sched, arp)			\
+PKT4_WORK_ROUTING_ETHERNET_QINQ(sched, arp)			\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched##sched##_arp##arp,\
+	pkt_work_routing_ether_qinq_sched##sched##_arp##arp,	\
+	pkt4_work_routing_ether_qinq_sched##sched##_arp##arp)
+
+routing_table_ah_hit_ether_qinq(0, 0)
+routing_table_ah_hit_ether_qinq(1, 0)
+routing_table_ah_hit_ether_qinq(2, 0)
+routing_table_ah_hit_ether_qinq(0, 1)
+routing_table_ah_hit_ether_qinq(1, 1)
+routing_table_ah_hit_ether_qinq(2, 1)
+
+#define PKT_WORK_ROUTING_ETHERNET_MPLS(color, arp)		\
+static inline void						\
+pkt_work_routing_ether_mpls_color##color##_arp##arp(		\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 0, 0, 1, color);\
+}
 
-	arp_key3->ip = entry3->ip;
-	if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key3->ip = ip3;
+#define PKT4_WORK_ROUTING_ETHERNET_MPLS(color, arp)		\
+static inline void						\
+pkt4_work_routing_ether_mpls_color##color##_arp##arp(		\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 0, 0, 1, color);\
 }
 
-PIPELINE_TABLE_AH_HIT(routing_table_ah_hit,
-	pkt_work_routing,
-	pkt4_work_routing);
+#define routing_table_ah_hit_ether_mpls(color, arp)		\
+PKT_WORK_ROUTING_ETHERNET_MPLS(color, arp)			\
+PKT4_WORK_ROUTING_ETHERNET_MPLS(color, arp)			\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color##color##_arp##arp,\
+	pkt_work_routing_ether_mpls_color##color##_arp##arp,	\
+	pkt4_work_routing_ether_mpls_color##color##_arp##arp)
+
+routing_table_ah_hit_ether_mpls(0, 0)
+routing_table_ah_hit_ether_mpls(1, 0)
+routing_table_ah_hit_ether_mpls(0, 1)
+routing_table_ah_hit_ether_mpls(1, 1)
+
+static rte_pipeline_table_action_handler_hit
+get_routing_table_ah_hit(struct pipeline_routing *p)
+{
+	if (p->params.dbg_ah_disable)
+		return NULL;
+
+	switch (p->params.encap) {
+	case PIPELINE_ROUTING_ENCAP_ETHERNET:
+		return (p->params.n_arp_entries) ?
+			routing_table_ah_hit_ether_arp1 :
+			routing_table_ah_hit_ether_arp0;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ:
+		if (p->params.n_arp_entries)
+			switch (p->params.qinq_sched) {
+			case 0:
+				return routing_table_ah_hit_ether_qinq_sched0_arp1;
+			case 1:
+				return routing_table_ah_hit_ether_qinq_sched1_arp1;
+			case 2:
+				return routing_table_ah_hit_ether_qinq_sched2_arp1;
+			default:
+				return NULL;
+			}
+		 else
+			switch (p->params.qinq_sched) {
+			case 0:
+				return routing_table_ah_hit_ether_qinq_sched0_arp0;
+			case 1:
+				return routing_table_ah_hit_ether_qinq_sched1_arp0;
+			case 2:
+				return routing_table_ah_hit_ether_qinq_sched2_arp0;
+			default:
+				return NULL;
+			}
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS:
+		if (p->params.n_arp_entries)
+			if (p->params.mpls_color_mark)
+				return routing_table_ah_hit_ether_mpls_color1_arp1;
+			else
+				return routing_table_ah_hit_ether_mpls_color0_arp1;
+		else
+			if (p->params.mpls_color_mark)
+				return routing_table_ah_hit_ether_mpls_color1_arp0;
+			else
+				return routing_table_ah_hit_ether_mpls_color0_arp0;
+
+	default:
+		return NULL;
+	}
+}
 
 /*
  * ARP table
@@ -229,6 +826,9 @@ struct arp_table_entry {
 	uint64_t macaddr;
 };
 
+/**
+ * ARP table AH
+ */
 static inline void
 pkt_work_arp(
 	struct rte_mbuf *pkt,
@@ -237,20 +837,15 @@ pkt_work_arp(
 {
 	struct arp_table_entry *entry = (struct arp_table_entry *) table_entry;
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw = rte_pktmbuf_mtod(pkt, uint8_t *);
-
-	/* Read: table entry */
-	uint64_t mac_addr_dst = entry->macaddr;
-	uint64_t mac_addr_src = 0;
+	/* Read */
+	uint64_t macaddr_dst = entry->macaddr;
+	uint64_t *slab_ptr = (uint64_t *) ((char *) pkt->buf_addr +
+		(pkt->data_off - 2));
 
-	/* Compute: Ethernet header */
-	uint64_t slab0 = mac_addr_dst | (mac_addr_src << 48);
-	uint32_t slab1 = mac_addr_src >> 16;
+	/* Compute */
 
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw) = slab0;
-	*((uint32_t *) (raw + 8)) = slab1;
+	/* Write */
+	MACADDR_DST_WRITE(slab_ptr, macaddr_dst);
 }
 
 static inline void
@@ -268,59 +863,69 @@ pkt4_work_arp(
 	struct arp_table_entry *entry3 =
 		(struct arp_table_entry *) table_entries[3];
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw0 = rte_pktmbuf_mtod(pkts[0], uint8_t *);
-	uint8_t *raw1 = rte_pktmbuf_mtod(pkts[1], uint8_t *);
-	uint8_t *raw2 = rte_pktmbuf_mtod(pkts[2], uint8_t *);
-	uint8_t *raw3 = rte_pktmbuf_mtod(pkts[3], uint8_t *);
-
-	/* Read: table entry */
-	uint64_t mac_addr_dst0 = entry0->macaddr;
-	uint64_t mac_addr_dst1 = entry1->macaddr;
-	uint64_t mac_addr_dst2 = entry2->macaddr;
-	uint64_t mac_addr_dst3 = entry3->macaddr;
-
-	uint64_t mac_addr_src0 = 0;
-	uint64_t mac_addr_src1 = 0;
-	uint64_t mac_addr_src2 = 0;
-	uint64_t mac_addr_src3 = 0;
-
-	/* Compute: Ethernet header */
-	uint64_t pkt0_slab0 = mac_addr_dst0 | (mac_addr_src0 << 48);
-	uint64_t pkt1_slab0 = mac_addr_dst1 | (mac_addr_src1 << 48);
-	uint64_t pkt2_slab0 = mac_addr_dst2 | (mac_addr_src2 << 48);
-	uint64_t pkt3_slab0 = mac_addr_dst3 | (mac_addr_src3 << 48);
-
-	uint32_t pkt0_slab1 = mac_addr_src0 >> 16;
-	uint32_t pkt1_slab1 = mac_addr_src1 >> 16;
-	uint32_t pkt2_slab1 = mac_addr_src2 >> 16;
-	uint32_t pkt3_slab1 = mac_addr_src3 >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw0) = pkt0_slab0;
-	*((uint32_t *) (raw0 + 8)) = pkt0_slab1;
-	*((uint64_t *) raw1) = pkt1_slab0;
-	*((uint32_t *) (raw1 + 8)) = pkt1_slab1;
-	*((uint64_t *) raw2) = pkt2_slab0;
-	*((uint32_t *) (raw2 + 8)) = pkt2_slab1;
-	*((uint64_t *) raw3) = pkt3_slab0;
-	*((uint32_t *) (raw3 + 8)) = pkt3_slab1;
+	/* Read */
+	uint64_t macaddr_dst0 = entry0->macaddr;
+	uint64_t macaddr_dst1 = entry1->macaddr;
+	uint64_t macaddr_dst2 = entry2->macaddr;
+	uint64_t macaddr_dst3 = entry3->macaddr;
+
+	uint64_t *slab_ptr0 = (uint64_t *) ((char *) pkts[0]->buf_addr +
+		(pkts[0]->data_off - 2));
+	uint64_t *slab_ptr1 = (uint64_t *) ((char *) pkts[1]->buf_addr +
+		(pkts[1]->data_off - 2));
+	uint64_t *slab_ptr2 = (uint64_t *) ((char *) pkts[2]->buf_addr +
+		(pkts[2]->data_off - 2));
+	uint64_t *slab_ptr3 = (uint64_t *) ((char *) pkts[3]->buf_addr +
+		(pkts[3]->data_off - 2));
+
+	/* Compute */
+
+	/* Write */
+	MACADDR_DST_WRITE(slab_ptr0, macaddr_dst0);
+	MACADDR_DST_WRITE(slab_ptr1, macaddr_dst1);
+	MACADDR_DST_WRITE(slab_ptr2, macaddr_dst2);
+	MACADDR_DST_WRITE(slab_ptr3, macaddr_dst3);
 }
 
 PIPELINE_TABLE_AH_HIT(arp_table_ah_hit,
 	pkt_work_arp,
 	pkt4_work_arp);
 
-static int
-pipeline_routing_parse_args(struct pipeline_routing *p,
+static rte_pipeline_table_action_handler_hit
+get_arp_table_ah_hit(struct pipeline_routing *p)
+{
+	if (p->params.dbg_ah_disable)
+		return NULL;
+
+	return arp_table_ah_hit;
+}
+
+/*
+ * Argument parsing
+ */
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
 	struct pipeline_params *params)
 {
 	uint32_t n_routes_present = 0;
+	uint32_t encap_present = 0;
+	uint32_t qinq_sched_present = 0;
+	uint32_t mpls_color_mark_present = 0;
 	uint32_t n_arp_entries_present = 0;
-	uint32_t ip_da_offset_present = 0;
+	uint32_t ip_hdr_offset_present = 0;
 	uint32_t arp_key_offset_present = 0;
+	uint32_t color_offset_present = 0;
+	uint32_t dbg_ah_disable_present = 0;
 	uint32_t i;
 
+	/* default values */
+	p->n_routes = PIPELINE_ROUTING_N_ROUTES_DEFAULT;
+	p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET;
+	p->qinq_sched = 0;
+	p->mpls_color_mark = 0;
+	p->n_arp_entries = 0;
+	p->dbg_ah_disable = 0;
+
 	for (i = 0; i < params->n_args; i++) {
 		char *arg_name = params->args_name[i];
 		char *arg_value = params->args_value[i];
@@ -338,6 +943,70 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* encap */
+		if (strcmp(arg_name, "encap") == 0) {
+			if (encap_present)
+				return -1;
+			encap_present = 1;
+
+			/* ethernet */
+			if (strcmp(arg_value, "ethernet") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET;
+				continue;
+			}
+
+			/* ethernet_qinq */
+			if (strcmp(arg_value, "ethernet_qinq") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ;
+				continue;
+			}
+
+			/* ethernet_mpls */
+			if (strcmp(arg_value, "ethernet_mpls") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS;
+				continue;
+			}
+
+			/* any other */
+			return -1;
+		}
+
+		/* qinq_sched */
+		if (strcmp(arg_name, "qinq_sched") == 0) {
+			if (qinq_sched_present)
+				return -1;
+
+			qinq_sched_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->qinq_sched = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->qinq_sched = 1;
+			else if (strcmp(arg_value, "test") == 0)
+				p->qinq_sched = 2;
+			else
+				return -1;
+
+			continue;
+		}
+
+		/* mpls_color_mark */
+		if (strcmp(arg_name, "mpls_color_mark") == 0) {
+			if (mpls_color_mark_present)
+				return -1;
+
+			mpls_color_mark_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->mpls_color_mark = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->mpls_color_mark = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
 		/* n_arp_entries */
 		if (strcmp(arg_name, "n_arp_entries") == 0) {
 			if (n_arp_entries_present)
@@ -345,19 +1014,17 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			n_arp_entries_present = 1;
 
 			p->n_arp_entries = atoi(arg_value);
-			if (p->n_arp_entries == 0)
-				return -1;
 
 			continue;
 		}
 
-		/* ip_da_offset */
-		if (strcmp(arg_name, "ip_da_offset") == 0) {
-			if (ip_da_offset_present)
+		/* ip_hdr_offset */
+		if (strcmp(arg_name, "ip_hdr_offset") == 0) {
+			if (ip_hdr_offset_present)
 				return -1;
-			ip_da_offset_present = 1;
+			ip_hdr_offset_present = 1;
 
-			p->ip_da_offset = atoi(arg_value);
+			p->ip_hdr_offset = atoi(arg_value);
 
 			continue;
 		}
@@ -373,15 +1040,67 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* color_offset */
+		if (strcmp(arg_name, "color_offset") == 0) {
+			if (color_offset_present)
+				return -1;
+			color_offset_present = 1;
+
+			p->color_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* debug */
+		if (strcmp(arg_name, "dbg_ah_disable") == 0) {
+			if (dbg_ah_disable_present)
+				return -1;
+			dbg_ah_disable_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->dbg_ah_disable = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->dbg_ah_disable = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
 		/* any other */
 		return -1;
 	}
 
 	/* Check that mandatory arguments are present */
-	if ((n_routes_present == 0) ||
-		(n_arp_entries_present == 0) ||
-		(ip_da_offset_present == 0) ||
-		(n_arp_entries_present && (arp_key_offset_present == 0)))
+	if (ip_hdr_offset_present == 0)
+		return -1;
+
+	/* Check relations between arguments */
+	switch (p->encap) {
+	case PIPELINE_ROUTING_ENCAP_ETHERNET:
+		if (p->qinq_sched ||
+			p->mpls_color_mark ||
+			color_offset_present)
+			return -1;
+		break;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ:
+		if (p->mpls_color_mark ||
+			color_offset_present)
+			return -1;
+		break;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS:
+		if (p->qinq_sched)
+			return -1;
+		break;
+
+	default:
+		return -1;
+	}
+
+	if ((p->n_arp_entries && (arp_key_offset_present == 0)) ||
+		((p->n_arp_entries == 0) && arp_key_offset_present))
 		return -1;
 
 	return 0;
@@ -414,7 +1133,7 @@ pipeline_routing_init(struct pipeline_params *params,
 	PLOG(p, HIGH, "Routing");
 
 	/* Parse arguments */
-	if (pipeline_routing_parse_args(p_rt, params))
+	if (pipeline_routing_parse_args(&p_rt->params, params))
 		return NULL;
 
 	/* Pipeline */
@@ -484,15 +1203,16 @@ pipeline_routing_init(struct pipeline_params *params,
 	p->n_tables = 1;
 	{
 		struct rte_table_lpm_params table_lpm_params = {
-			.n_rules = p_rt->n_routes,
+			.n_rules = p_rt->params.n_routes,
 			.entry_unique_size = sizeof(struct routing_table_entry),
-			.offset = p_rt->ip_da_offset,
+			.offset = p_rt->params.ip_hdr_offset +
+				__builtin_offsetof(struct ipv4_hdr, dst_addr),
 		};
 
 		struct rte_pipeline_table_params table_params = {
 				.ops = &rte_table_lpm_ops,
 				.arg_create = &table_lpm_params,
-				.f_action_hit = routing_table_ah_hit,
+				.f_action_hit = get_routing_table_ah_hit(p_rt),
 				.f_action_miss = NULL,
 				.arg_ah = p_rt,
 				.action_data_size =
@@ -514,20 +1234,20 @@ pipeline_routing_init(struct pipeline_params *params,
 	}
 
 	/* ARP table configuration */
-	if (p_rt->n_arp_entries) {
+	if (p_rt->params.n_arp_entries) {
 		struct rte_table_hash_key8_ext_params table_arp_params = {
-			.n_entries = p_rt->n_arp_entries,
-			.n_entries_ext = p_rt->n_arp_entries,
+			.n_entries = p_rt->params.n_arp_entries,
+			.n_entries_ext = p_rt->params.n_arp_entries,
 			.f_hash = hash_default_key8,
 			.seed = 0,
 			.signature_offset = 0, /* Unused */
-			.key_offset = p_rt->arp_key_offset,
+			.key_offset = p_rt->params.arp_key_offset,
 		};
 
 		struct rte_pipeline_table_params table_params = {
 			.ops = &rte_table_hash_key8_ext_dosig_ops,
 			.arg_create = &table_arp_params,
-			.f_action_hit = arp_table_ah_hit,
+			.f_action_hit = get_arp_table_ah_hit(p_rt),
 			.f_action_miss = NULL,
 			.arg_ah = p_rt,
 			.action_data_size = sizeof(struct arp_table_entry) -
@@ -665,6 +1385,7 @@ pipeline_routing_msg_req_custom_handler(struct pipeline *p,
 void *
 pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 {
+	struct pipeline_routing *p_rt = (struct pipeline_routing *) p;
 	struct pipeline_routing_route_add_msg_req *req = msg;
 	struct pipeline_routing_route_add_msg_rsp *rsp = msg;
 
@@ -673,26 +1394,316 @@ pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 		.depth = req->key.key.ipv4.depth,
 	};
 
-	struct routing_table_entry entry = {
+	struct routing_table_entry entry_arp0 = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->data.port_id]},
+		},
+
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = 0,
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
+	};
+
+	struct routing_table_entry entry_arp1 = {
 		.head = {
 			.action = RTE_PIPELINE_ACTION_TABLE,
 			{.table_id = p->table_id[1]},
 		},
 
-		.flags = req->flags,
-		.port_id = req->port_id,
-		.ip = rte_bswap32(req->ip),
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = rte_bswap32(req->data.ethernet.ip),
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
 	};
 
-	if (req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) {
+	struct rte_pipeline_table_entry *entry = (p_rt->params.n_arp_entries) ?
+		(struct rte_pipeline_table_entry *) &entry_arp1 :
+		(struct rte_pipeline_table_entry *) &entry_arp0;
+
+	if ((req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) ||
+		((p_rt->params.n_arp_entries == 0) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_ARP)) ||
+		(p_rt->params.n_arp_entries &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_ARP) == 0)) ||
+		((p_rt->params.encap != PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)) ||
+		((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ) == 0)) ||
+		((p_rt->params.encap != PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS)) ||
+		((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) == 0))) {
 		rsp->status = -1;
 		return rsp;
 	}
 
+	/* Ether - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] =
+			rte_bswap64((macaddr_src << 16) | ethertype);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[1] + 2;
+		entry_arp0.ether_l2_length = 14;
+	}
+
+	/* Ether - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		entry_arp1.slab[0] = rte_bswap64((macaddr_src << 16) |
+			ethertype);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[0] - 6;
+		entry_arp1.ether_l2_length = 14;
+	}
+
+	/* Ether QinQ - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.slab[2] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset - 3 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[2] + 2;
+		entry_arp0.ether_l2_length = 22;
+	}
+
+	/* Ether QinQ - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		entry_arp1.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp1.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[1] - 6;
+		entry_arp1.ether_l2_length = 22;
+	}
+
+	/* Ether MPLS - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		switch (n_labels) {
+		case 1:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp0.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp0.slab[3] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[3] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 2 * 8);
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[3] + 2;
+		entry_arp0.ether_l2_length = n_labels * 4 + 14;
+	}
+
+	/* Ether MPLS - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		switch (n_labels) {
+		case 1:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp1.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp1.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[2] - 6;
+		entry_arp1.ether_l2_length = n_labels * 4 + 14;
+	}
+
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[0],
 		&key,
-		(struct rte_pipeline_table_entry *) &entry,
+		entry,
 		&rsp->key_found,
 		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
 
@@ -789,6 +1800,7 @@ pipeline_routing_msg_req_arp_add_handler(struct pipeline *p, void *msg)
 	}
 
 	*((struct ether_addr *) &entry.macaddr) = req->macaddr;
+	entry.macaddr = entry.macaddr << 16;
 
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[1],
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.h b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
index 45f37a1..ec767b2 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
@@ -39,6 +39,44 @@
 #include "pipeline_common_be.h"
 
 /*
+ * Pipeline argument parsing
+ */
+#ifndef PIPELINE_ROUTING_N_ROUTES_DEFAULT
+#define PIPELINE_ROUTING_N_ROUTES_DEFAULT                  4096
+#endif
+
+enum pipeline_routing_encap {
+	PIPELINE_ROUTING_ENCAP_ETHERNET = 0,
+	PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ,
+	PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS,
+};
+
+struct pipeline_routing_params {
+	/* routing */
+	uint32_t n_routes;
+
+	/* routing packet encapsulation */
+	enum pipeline_routing_encap encap;
+	uint32_t qinq_sched;
+	uint32_t mpls_color_mark;
+
+	/* arp */
+	uint32_t n_arp_entries;
+
+	/* packet buffer offsets */
+	uint32_t ip_hdr_offset;
+	uint32_t arp_key_offset;
+	uint32_t color_offset;
+
+	/* debug */
+	uint32_t dbg_ah_disable;
+};
+
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
+	struct pipeline_params *params);
+
+/*
  * Route
  */
 enum pipeline_routing_route_key_type {
@@ -59,6 +97,36 @@ struct pipeline_routing_route_key {
 
 enum pipeline_routing_route_flags {
 	PIPELINE_ROUTING_ROUTE_LOCAL = 1 << 0, /* 0 = remote; 1 = local */
+	PIPELINE_ROUTING_ROUTE_ARP = 1 << 1, /* 0 = ARP OFF; 1 = ARP ON */
+	PIPELINE_ROUTING_ROUTE_QINQ = 1 << 2, /* 0 = QINQ OFF; 1 = QINQ ON */
+	PIPELINE_ROUTING_ROUTE_MPLS = 1 << 3, /* 0 = MPLS OFF; 1 = MPLS ON */
+};
+
+#define PIPELINE_ROUTING_MPLS_LABELS_MAX         4
+
+struct pipeline_routing_route_data {
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+
+	union {
+		/* Next hop IP (valid only when ARP is enabled) */
+		uint32_t ip;
+
+		/* Next hop MAC address (valid only when ARP disabled */
+		struct ether_addr macaddr;
+	} ethernet;
+
+	union {
+		struct {
+			uint16_t svlan;
+			uint16_t cvlan;
+		} qinq;
+
+		struct {
+			uint32_t labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+			uint32_t n_labels;
+		} mpls;
+	} l2;
 };
 
 /*
@@ -106,9 +174,7 @@ struct pipeline_routing_route_add_msg_req {
 	struct pipeline_routing_route_key key;
 
 	/* data */
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+	struct pipeline_routing_route_data data;
 };
 
 struct pipeline_routing_route_add_msg_rsp {
-- 
2.1.0

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

* [dpdk-dev] [PATCH v4] ip_pipeline: add more functions to routing-pipeline
  2015-10-19 10:59           ` [dpdk-dev] [PATCH v3] " Jasvinder Singh
@ 2015-10-28 10:35             ` Jasvinder Singh
  2015-12-02 10:22               ` [dpdk-dev] [PATCH v5] " Jasvinder Singh
  0 siblings, 1 reply; 11+ messages in thread
From: Jasvinder Singh @ 2015-10-28 10:35 UTC (permalink / raw)
  To: dev

This patch adds following features to the
routing-pipeline to enable it for various NFV
use-cases;

1.Fast-path ARP table enable/disable
2.Double-tagged VLAN (Q-in-Q) packet enacapsulation
for the next-hop
3.MPLS encapsulation for the next-hop
4.Add colour (Traffic-class for QoS) to the MPLS tag
5.Classification action to select the input queue
of the hierarchical scehdular (QoS)

The above proposed features can be enabled
(or disabled) through the parameters specified
in configuration file as below;

[PIPELINE0]
type = ROUTING
core = 1
pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
n_routes = 4096
n_arp_entries = 1024
ip_hdr_offset = 142
arp_key_offset = 64
l2 = qinq
qinq_sched = no

The LPM table entries might include additional
fields depending upon the packet encapsulation
(Q-in-Q, MPLS)for the next-hop. The CLI
commands for adding or deleting such entries
to LPM table have been implemented. Action
handlers for QinQ and MPLS encapsulation,
classification action to select the input queue
of the hierarchical schedular(QoS) and adding
colour (Traffic-class for QoS) to the MPLS
tag have been implemented.

v2:
*fixed bug in print_route

v3:
*replaced config file "l2 = qinq/mpls" with
"encap = ethernet/ethernet_qinq/ethernet_mpls"
*added config file option "dbg_ah_disable=yes/no"
to disable table action handlers for routing and
arp, which is a quick way to disable packet
encapsulation.

*compacted routing table action handlers

*writing 6 bytes for the macaddr_dst instead of
8 bytes during encapsulation, as the additional
2 bytes are located on previous cache line.

v4:
*fixed bug as RTE_MBUF_METADATA_* macros to
access the packet meta-data covers the packet
mbuf structure.

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>

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

---
 examples/ip_pipeline/pipeline/pipeline_routing.c   |  806 ++++++++++++-
 examples/ip_pipeline/pipeline/pipeline_routing.h   |    8 +-
 .../ip_pipeline/pipeline/pipeline_routing_be.c     | 1262 ++++++++++++++++++--
 .../ip_pipeline/pipeline/pipeline_routing_be.h     |   72 +-
 4 files changed, 1960 insertions(+), 188 deletions(-)

diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline/pipeline_routing.c
index beec982..4f6ff81 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.c
@@ -43,7 +43,7 @@
 
 struct app_pipeline_routing_route {
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params params;
+	struct pipeline_routing_route_data data;
 	void *entry_ptr;
 
 	TAILQ_ENTRY(app_pipeline_routing_route) node;
@@ -187,21 +187,55 @@ print_route(const struct app_pipeline_routing_route *route)
 				&route->key.key.ipv4;
 
 		printf("IP Prefix = %" PRIu32 ".%" PRIu32
-			".%" PRIu32 ".%" PRIu32 "/%" PRIu32 " => "
-			"(Port = %" PRIu32 ", Next Hop IP = "
-			"%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ")\n",
+			".%" PRIu32 ".%" PRIu32 "/%" PRIu32
+			" => (Port = %" PRIu32,
+
 			(key->ip >> 24) & 0xFF,
 			(key->ip >> 16) & 0xFF,
 			(key->ip >> 8) & 0xFF,
 			key->ip & 0xFF,
 
 			key->depth,
-			route->params.port_id,
+			route->data.port_id);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_ARP)
+			printf(
+				", Next Hop IP = %" PRIu32 ".%" PRIu32
+				".%" PRIu32 ".%" PRIu32,
+
+				(route->data.ethernet.ip >> 24) & 0xFF,
+				(route->data.ethernet.ip >> 16) & 0xFF,
+				(route->data.ethernet.ip >> 8) & 0xFF,
+				route->data.ethernet.ip & 0xFF);
+		else
+			printf(
+				", Next Hop HWaddress = %02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32,
+
+				route->data.ethernet.macaddr.addr_bytes[0],
+				route->data.ethernet.macaddr.addr_bytes[1],
+				route->data.ethernet.macaddr.addr_bytes[2],
+				route->data.ethernet.macaddr.addr_bytes[3],
+				route->data.ethernet.macaddr.addr_bytes[4],
+				route->data.ethernet.macaddr.addr_bytes[5]);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)
+			printf(", QinQ SVLAN = %" PRIu32 " CVLAN = %" PRIu32,
+				route->data.l2.qinq.svlan,
+				route->data.l2.qinq.cvlan);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) {
+			uint32_t i;
+
+			printf(", MPLS labels");
+			for (i = 0; i < route->data.l2.mpls.n_labels; i++)
+				printf(" %" PRIu32,
+					route->data.l2.mpls.labels[i]);
+		}
 
-			(route->params.ip >> 24) & 0xFF,
-			(route->params.ip >> 16) & 0xFF,
-			(route->params.ip >> 8) & 0xFF,
-			route->params.ip & 0xFF);
+		printf(")\n");
 	}
 }
 
@@ -209,9 +243,10 @@ static void
 print_arp_entry(const struct app_pipeline_routing_arp_entry *entry)
 {
 	printf("(Port = %" PRIu32 ", IP = %" PRIu32 ".%" PRIu32
-		".%" PRIu32 ".%" PRIu32 ") => "
-		"HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
+		".%" PRIu32 ".%" PRIu32
+		") => HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
 		":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
+
 		entry->key.key.ipv4.port_id,
 		(entry->key.key.ipv4.ip >> 24) & 0xFF,
 		(entry->key.key.ipv4.ip >> 16) & 0xFF,
@@ -253,7 +288,7 @@ int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params)
+	struct pipeline_routing_route_data *data)
 {
 	struct pipeline_routing *p;
 
@@ -267,7 +302,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	/* Check input arguments */
 	if ((app == NULL) ||
 		(key == NULL) ||
-		(route_params == NULL))
+		(data == NULL))
 		return -1;
 
 	p = app_pipeline_data_fe(app, pipeline_id);
@@ -287,8 +322,8 @@ app_pipeline_routing_add_route(struct app_params *app,
 		netmask = (~0) << (32 - depth);
 		key->key.ipv4.ip &= netmask;
 
-		/* route params */
-		if (route_params->port_id >= p->n_ports_out)
+		/* data */
+		if (data->port_id >= p->n_ports_out)
 			return -1;
 	}
 	break;
@@ -318,9 +353,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	req->type = PIPELINE_MSG_REQ_CUSTOM;
 	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD;
 	memcpy(&req->key, key, sizeof(*key));
-	req->flags = route_params->flags;
-	req->port_id = route_params->port_id;
-	req->ip = route_params->ip;
+	memcpy(&req->data, data, sizeof(*data));
 
 	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
 	if (rsp == NULL) {
@@ -341,7 +374,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	}
 
 	memcpy(&entry->key, key, sizeof(*key));
-	memcpy(&entry->params, route_params, sizeof(*route_params));
+	memcpy(&entry->data, data, sizeof(*data));
 	entry->entry_ptr = rsp->entry_ptr;
 
 	/* Commit entry */
@@ -820,31 +853,174 @@ app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
 	return 0;
 }
 
+static int
+parse_labels(char *string, uint32_t *labels, uint32_t *n_labels)
+{
+	uint32_t n_max_labels = *n_labels, count = 0;
+
+	/* Check for void list of labels */
+	if (strcmp(string, "<void>") == 0) {
+		*n_labels = 0;
+		return 0;
+	}
+
+	/* At least one label should be present */
+	for ( ; (*string != '\0'); ) {
+		char *next;
+		int value;
+
+		if (count >= n_max_labels)
+			return -1;
+
+		if (count > 0) {
+			if (string[0] != ':')
+				return -1;
+
+			string++;
+		}
+
+		value = strtol(string, &next, 10);
+		if (next == string)
+			return -1;
+		string = next;
+
+		labels[count++] = (uint32_t) value;
+	}
+
+	*n_labels = count;
+	return 0;
+}
+
+/*
+ * route add (mpls = no, qinq = no, arp = no)
+ */
+
+struct cmd_route_add1_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+};
+
+static void
+cmd_route_add1_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add1_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = 0;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add1_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add1_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add1_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add1_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add1_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add1_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add1_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add1_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add1_result, macaddr);
+
+static cmdline_parse_inst_t cmd_route_add1 = {
+	.f = cmd_route_add1_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add1_p_string,
+		(void *)&cmd_route_add1_p,
+		(void *)&cmd_route_add1_route_string,
+		(void *)&cmd_route_add1_add_string,
+		(void *)&cmd_route_add1_ip,
+		(void *)&cmd_route_add1_depth,
+		(void *)&cmd_route_add1_port_string,
+		(void *)&cmd_route_add1_port,
+		(void *)&cmd_route_add1_ether_string,
+		(void *)&cmd_route_add1_macaddr,
+		NULL,
+	},
+};
+
 /*
- * route add
+ * route add (mpls = no, qinq = no, arp = yes)
  */
 
-struct cmd_route_add_result {
+struct cmd_route_add2_result {
 	cmdline_fixed_string_t p_string;
 	uint32_t p;
 	cmdline_fixed_string_t route_string;
 	cmdline_fixed_string_t add_string;
 	cmdline_ipaddr_t ip;
 	uint32_t depth;
+	cmdline_fixed_string_t port_string;
 	uint32_t port;
+	cmdline_fixed_string_t ether_string;
 	cmdline_ipaddr_t nh_ip;
 };
 
 static void
-cmd_route_add_parsed(
+cmd_route_add2_parsed(
 	void *parsed_result,
 	__rte_unused struct cmdline *cl,
 	void *data)
 {
-	struct cmd_route_add_result *params = parsed_result;
+	struct cmd_route_add2_result *params = parsed_result;
 	struct app_params *app = data;
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params rt_params;
+	struct pipeline_routing_route_data route_data;
 	int status;
 
 	/* Create route */
@@ -852,14 +1028,15 @@ cmd_route_add_parsed(
 	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
 	key.key.ipv4.depth = params->depth;
 
-	rt_params.flags = 0; /* remote route */
-	rt_params.port_id = params->port;
-	rt_params.ip = rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.flags = PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
 
 	status = app_pipeline_routing_add_route(app,
 		params->p,
 		&key,
-		&rt_params);
+		&route_data);
 
 	if (status != 0) {
 		printf("Command failed\n");
@@ -867,46 +1044,558 @@ cmd_route_add_parsed(
 	}
 }
 
-static cmdline_parse_token_string_t cmd_route_add_p_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, p_string,
+static cmdline_parse_token_string_t cmd_route_add2_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, p_string,
 	"p");
 
-static cmdline_parse_token_num_t cmd_route_add_p =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, p, UINT32);
+static cmdline_parse_token_num_t cmd_route_add2_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, p, UINT32);
 
-static cmdline_parse_token_string_t cmd_route_add_route_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, route_string,
+static cmdline_parse_token_string_t cmd_route_add2_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, route_string,
 	"route");
 
-static cmdline_parse_token_string_t cmd_route_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, add_string,
+static cmdline_parse_token_string_t cmd_route_add2_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, add_string,
 	"add");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, ip);
+static cmdline_parse_token_ipaddr_t cmd_route_add2_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add2_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add2_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add2_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, nh_ip);
+
+static cmdline_parse_inst_t cmd_route_add2 = {
+	.f = cmd_route_add2_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add2_p_string,
+		(void *)&cmd_route_add2_p,
+		(void *)&cmd_route_add2_route_string,
+		(void *)&cmd_route_add2_add_string,
+		(void *)&cmd_route_add2_ip,
+		(void *)&cmd_route_add2_depth,
+		(void *)&cmd_route_add2_port_string,
+		(void *)&cmd_route_add2_port,
+		(void *)&cmd_route_add2_ether_string,
+		(void *)&cmd_route_add2_nh_ip,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = no)
+ */
+
+struct cmd_route_add3_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add3_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add3_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add3_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add3_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add3_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add3_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add3_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add3_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add3_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add3_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add3_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add3_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add3_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add3_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add3 = {
+	.f = cmd_route_add3_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add3_p_string,
+		(void *)&cmd_route_add3_p,
+		(void *)&cmd_route_add3_route_string,
+		(void *)&cmd_route_add3_add_string,
+		(void *)&cmd_route_add3_ip,
+		(void *)&cmd_route_add3_depth,
+		(void *)&cmd_route_add3_port_string,
+		(void *)&cmd_route_add3_port,
+		(void *)&cmd_route_add3_ether_string,
+		(void *)&cmd_route_add3_macaddr,
+		(void *)&cmd_route_add3_qinq_string,
+		(void *)&cmd_route_add3_svlan,
+		(void *)&cmd_route_add3_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = yes)
+ */
+
+struct cmd_route_add4_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add4_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add4_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add4_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add4_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add4_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add4_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add4_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, nh_ip);
+
+static cmdline_parse_token_string_t cmd_route_add4_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add4_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add4_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add4 = {
+	.f = cmd_route_add4_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add4_p_string,
+		(void *)&cmd_route_add4_p,
+		(void *)&cmd_route_add4_route_string,
+		(void *)&cmd_route_add4_add_string,
+		(void *)&cmd_route_add4_ip,
+		(void *)&cmd_route_add4_depth,
+		(void *)&cmd_route_add4_port_string,
+		(void *)&cmd_route_add4_port,
+		(void *)&cmd_route_add4_ether_string,
+		(void *)&cmd_route_add4_nh_ip,
+		(void *)&cmd_route_add4_qinq_string,
+		(void *)&cmd_route_add4_svlan,
+		(void *)&cmd_route_add4_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = no)
+ */
+
+struct cmd_route_add5_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add5_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add5_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add5_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add5_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add5_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add5_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add5_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add5_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add5_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add5_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add5_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_string,
+	"mpls");
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_labels,
+	NULL);
+
+static cmdline_parse_inst_t cmd_route_add5 = {
+	.f = cmd_route_add5_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add5_p_string,
+		(void *)&cmd_route_add5_p,
+		(void *)&cmd_route_add5_route_string,
+		(void *)&cmd_route_add5_add_string,
+		(void *)&cmd_route_add5_ip,
+		(void *)&cmd_route_add5_depth,
+		(void *)&cmd_route_add5_port_string,
+		(void *)&cmd_route_add5_port,
+		(void *)&cmd_route_add5_ether_string,
+		(void *)&cmd_route_add5_macaddr,
+		(void *)&cmd_route_add5_mpls_string,
+		(void *)&cmd_route_add5_mpls_labels,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = yes)
+ */
+
+struct cmd_route_add6_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add6_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add6_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add6_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add6_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add6_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add6_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add6_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add6_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, ether_string,
+	"ether");
 
-static cmdline_parse_token_num_t cmd_route_add_depth =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, depth, UINT32);
+static cmdline_parse_token_ipaddr_t cmd_route_add6_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, nh_ip);
 
-static cmdline_parse_token_num_t cmd_route_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, port, UINT32);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_string,
+	"mpls");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_nh_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, nh_ip);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_labels,
+	NULL);
 
-static cmdline_parse_inst_t cmd_route_add = {
-	.f = cmd_route_add_parsed,
+static cmdline_parse_inst_t cmd_route_add6 = {
+	.f = cmd_route_add6_parsed,
 	.data = NULL,
-	.help_str = "Route add",
+	.help_str = "Route add (mpls = yes, arp = yes)",
 	.tokens = {
-		(void *)&cmd_route_add_p_string,
-		(void *)&cmd_route_add_p,
-		(void *)&cmd_route_add_route_string,
-		(void *)&cmd_route_add_add_string,
-		(void *)&cmd_route_add_ip,
-		(void *)&cmd_route_add_depth,
-		(void *)&cmd_route_add_port,
-		(void *)&cmd_route_add_nh_ip,
+		(void *)&cmd_route_add6_p_string,
+		(void *)&cmd_route_add6_p,
+		(void *)&cmd_route_add6_route_string,
+		(void *)&cmd_route_add6_add_string,
+		(void *)&cmd_route_add6_ip,
+		(void *)&cmd_route_add6_depth,
+		(void *)&cmd_route_add6_port_string,
+		(void *)&cmd_route_add6_port,
+		(void *)&cmd_route_add6_ether_string,
+		(void *)&cmd_route_add6_nh_ip,
+		(void *)&cmd_route_add6_mpls_string,
+		(void *)&cmd_route_add6_mpls_labels,
 		NULL,
 	},
 };
@@ -1519,7 +2208,12 @@ static cmdline_parse_inst_t cmd_arp_ls = {
 };
 
 static cmdline_parse_ctx_t pipeline_cmds[] = {
-	(cmdline_parse_inst_t *)&cmd_route_add,
+	(cmdline_parse_inst_t *)&cmd_route_add1,
+	(cmdline_parse_inst_t *)&cmd_route_add2,
+	(cmdline_parse_inst_t *)&cmd_route_add3,
+	(cmdline_parse_inst_t *)&cmd_route_add4,
+	(cmdline_parse_inst_t *)&cmd_route_add5,
+	(cmdline_parse_inst_t *)&cmd_route_add6,
 	(cmdline_parse_inst_t *)&cmd_route_del,
 	(cmdline_parse_inst_t *)&cmd_route_add_default,
 	(cmdline_parse_inst_t *)&cmd_route_del_default,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.h b/examples/ip_pipeline/pipeline/pipeline_routing.h
index e1016f9..fa41642 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.h
@@ -41,17 +41,11 @@
  * Route
  */
 
-struct app_pipeline_routing_route_params {
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* IP address for the next hop (only for remote routes) */
-};
-
 int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params);
+	struct pipeline_routing_route_data *data);
 
 int
 app_pipeline_routing_delete_route(struct app_params *app,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.c b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
index 06d3e65..c506e2b 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
@@ -49,16 +49,31 @@
 #include "pipeline_actions_common.h"
 #include "hash_func.h"
 
+#define MPLS_LABEL(label, exp, s, ttl)					\
+	(((((uint64_t) (label)) & 0xFFFFFLLU) << 12) |		\
+	((((uint64_t) (exp)) & 0x7LLU) << 9) |				\
+	((((uint64_t) (s)) & 0x1LLU) << 8) |				\
+	(((uint64_t) (ttl)) & 0xFFLU))
+
+#define RTE_SCHED_PORT_HIERARCHY(subport, pipe,		\
+	traffic_class, queue, color)					\
+	((((uint32_t) (queue)) & 0x3) |					\
+	((((uint32_t) (traffic_class)) & 0x3) << 2) |		\
+	((((uint32_t) (pipe)) & 0xFFFFF) << 4) |			\
+	((((uint32_t) (subport)) & 0x3F) << 24) |			\
+	((((uint32_t) (color)) & 0x3) << 30))
+
+#define MAC_SRC_DEFAULT 0x112233445566
+
 struct pipeline_routing {
 	struct pipeline p;
+	struct pipeline_routing_params params;
 	pipeline_msg_req_handler custom_handlers[PIPELINE_ROUTING_MSG_REQS];
-
-	uint32_t n_routes;
-	uint32_t n_arp_entries;
-	uint32_t ip_da_offset;
-	uint32_t arp_key_offset;
 } __rte_cache_aligned;
 
+/*
+ * Message handlers
+ */
 static void *
 pipeline_routing_msg_req_custom_handler(struct pipeline *p, void *msg);
 
@@ -138,35 +153,167 @@ struct routing_table_entry {
 	uint32_t flags;
 	uint32_t port_id; /* Output port ID */
 	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+
+	/* ether_l2 */
+	uint16_t data_offset;
+	uint16_t ether_l2_length;
+	uint64_t slab[4];
+	uint16_t slab_offset[4];
 };
 
-static inline void
+struct layout {
+	uint16_t a;
+	uint32_t b;
+	uint16_t c;
+} __attribute__((__packed__));
+
+#define MACADDR_DST_WRITE(slab_ptr, slab)			\
+{								\
+	struct layout *dst = (struct layout *) (slab_ptr);	\
+	struct layout *src = (struct layout *) &(slab);		\
+								\
+	dst->b = src->b;					\
+	dst->c = src->c;					\
+}
+
+static inline __attribute__((always_inline)) void
 pkt_work_routing(
 	struct rte_mbuf *pkt,
 	struct rte_pipeline_table_entry *table_entry,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry =
 		(struct routing_table_entry *) table_entry;
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkt, p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->arp_key_offset);
-	uint32_t ip = RTE_MBUF_METADATA_UINT32(pkt, p_rt->ip_da_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr, *slab1_ptr, *slab2_ptr, *slab3_ptr;
+	uint32_t ip_da, nh_ip, port_id, sched;
+	uint16_t total_length, data_offset, ether_l2_length;
+
+	/* Read */
+	total_length = rte_bswap16(ip->total_length);
+	ip_da = ip->dst_addr;
+	data_offset = entry->data_offset;
+	ether_l2_length = entry->ether_l2_length;
+	slab0_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[0]);
+	slab1_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[1]);
+	slab2_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[2]);
+	slab3_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[3]);
+
+	if (arp) {
+		port_id = entry->port_id;
+		nh_ip = entry->ip;
+		if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip = ip_da;
+	}
+
+	/* Compute */
+	total_length += ether_l2_length;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp = ip->type_of_service >> 2;
+		uint32_t svlan, cvlan, tc, tc_q;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq = rte_bswap64(entry->slab[0]);
+
+			svlan = (slab_qinq >> 48) & 0xFFF;
+			cvlan = (slab_qinq >> 16) & 0xFFF;
+			tc = (dscp >> 2) & 0x3;
+			tc_q = dscp & 0x3;
+		} else {
+			uint32_t ip_src = rte_bswap32(ip->src_addr);
+
+			svlan = 0;
+			cvlan = (ip_src >> 16) & 0xFFF;
+			tc = (ip_src >> 2) & 0x3;
+			tc_q = ip_src & 0x3;
+		}
 
-	arp_key->port_id = entry->port_id;
-	arp_key->ip = entry->ip;
-	if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key->ip = ip;
+		sched = RTE_SCHED_PORT_HIERARCHY(svlan,
+			cvlan,
+			tc,
+			tc_q,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkt->data_off = data_offset;
+	pkt->data_len = total_length;
+	pkt->pkt_len = total_length;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr = entry->slab[0];
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab1_ptr, entry->slab[1]);
+	}
+
+	if (qinq) {
+		*slab0_ptr = entry->slab[0];
+		*slab1_ptr = entry->slab[1];
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab2_ptr, entry->slab[2]);
+
+		if (qinq_sched)
+			pkt->hash.sched = sched;
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp = rte_bswap64(
+				(MPLS_LABEL(0, pkt_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt_color, 0, 0));
+
+			*slab0_ptr = entry->slab[0] | mpls_exp;
+			*slab1_ptr = entry->slab[1] | mpls_exp;
+			*slab2_ptr = entry->slab[2];
+		} else {
+			*slab0_ptr = entry->slab[0];
+			*slab1_ptr = entry->slab[1];
+			*slab2_ptr = entry->slab[2];
+		}
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab3_ptr, entry->slab[3]);
+	}
+
+	if (arp) {
+		arp_key->port_id = port_id;
+		arp_key->ip = nh_ip;
+	}
 }
 
-static inline void
+static inline __attribute__((always_inline)) void
 pkt4_work_routing(
 	struct rte_mbuf **pkts,
 	struct rte_pipeline_table_entry **table_entries,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry0 =
 		(struct routing_table_entry *) table_entries[0];
 	struct routing_table_entry *entry1 =
@@ -175,51 +322,501 @@ pkt4_work_routing(
 		(struct routing_table_entry *) table_entries[2];
 	struct routing_table_entry *entry3 =
 		(struct routing_table_entry *) table_entries[3];
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip0 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip1 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip2 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip3 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt0_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->params.color_offset);
+	enum rte_meter_color pkt1_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->params.color_offset);
+	enum rte_meter_color pkt2_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->params.color_offset);
+	enum rte_meter_color pkt3_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key0 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[0], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key1 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[1], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key2 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[2], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key3 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[3], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr0, *slab1_ptr0, *slab2_ptr0, *slab3_ptr0;
+	uint64_t *slab0_ptr1, *slab1_ptr1, *slab2_ptr1, *slab3_ptr1;
+	uint64_t *slab0_ptr2, *slab1_ptr2, *slab2_ptr2, *slab3_ptr2;
+	uint64_t *slab0_ptr3, *slab1_ptr3, *slab2_ptr3, *slab3_ptr3;
+
+	uint32_t ip_da0, nh_ip0, port_id0, sched0;
+	uint32_t ip_da1, nh_ip1, port_id1, sched1;
+	uint32_t ip_da2, nh_ip2, port_id2, sched2;
+	uint32_t ip_da3, nh_ip3, port_id3, sched3;
+
+	uint16_t total_length0, data_offset0, ether_l2_length0;
+	uint16_t total_length1, data_offset1, ether_l2_length1;
+	uint16_t total_length2, data_offset2, ether_l2_length2;
+	uint16_t total_length3, data_offset3, ether_l2_length3;
+
+	/* Read */
+	total_length0 = rte_bswap16(ip0->total_length);
+	total_length1 = rte_bswap16(ip1->total_length);
+	total_length2 = rte_bswap16(ip2->total_length);
+	total_length3 = rte_bswap16(ip3->total_length);
+
+	ip_da0 = ip0->dst_addr;
+	ip_da1 = ip1->dst_addr;
+	ip_da2 = ip2->dst_addr;
+	ip_da3 = ip3->dst_addr;
+
+	data_offset0 = entry0->data_offset;
+	data_offset1 = entry1->data_offset;
+	data_offset2 = entry2->data_offset;
+	data_offset3 = entry3->data_offset;
+
+	ether_l2_length0 = entry0->ether_l2_length;
+	ether_l2_length1 = entry1->ether_l2_length;
+	ether_l2_length2 = entry2->ether_l2_length;
+	ether_l2_length3 = entry3->ether_l2_length;
+
+	slab0_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[0]);
+	slab1_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[1]);
+	slab2_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[2]);
+	slab3_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[3]);
+
+	slab0_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[0]);
+	slab1_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[1]);
+	slab2_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[2]);
+	slab3_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[3]);
+
+	slab0_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[0]);
+	slab1_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[1]);
+	slab2_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[2]);
+	slab3_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[3]);
+
+	slab0_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[0]);
+	slab1_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[1]);
+	slab2_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[2]);
+	slab3_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[3]);
+
+	if (arp) {
+		port_id0 = entry0->port_id;
+		nh_ip0 = entry0->ip;
+		if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip0 = ip_da0;
+
+		port_id1 = entry1->port_id;
+		nh_ip1 = entry1->ip;
+		if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip1 = ip_da1;
+
+		port_id2 = entry2->port_id;
+		nh_ip2 = entry2->ip;
+		if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip2 = ip_da2;
+
+		port_id3 = entry3->port_id;
+		nh_ip3 = entry3->ip;
+		if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip3 = ip_da3;
+	}
+
+	/* Compute */
+	total_length0 += ether_l2_length0;
+	total_length1 += ether_l2_length1;
+	total_length2 += ether_l2_length2;
+	total_length3 += ether_l2_length3;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp0 = ip0->type_of_service >> 2;
+		uint32_t dscp1 = ip1->type_of_service >> 2;
+		uint32_t dscp2 = ip2->type_of_service >> 2;
+		uint32_t dscp3 = ip3->type_of_service >> 2;
+		uint32_t svlan0, cvlan0, tc0, tc_q0;
+		uint32_t svlan1, cvlan1, tc1, tc_q1;
+		uint32_t svlan2, cvlan2, tc2, tc_q2;
+		uint32_t svlan3, cvlan3, tc3, tc_q3;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq0 = rte_bswap64(entry0->slab[0]);
+			uint64_t slab_qinq1 = rte_bswap64(entry1->slab[0]);
+			uint64_t slab_qinq2 = rte_bswap64(entry2->slab[0]);
+			uint64_t slab_qinq3 = rte_bswap64(entry3->slab[0]);
+
+			svlan0 = (slab_qinq0 >> 48) & 0xFFF;
+			svlan1 = (slab_qinq1 >> 48) & 0xFFF;
+			svlan2 = (slab_qinq2 >> 48) & 0xFFF;
+			svlan3 = (slab_qinq3 >> 48) & 0xFFF;
+
+			cvlan0 = (slab_qinq0 >> 16) & 0xFFF;
+			cvlan1 = (slab_qinq1 >> 16) & 0xFFF;
+			cvlan2 = (slab_qinq2 >> 16) & 0xFFF;
+			cvlan3 = (slab_qinq3 >> 16) & 0xFFF;
+
+			tc0 = (dscp0 >> 2) & 0x3;
+			tc1 = (dscp1 >> 2) & 0x3;
+			tc2 = (dscp2 >> 2) & 0x3;
+			tc3 = (dscp3 >> 2) & 0x3;
+
+			tc_q0 = dscp0 & 0x3;
+			tc_q1 = dscp1 & 0x3;
+			tc_q2 = dscp2 & 0x3;
+			tc_q3 = dscp3 & 0x3;
+		} else {
+			uint32_t ip_src0 = rte_bswap32(ip0->src_addr);
+			uint32_t ip_src1 = rte_bswap32(ip1->src_addr);
+			uint32_t ip_src2 = rte_bswap32(ip2->src_addr);
+			uint32_t ip_src3 = rte_bswap32(ip3->src_addr);
+
+			svlan0 = 0;
+			svlan1 = 0;
+			svlan2 = 0;
+			svlan3 = 0;
+
+			cvlan0 = (ip_src0 >> 16) & 0xFFF;
+			cvlan1 = (ip_src1 >> 16) & 0xFFF;
+			cvlan2 = (ip_src2 >> 16) & 0xFFF;
+			cvlan3 = (ip_src3 >> 16) & 0xFFF;
+
+			tc0 = (ip_src0 >> 2) & 0x3;
+			tc1 = (ip_src1 >> 2) & 0x3;
+			tc2 = (ip_src2 >> 2) & 0x3;
+			tc3 = (ip_src3 >> 2) & 0x3;
+
+			tc_q0 = ip_src0 & 0x3;
+			tc_q1 = ip_src1 & 0x3;
+			tc_q2 = ip_src2 & 0x3;
+			tc_q3 = ip_src3 & 0x3;
+		}
+
+		sched0 = RTE_SCHED_PORT_HIERARCHY(svlan0,
+			cvlan0,
+			tc0,
+			tc_q0,
+			e_RTE_METER_GREEN);
+		sched1 = RTE_SCHED_PORT_HIERARCHY(svlan1,
+			cvlan1,
+			tc1,
+			tc_q1,
+			e_RTE_METER_GREEN);
+		sched2 = RTE_SCHED_PORT_HIERARCHY(svlan2,
+			cvlan2,
+			tc2,
+			tc_q2,
+			e_RTE_METER_GREEN);
+		sched3 = RTE_SCHED_PORT_HIERARCHY(svlan3,
+			cvlan3,
+			tc3,
+			tc_q3,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkts[0]->data_off = data_offset0;
+	pkts[1]->data_off = data_offset1;
+	pkts[2]->data_off = data_offset2;
+	pkts[3]->data_off = data_offset3;
+
+	pkts[0]->data_len = total_length0;
+	pkts[1]->data_len = total_length1;
+	pkts[2]->data_len = total_length2;
+	pkts[3]->data_len = total_length3;
+
+	pkts[0]->pkt_len = total_length0;
+	pkts[1]->pkt_len = total_length1;
+	pkts[2]->pkt_len = total_length2;
+	pkts[3]->pkt_len = total_length3;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab1_ptr0, entry0->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr1, entry1->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr2, entry2->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr3, entry3->slab[1]);
+		}
+	}
+
+	if (qinq) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		*slab1_ptr0 = entry0->slab[1];
+		*slab1_ptr1 = entry1->slab[1];
+		*slab1_ptr2 = entry2->slab[1];
+		*slab1_ptr3 = entry3->slab[1];
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab2_ptr0, entry0->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr1, entry1->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr2, entry2->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr3, entry3->slab[2]);
+		}
+
+		if (qinq_sched) {
+			pkts[0]->hash.sched = sched0;
+			pkts[1]->hash.sched = sched1;
+			pkts[2]->hash.sched = sched2;
+			pkts[3]->hash.sched = sched3;
+		}
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp0 = rte_bswap64(
+				(MPLS_LABEL(0, pkt0_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt0_color, 0, 0));
+			uint64_t mpls_exp1 = rte_bswap64(
+				(MPLS_LABEL(0, pkt1_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt1_color, 0, 0));
+			uint64_t mpls_exp2 = rte_bswap64(
+				(MPLS_LABEL(0, pkt2_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt2_color, 0, 0));
+			uint64_t mpls_exp3 = rte_bswap64(
+				(MPLS_LABEL(0, pkt3_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt3_color, 0, 0));
+
+			*slab0_ptr0 = entry0->slab[0] | mpls_exp0;
+			*slab0_ptr1 = entry1->slab[0] | mpls_exp1;
+			*slab0_ptr2 = entry2->slab[0] | mpls_exp2;
+			*slab0_ptr3 = entry3->slab[0] | mpls_exp3;
+
+			*slab1_ptr0 = entry0->slab[1] | mpls_exp0;
+			*slab1_ptr1 = entry1->slab[1] | mpls_exp1;
+			*slab1_ptr2 = entry2->slab[1] | mpls_exp2;
+			*slab1_ptr3 = entry3->slab[1] | mpls_exp3;
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		} else {
+			*slab0_ptr0 = entry0->slab[0];
+			*slab0_ptr1 = entry1->slab[0];
+			*slab0_ptr2 = entry2->slab[0];
+			*slab0_ptr3 = entry3->slab[0];
+
+			*slab1_ptr0 = entry0->slab[1];
+			*slab1_ptr1 = entry1->slab[1];
+			*slab1_ptr2 = entry2->slab[1];
+			*slab1_ptr3 = entry3->slab[1];
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		}
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab3_ptr0, entry0->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr1, entry1->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr2, entry2->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr3, entry3->slab[3]);
+		}
+	}
+
+	if (arp) {
+		arp_key0->port_id = port_id0;
+		arp_key1->port_id = port_id1;
+		arp_key2->port_id = port_id2;
+		arp_key3->port_id = port_id3;
+
+		arp_key0->ip = nh_ip0;
+		arp_key1->ip = nh_ip1;
+		arp_key2->ip = nh_ip2;
+		arp_key3->ip = nh_ip3;
+	}
+}
 
-	uint32_t ip0 = RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->ip_da_offset);
-	uint32_t ip1 = RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->ip_da_offset);
-	uint32_t ip2 = RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->ip_da_offset);
-	uint32_t ip3 = RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->ip_da_offset);
+#define PKT_WORK_ROUTING_ETHERNET(arp)				\
+static inline void						\
+pkt_work_routing_ether_arp##arp(				\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 0, 0, 0, 0);\
+}
 
-	arp_key0->port_id = entry0->port_id;
-	arp_key1->port_id = entry1->port_id;
-	arp_key2->port_id = entry2->port_id;
-	arp_key3->port_id = entry3->port_id;
+#define PKT4_WORK_ROUTING_ETHERNET(arp)				\
+static inline void						\
+pkt4_work_routing_ether_arp##arp(				\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 0, 0, 0, 0);\
+}
 
-	arp_key0->ip = entry0->ip;
-	if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key0->ip = ip0;
+#define routing_table_ah_hit_ether(arp)				\
+PKT_WORK_ROUTING_ETHERNET(arp)					\
+PKT4_WORK_ROUTING_ETHERNET(arp)					\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_arp##arp,	\
+	pkt_work_routing_ether_arp##arp,			\
+	pkt4_work_routing_ether_arp##arp)
+
+routing_table_ah_hit_ether(0)
+routing_table_ah_hit_ether(1)
+
+#define PKT_WORK_ROUTING_ETHERNET_QINQ(sched, arp)		\
+static inline void						\
+pkt_work_routing_ether_qinq_sched##sched##_arp##arp(		\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 1, sched, 0, 0);\
+}
 
-	arp_key1->ip = entry1->ip;
-	if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key1->ip = ip1;
+#define PKT4_WORK_ROUTING_ETHERNET_QINQ(sched, arp)		\
+static inline void						\
+pkt4_work_routing_ether_qinq_sched##sched##_arp##arp(		\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 1, sched, 0, 0);\
+}
 
-	arp_key2->ip = entry2->ip;
-	if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key2->ip = ip2;
+#define routing_table_ah_hit_ether_qinq(sched, arp)		\
+PKT_WORK_ROUTING_ETHERNET_QINQ(sched, arp)			\
+PKT4_WORK_ROUTING_ETHERNET_QINQ(sched, arp)			\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched##sched##_arp##arp,\
+	pkt_work_routing_ether_qinq_sched##sched##_arp##arp,	\
+	pkt4_work_routing_ether_qinq_sched##sched##_arp##arp)
+
+routing_table_ah_hit_ether_qinq(0, 0)
+routing_table_ah_hit_ether_qinq(1, 0)
+routing_table_ah_hit_ether_qinq(2, 0)
+routing_table_ah_hit_ether_qinq(0, 1)
+routing_table_ah_hit_ether_qinq(1, 1)
+routing_table_ah_hit_ether_qinq(2, 1)
+
+#define PKT_WORK_ROUTING_ETHERNET_MPLS(color, arp)		\
+static inline void						\
+pkt_work_routing_ether_mpls_color##color##_arp##arp(		\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 0, 0, 1, color);\
+}
 
-	arp_key3->ip = entry3->ip;
-	if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key3->ip = ip3;
+#define PKT4_WORK_ROUTING_ETHERNET_MPLS(color, arp)		\
+static inline void						\
+pkt4_work_routing_ether_mpls_color##color##_arp##arp(		\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 0, 0, 1, color);\
 }
 
-PIPELINE_TABLE_AH_HIT(routing_table_ah_hit,
-	pkt_work_routing,
-	pkt4_work_routing);
+#define routing_table_ah_hit_ether_mpls(color, arp)		\
+PKT_WORK_ROUTING_ETHERNET_MPLS(color, arp)			\
+PKT4_WORK_ROUTING_ETHERNET_MPLS(color, arp)			\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color##color##_arp##arp,\
+	pkt_work_routing_ether_mpls_color##color##_arp##arp,	\
+	pkt4_work_routing_ether_mpls_color##color##_arp##arp)
+
+routing_table_ah_hit_ether_mpls(0, 0)
+routing_table_ah_hit_ether_mpls(1, 0)
+routing_table_ah_hit_ether_mpls(0, 1)
+routing_table_ah_hit_ether_mpls(1, 1)
+
+static rte_pipeline_table_action_handler_hit
+get_routing_table_ah_hit(struct pipeline_routing *p)
+{
+	if (p->params.dbg_ah_disable)
+		return NULL;
+
+	switch (p->params.encap) {
+	case PIPELINE_ROUTING_ENCAP_ETHERNET:
+		return (p->params.n_arp_entries) ?
+			routing_table_ah_hit_ether_arp1 :
+			routing_table_ah_hit_ether_arp0;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ:
+		if (p->params.n_arp_entries)
+			switch (p->params.qinq_sched) {
+			case 0:
+				return routing_table_ah_hit_ether_qinq_sched0_arp1;
+			case 1:
+				return routing_table_ah_hit_ether_qinq_sched1_arp1;
+			case 2:
+				return routing_table_ah_hit_ether_qinq_sched2_arp1;
+			default:
+				return NULL;
+			}
+		 else
+			switch (p->params.qinq_sched) {
+			case 0:
+				return routing_table_ah_hit_ether_qinq_sched0_arp0;
+			case 1:
+				return routing_table_ah_hit_ether_qinq_sched1_arp0;
+			case 2:
+				return routing_table_ah_hit_ether_qinq_sched2_arp0;
+			default:
+				return NULL;
+			}
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS:
+		if (p->params.n_arp_entries)
+			if (p->params.mpls_color_mark)
+				return routing_table_ah_hit_ether_mpls_color1_arp1;
+			else
+				return routing_table_ah_hit_ether_mpls_color0_arp1;
+		else
+			if (p->params.mpls_color_mark)
+				return routing_table_ah_hit_ether_mpls_color1_arp0;
+			else
+				return routing_table_ah_hit_ether_mpls_color0_arp0;
+
+	default:
+		return NULL;
+	}
+}
 
 /*
  * ARP table
@@ -229,6 +826,9 @@ struct arp_table_entry {
 	uint64_t macaddr;
 };
 
+/**
+ * ARP table AH
+ */
 static inline void
 pkt_work_arp(
 	struct rte_mbuf *pkt,
@@ -237,20 +837,15 @@ pkt_work_arp(
 {
 	struct arp_table_entry *entry = (struct arp_table_entry *) table_entry;
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw = rte_pktmbuf_mtod(pkt, uint8_t *);
-
-	/* Read: table entry */
-	uint64_t mac_addr_dst = entry->macaddr;
-	uint64_t mac_addr_src = 0;
+	/* Read */
+	uint64_t macaddr_dst = entry->macaddr;
+	uint64_t *slab_ptr = (uint64_t *) ((char *) pkt->buf_addr +
+		(pkt->data_off - 2));
 
-	/* Compute: Ethernet header */
-	uint64_t slab0 = mac_addr_dst | (mac_addr_src << 48);
-	uint32_t slab1 = mac_addr_src >> 16;
+	/* Compute */
 
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw) = slab0;
-	*((uint32_t *) (raw + 8)) = slab1;
+	/* Write */
+	MACADDR_DST_WRITE(slab_ptr, macaddr_dst);
 }
 
 static inline void
@@ -268,59 +863,69 @@ pkt4_work_arp(
 	struct arp_table_entry *entry3 =
 		(struct arp_table_entry *) table_entries[3];
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw0 = rte_pktmbuf_mtod(pkts[0], uint8_t *);
-	uint8_t *raw1 = rte_pktmbuf_mtod(pkts[1], uint8_t *);
-	uint8_t *raw2 = rte_pktmbuf_mtod(pkts[2], uint8_t *);
-	uint8_t *raw3 = rte_pktmbuf_mtod(pkts[3], uint8_t *);
-
-	/* Read: table entry */
-	uint64_t mac_addr_dst0 = entry0->macaddr;
-	uint64_t mac_addr_dst1 = entry1->macaddr;
-	uint64_t mac_addr_dst2 = entry2->macaddr;
-	uint64_t mac_addr_dst3 = entry3->macaddr;
-
-	uint64_t mac_addr_src0 = 0;
-	uint64_t mac_addr_src1 = 0;
-	uint64_t mac_addr_src2 = 0;
-	uint64_t mac_addr_src3 = 0;
-
-	/* Compute: Ethernet header */
-	uint64_t pkt0_slab0 = mac_addr_dst0 | (mac_addr_src0 << 48);
-	uint64_t pkt1_slab0 = mac_addr_dst1 | (mac_addr_src1 << 48);
-	uint64_t pkt2_slab0 = mac_addr_dst2 | (mac_addr_src2 << 48);
-	uint64_t pkt3_slab0 = mac_addr_dst3 | (mac_addr_src3 << 48);
-
-	uint32_t pkt0_slab1 = mac_addr_src0 >> 16;
-	uint32_t pkt1_slab1 = mac_addr_src1 >> 16;
-	uint32_t pkt2_slab1 = mac_addr_src2 >> 16;
-	uint32_t pkt3_slab1 = mac_addr_src3 >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw0) = pkt0_slab0;
-	*((uint32_t *) (raw0 + 8)) = pkt0_slab1;
-	*((uint64_t *) raw1) = pkt1_slab0;
-	*((uint32_t *) (raw1 + 8)) = pkt1_slab1;
-	*((uint64_t *) raw2) = pkt2_slab0;
-	*((uint32_t *) (raw2 + 8)) = pkt2_slab1;
-	*((uint64_t *) raw3) = pkt3_slab0;
-	*((uint32_t *) (raw3 + 8)) = pkt3_slab1;
+	/* Read */
+	uint64_t macaddr_dst0 = entry0->macaddr;
+	uint64_t macaddr_dst1 = entry1->macaddr;
+	uint64_t macaddr_dst2 = entry2->macaddr;
+	uint64_t macaddr_dst3 = entry3->macaddr;
+
+	uint64_t *slab_ptr0 = (uint64_t *) ((char *) pkts[0]->buf_addr +
+		(pkts[0]->data_off - 2));
+	uint64_t *slab_ptr1 = (uint64_t *) ((char *) pkts[1]->buf_addr +
+		(pkts[1]->data_off - 2));
+	uint64_t *slab_ptr2 = (uint64_t *) ((char *) pkts[2]->buf_addr +
+		(pkts[2]->data_off - 2));
+	uint64_t *slab_ptr3 = (uint64_t *) ((char *) pkts[3]->buf_addr +
+		(pkts[3]->data_off - 2));
+
+	/* Compute */
+
+	/* Write */
+	MACADDR_DST_WRITE(slab_ptr0, macaddr_dst0);
+	MACADDR_DST_WRITE(slab_ptr1, macaddr_dst1);
+	MACADDR_DST_WRITE(slab_ptr2, macaddr_dst2);
+	MACADDR_DST_WRITE(slab_ptr3, macaddr_dst3);
 }
 
 PIPELINE_TABLE_AH_HIT(arp_table_ah_hit,
 	pkt_work_arp,
 	pkt4_work_arp);
 
-static int
-pipeline_routing_parse_args(struct pipeline_routing *p,
+static rte_pipeline_table_action_handler_hit
+get_arp_table_ah_hit(struct pipeline_routing *p)
+{
+	if (p->params.dbg_ah_disable)
+		return NULL;
+
+	return arp_table_ah_hit;
+}
+
+/*
+ * Argument parsing
+ */
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
 	struct pipeline_params *params)
 {
 	uint32_t n_routes_present = 0;
+	uint32_t encap_present = 0;
+	uint32_t qinq_sched_present = 0;
+	uint32_t mpls_color_mark_present = 0;
 	uint32_t n_arp_entries_present = 0;
-	uint32_t ip_da_offset_present = 0;
+	uint32_t ip_hdr_offset_present = 0;
 	uint32_t arp_key_offset_present = 0;
+	uint32_t color_offset_present = 0;
+	uint32_t dbg_ah_disable_present = 0;
 	uint32_t i;
 
+	/* default values */
+	p->n_routes = PIPELINE_ROUTING_N_ROUTES_DEFAULT;
+	p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET;
+	p->qinq_sched = 0;
+	p->mpls_color_mark = 0;
+	p->n_arp_entries = 0;
+	p->dbg_ah_disable = 0;
+
 	for (i = 0; i < params->n_args; i++) {
 		char *arg_name = params->args_name[i];
 		char *arg_value = params->args_value[i];
@@ -338,6 +943,70 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* encap */
+		if (strcmp(arg_name, "encap") == 0) {
+			if (encap_present)
+				return -1;
+			encap_present = 1;
+
+			/* ethernet */
+			if (strcmp(arg_value, "ethernet") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET;
+				continue;
+			}
+
+			/* ethernet_qinq */
+			if (strcmp(arg_value, "ethernet_qinq") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ;
+				continue;
+			}
+
+			/* ethernet_mpls */
+			if (strcmp(arg_value, "ethernet_mpls") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS;
+				continue;
+			}
+
+			/* any other */
+			return -1;
+		}
+
+		/* qinq_sched */
+		if (strcmp(arg_name, "qinq_sched") == 0) {
+			if (qinq_sched_present)
+				return -1;
+
+			qinq_sched_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->qinq_sched = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->qinq_sched = 1;
+			else if (strcmp(arg_value, "test") == 0)
+				p->qinq_sched = 2;
+			else
+				return -1;
+
+			continue;
+		}
+
+		/* mpls_color_mark */
+		if (strcmp(arg_name, "mpls_color_mark") == 0) {
+			if (mpls_color_mark_present)
+				return -1;
+
+			mpls_color_mark_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->mpls_color_mark = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->mpls_color_mark = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
 		/* n_arp_entries */
 		if (strcmp(arg_name, "n_arp_entries") == 0) {
 			if (n_arp_entries_present)
@@ -345,19 +1014,17 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			n_arp_entries_present = 1;
 
 			p->n_arp_entries = atoi(arg_value);
-			if (p->n_arp_entries == 0)
-				return -1;
 
 			continue;
 		}
 
-		/* ip_da_offset */
-		if (strcmp(arg_name, "ip_da_offset") == 0) {
-			if (ip_da_offset_present)
+		/* ip_hdr_offset */
+		if (strcmp(arg_name, "ip_hdr_offset") == 0) {
+			if (ip_hdr_offset_present)
 				return -1;
-			ip_da_offset_present = 1;
+			ip_hdr_offset_present = 1;
 
-			p->ip_da_offset = atoi(arg_value);
+			p->ip_hdr_offset = atoi(arg_value);
 
 			continue;
 		}
@@ -373,15 +1040,67 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* color_offset */
+		if (strcmp(arg_name, "color_offset") == 0) {
+			if (color_offset_present)
+				return -1;
+			color_offset_present = 1;
+
+			p->color_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* debug */
+		if (strcmp(arg_name, "dbg_ah_disable") == 0) {
+			if (dbg_ah_disable_present)
+				return -1;
+			dbg_ah_disable_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->dbg_ah_disable = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->dbg_ah_disable = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
 		/* any other */
 		return -1;
 	}
 
 	/* Check that mandatory arguments are present */
-	if ((n_routes_present == 0) ||
-		(n_arp_entries_present == 0) ||
-		(ip_da_offset_present == 0) ||
-		(n_arp_entries_present && (arp_key_offset_present == 0)))
+	if (ip_hdr_offset_present == 0)
+		return -1;
+
+	/* Check relations between arguments */
+	switch (p->encap) {
+	case PIPELINE_ROUTING_ENCAP_ETHERNET:
+		if (p->qinq_sched ||
+			p->mpls_color_mark ||
+			color_offset_present)
+			return -1;
+		break;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ:
+		if (p->mpls_color_mark ||
+			color_offset_present)
+			return -1;
+		break;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS:
+		if (p->qinq_sched)
+			return -1;
+		break;
+
+	default:
+		return -1;
+	}
+
+	if ((p->n_arp_entries && (arp_key_offset_present == 0)) ||
+		((p->n_arp_entries == 0) && arp_key_offset_present))
 		return -1;
 
 	return 0;
@@ -414,7 +1133,7 @@ pipeline_routing_init(struct pipeline_params *params,
 	PLOG(p, HIGH, "Routing");
 
 	/* Parse arguments */
-	if (pipeline_routing_parse_args(p_rt, params))
+	if (pipeline_routing_parse_args(&p_rt->params, params))
 		return NULL;
 
 	/* Pipeline */
@@ -485,15 +1204,16 @@ pipeline_routing_init(struct pipeline_params *params,
 	{
 		struct rte_table_lpm_params table_lpm_params = {
 			.name = p->name,
-			.n_rules = p_rt->n_routes,
+			.n_rules = p_rt->params.n_routes,
 			.entry_unique_size = sizeof(struct routing_table_entry),
-			.offset = p_rt->ip_da_offset,
+			.offset = p_rt->params.ip_hdr_offset +
+				__builtin_offsetof(struct ipv4_hdr, dst_addr),
 		};
 
 		struct rte_pipeline_table_params table_params = {
 				.ops = &rte_table_lpm_ops,
 				.arg_create = &table_lpm_params,
-				.f_action_hit = routing_table_ah_hit,
+				.f_action_hit = get_routing_table_ah_hit(p_rt),
 				.f_action_miss = NULL,
 				.arg_ah = p_rt,
 				.action_data_size =
@@ -515,20 +1235,20 @@ pipeline_routing_init(struct pipeline_params *params,
 	}
 
 	/* ARP table configuration */
-	if (p_rt->n_arp_entries) {
+	if (p_rt->params.n_arp_entries) {
 		struct rte_table_hash_key8_ext_params table_arp_params = {
-			.n_entries = p_rt->n_arp_entries,
-			.n_entries_ext = p_rt->n_arp_entries,
+			.n_entries = p_rt->params.n_arp_entries,
+			.n_entries_ext = p_rt->params.n_arp_entries,
 			.f_hash = hash_default_key8,
 			.seed = 0,
 			.signature_offset = 0, /* Unused */
-			.key_offset = p_rt->arp_key_offset,
+			.key_offset = p_rt->params.arp_key_offset,
 		};
 
 		struct rte_pipeline_table_params table_params = {
 			.ops = &rte_table_hash_key8_ext_dosig_ops,
 			.arg_create = &table_arp_params,
-			.f_action_hit = arp_table_ah_hit,
+			.f_action_hit = get_arp_table_ah_hit(p_rt),
 			.f_action_miss = NULL,
 			.arg_ah = p_rt,
 			.action_data_size = sizeof(struct arp_table_entry) -
@@ -666,6 +1386,7 @@ pipeline_routing_msg_req_custom_handler(struct pipeline *p,
 void *
 pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 {
+	struct pipeline_routing *p_rt = (struct pipeline_routing *) p;
 	struct pipeline_routing_route_add_msg_req *req = msg;
 	struct pipeline_routing_route_add_msg_rsp *rsp = msg;
 
@@ -674,26 +1395,322 @@ pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 		.depth = req->key.key.ipv4.depth,
 	};
 
-	struct routing_table_entry entry = {
+	struct routing_table_entry entry_arp0 = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->data.port_id]},
+		},
+
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = 0,
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
+	};
+
+	struct routing_table_entry entry_arp1 = {
 		.head = {
 			.action = RTE_PIPELINE_ACTION_TABLE,
 			{.table_id = p->table_id[1]},
 		},
 
-		.flags = req->flags,
-		.port_id = req->port_id,
-		.ip = rte_bswap32(req->ip),
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = rte_bswap32(req->data.ethernet.ip),
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
 	};
 
-	if (req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) {
+	struct rte_pipeline_table_entry *entry = (p_rt->params.n_arp_entries) ?
+		(struct rte_pipeline_table_entry *) &entry_arp1 :
+		(struct rte_pipeline_table_entry *) &entry_arp0;
+
+	if ((req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) ||
+		((p_rt->params.n_arp_entries == 0) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_ARP)) ||
+		(p_rt->params.n_arp_entries &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_ARP) == 0)) ||
+		((p_rt->params.encap != PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)) ||
+		((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ) == 0)) ||
+		((p_rt->params.encap != PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS)) ||
+		((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) == 0))) {
 		rsp->status = -1;
 		return rsp;
 	}
 
+	/* Ether - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] =
+			rte_bswap64((macaddr_src << 16) | ethertype);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[1] + 2
+			- sizeof(struct rte_mbuf);
+		entry_arp0.ether_l2_length = 14;
+	}
+
+	/* Ether - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		entry_arp1.slab[0] = rte_bswap64((macaddr_src << 16) |
+			ethertype);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[0] - 6
+			- sizeof(struct rte_mbuf);
+		entry_arp1.ether_l2_length = 14;
+	}
+
+	/* Ether QinQ - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.slab[2] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset - 3 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[2] + 2
+			- sizeof(struct rte_mbuf);
+		entry_arp0.ether_l2_length = 22;
+	}
+
+	/* Ether QinQ - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		entry_arp1.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp1.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[1] - 6
+			- sizeof(struct rte_mbuf);
+		entry_arp1.ether_l2_length = 22;
+	}
+
+	/* Ether MPLS - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		switch (n_labels) {
+		case 1:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp0.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp0.slab[3] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[3] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 2 * 8);
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[3] + 2
+			- sizeof(struct rte_mbuf);
+		entry_arp0.ether_l2_length = n_labels * 4 + 14;
+	}
+
+	/* Ether MPLS - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		switch (n_labels) {
+		case 1:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp1.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp1.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[2] - 6
+			- sizeof(struct rte_mbuf);
+		entry_arp1.ether_l2_length = n_labels * 4 + 14;
+	}
+
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[0],
 		&key,
-		(struct rte_pipeline_table_entry *) &entry,
+		entry,
 		&rsp->key_found,
 		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
 
@@ -790,6 +1807,7 @@ pipeline_routing_msg_req_arp_add_handler(struct pipeline *p, void *msg)
 	}
 
 	*((struct ether_addr *) &entry.macaddr) = req->macaddr;
+	entry.macaddr = entry.macaddr << 16;
 
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[1],
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.h b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
index 45f37a1..ec767b2 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
@@ -39,6 +39,44 @@
 #include "pipeline_common_be.h"
 
 /*
+ * Pipeline argument parsing
+ */
+#ifndef PIPELINE_ROUTING_N_ROUTES_DEFAULT
+#define PIPELINE_ROUTING_N_ROUTES_DEFAULT                  4096
+#endif
+
+enum pipeline_routing_encap {
+	PIPELINE_ROUTING_ENCAP_ETHERNET = 0,
+	PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ,
+	PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS,
+};
+
+struct pipeline_routing_params {
+	/* routing */
+	uint32_t n_routes;
+
+	/* routing packet encapsulation */
+	enum pipeline_routing_encap encap;
+	uint32_t qinq_sched;
+	uint32_t mpls_color_mark;
+
+	/* arp */
+	uint32_t n_arp_entries;
+
+	/* packet buffer offsets */
+	uint32_t ip_hdr_offset;
+	uint32_t arp_key_offset;
+	uint32_t color_offset;
+
+	/* debug */
+	uint32_t dbg_ah_disable;
+};
+
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
+	struct pipeline_params *params);
+
+/*
  * Route
  */
 enum pipeline_routing_route_key_type {
@@ -59,6 +97,36 @@ struct pipeline_routing_route_key {
 
 enum pipeline_routing_route_flags {
 	PIPELINE_ROUTING_ROUTE_LOCAL = 1 << 0, /* 0 = remote; 1 = local */
+	PIPELINE_ROUTING_ROUTE_ARP = 1 << 1, /* 0 = ARP OFF; 1 = ARP ON */
+	PIPELINE_ROUTING_ROUTE_QINQ = 1 << 2, /* 0 = QINQ OFF; 1 = QINQ ON */
+	PIPELINE_ROUTING_ROUTE_MPLS = 1 << 3, /* 0 = MPLS OFF; 1 = MPLS ON */
+};
+
+#define PIPELINE_ROUTING_MPLS_LABELS_MAX         4
+
+struct pipeline_routing_route_data {
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+
+	union {
+		/* Next hop IP (valid only when ARP is enabled) */
+		uint32_t ip;
+
+		/* Next hop MAC address (valid only when ARP disabled */
+		struct ether_addr macaddr;
+	} ethernet;
+
+	union {
+		struct {
+			uint16_t svlan;
+			uint16_t cvlan;
+		} qinq;
+
+		struct {
+			uint32_t labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+			uint32_t n_labels;
+		} mpls;
+	} l2;
 };
 
 /*
@@ -106,9 +174,7 @@ struct pipeline_routing_route_add_msg_req {
 	struct pipeline_routing_route_key key;
 
 	/* data */
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+	struct pipeline_routing_route_data data;
 };
 
 struct pipeline_routing_route_add_msg_rsp {
-- 
2.1.0

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

* [dpdk-dev] [PATCH v5] ip_pipeline: add more functions to routing-pipeline
  2015-10-28 10:35             ` [dpdk-dev] [PATCH v4] " Jasvinder Singh
@ 2015-12-02 10:22               ` Jasvinder Singh
  2015-12-07  0:55                 ` Thomas Monjalon
  0 siblings, 1 reply; 11+ messages in thread
From: Jasvinder Singh @ 2015-12-02 10:22 UTC (permalink / raw)
  To: dev

This patch adds following features to the
routing-pipeline to enable it for various NFV
use-cases;

1.Fast-path ARP table enable/disable
2.Double-tagged VLAN (Q-in-Q) packet enacapsulation
for the next-hop
3.MPLS encapsulation for the next-hop
4.Add colour (Traffic-class for QoS) to the MPLS tag
5.Classification action to select the input queue
of the hierarchical schedular (QoS)

The above proposed features can be enabled
(or disabled) through the parameters specified
in configuration file as below;

[PIPELINE0]
type = ROUTING
core = 1
pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
n_routes = 4096
n_arp_entries = 1024
ip_hdr_offset = 142
arp_key_offset = 64
l2 = qinq
qinq_sched = no

The LPM table entries might include additional
fields depending upon the packet encapsulation
(Q-in-Q, MPLS)for the next-hop. The CLI
commands for adding or deleting such entries
to LPM table have been implemented. Action
handlers for QinQ and MPLS encapsulation,
classification action to select the input queue
of the hierarchical schedular(QoS) and adding
colour (Traffic-class for QoS) to the MPLS
tag have been implemented.

Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
v2:
*fixed bug in print_route

v3:
*replaced config file "l2 = qinq/mpls" with
"encap = ethernet/ethernet_qinq/ethernet_mpls"

*added config file option "dbg_ah_disable=yes/no"
to disable table action handlers for routing and
arp, which is a quick way to disable packet
encapsulation.

*compacted routing table action handlers

*writing 6 bytes for the macaddr_dst instead of
8 bytes during encapsulation, as the additional

v4:
*fixed bug as RTE_MBUF_METADATA_* macros to
access the packet meta-data covers the packet
mbuf structure.

v5:
*rebase to latest master

 examples/ip_pipeline/pipeline/pipeline_routing.c   |  806 ++++++++++++-
 examples/ip_pipeline/pipeline/pipeline_routing.h   |    8 +-
 .../ip_pipeline/pipeline/pipeline_routing_be.c     | 1270 ++++++++++++++++++--
 .../ip_pipeline/pipeline/pipeline_routing_be.h     |   72 +-
 4 files changed, 1968 insertions(+), 188 deletions(-)

diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.c b/examples/ip_pipeline/pipeline/pipeline_routing.c
index beec982..4f6ff81 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.c
@@ -43,7 +43,7 @@
 
 struct app_pipeline_routing_route {
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params params;
+	struct pipeline_routing_route_data data;
 	void *entry_ptr;
 
 	TAILQ_ENTRY(app_pipeline_routing_route) node;
@@ -187,21 +187,55 @@ print_route(const struct app_pipeline_routing_route *route)
 				&route->key.key.ipv4;
 
 		printf("IP Prefix = %" PRIu32 ".%" PRIu32
-			".%" PRIu32 ".%" PRIu32 "/%" PRIu32 " => "
-			"(Port = %" PRIu32 ", Next Hop IP = "
-			"%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ")\n",
+			".%" PRIu32 ".%" PRIu32 "/%" PRIu32
+			" => (Port = %" PRIu32,
+
 			(key->ip >> 24) & 0xFF,
 			(key->ip >> 16) & 0xFF,
 			(key->ip >> 8) & 0xFF,
 			key->ip & 0xFF,
 
 			key->depth,
-			route->params.port_id,
+			route->data.port_id);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_ARP)
+			printf(
+				", Next Hop IP = %" PRIu32 ".%" PRIu32
+				".%" PRIu32 ".%" PRIu32,
+
+				(route->data.ethernet.ip >> 24) & 0xFF,
+				(route->data.ethernet.ip >> 16) & 0xFF,
+				(route->data.ethernet.ip >> 8) & 0xFF,
+				route->data.ethernet.ip & 0xFF);
+		else
+			printf(
+				", Next Hop HWaddress = %02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32 ":%02" PRIx32
+				":%02" PRIx32,
+
+				route->data.ethernet.macaddr.addr_bytes[0],
+				route->data.ethernet.macaddr.addr_bytes[1],
+				route->data.ethernet.macaddr.addr_bytes[2],
+				route->data.ethernet.macaddr.addr_bytes[3],
+				route->data.ethernet.macaddr.addr_bytes[4],
+				route->data.ethernet.macaddr.addr_bytes[5]);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)
+			printf(", QinQ SVLAN = %" PRIu32 " CVLAN = %" PRIu32,
+				route->data.l2.qinq.svlan,
+				route->data.l2.qinq.cvlan);
+
+		if (route->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) {
+			uint32_t i;
+
+			printf(", MPLS labels");
+			for (i = 0; i < route->data.l2.mpls.n_labels; i++)
+				printf(" %" PRIu32,
+					route->data.l2.mpls.labels[i]);
+		}
 
-			(route->params.ip >> 24) & 0xFF,
-			(route->params.ip >> 16) & 0xFF,
-			(route->params.ip >> 8) & 0xFF,
-			route->params.ip & 0xFF);
+		printf(")\n");
 	}
 }
 
@@ -209,9 +243,10 @@ static void
 print_arp_entry(const struct app_pipeline_routing_arp_entry *entry)
 {
 	printf("(Port = %" PRIu32 ", IP = %" PRIu32 ".%" PRIu32
-		".%" PRIu32 ".%" PRIu32 ") => "
-		"HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
+		".%" PRIu32 ".%" PRIu32
+		") => HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
 		":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
+
 		entry->key.key.ipv4.port_id,
 		(entry->key.key.ipv4.ip >> 24) & 0xFF,
 		(entry->key.key.ipv4.ip >> 16) & 0xFF,
@@ -253,7 +288,7 @@ int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params)
+	struct pipeline_routing_route_data *data)
 {
 	struct pipeline_routing *p;
 
@@ -267,7 +302,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	/* Check input arguments */
 	if ((app == NULL) ||
 		(key == NULL) ||
-		(route_params == NULL))
+		(data == NULL))
 		return -1;
 
 	p = app_pipeline_data_fe(app, pipeline_id);
@@ -287,8 +322,8 @@ app_pipeline_routing_add_route(struct app_params *app,
 		netmask = (~0) << (32 - depth);
 		key->key.ipv4.ip &= netmask;
 
-		/* route params */
-		if (route_params->port_id >= p->n_ports_out)
+		/* data */
+		if (data->port_id >= p->n_ports_out)
 			return -1;
 	}
 	break;
@@ -318,9 +353,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	req->type = PIPELINE_MSG_REQ_CUSTOM;
 	req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD;
 	memcpy(&req->key, key, sizeof(*key));
-	req->flags = route_params->flags;
-	req->port_id = route_params->port_id;
-	req->ip = route_params->ip;
+	memcpy(&req->data, data, sizeof(*data));
 
 	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
 	if (rsp == NULL) {
@@ -341,7 +374,7 @@ app_pipeline_routing_add_route(struct app_params *app,
 	}
 
 	memcpy(&entry->key, key, sizeof(*key));
-	memcpy(&entry->params, route_params, sizeof(*route_params));
+	memcpy(&entry->data, data, sizeof(*data));
 	entry->entry_ptr = rsp->entry_ptr;
 
 	/* Commit entry */
@@ -820,31 +853,174 @@ app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
 	return 0;
 }
 
+static int
+parse_labels(char *string, uint32_t *labels, uint32_t *n_labels)
+{
+	uint32_t n_max_labels = *n_labels, count = 0;
+
+	/* Check for void list of labels */
+	if (strcmp(string, "<void>") == 0) {
+		*n_labels = 0;
+		return 0;
+	}
+
+	/* At least one label should be present */
+	for ( ; (*string != '\0'); ) {
+		char *next;
+		int value;
+
+		if (count >= n_max_labels)
+			return -1;
+
+		if (count > 0) {
+			if (string[0] != ':')
+				return -1;
+
+			string++;
+		}
+
+		value = strtol(string, &next, 10);
+		if (next == string)
+			return -1;
+		string = next;
+
+		labels[count++] = (uint32_t) value;
+	}
+
+	*n_labels = count;
+	return 0;
+}
+
+/*
+ * route add (mpls = no, qinq = no, arp = no)
+ */
+
+struct cmd_route_add1_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+};
+
+static void
+cmd_route_add1_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add1_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = 0;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add1_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add1_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add1_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add1_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add1_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add1_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add1_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add1_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add1_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add1_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add1_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add1_result, macaddr);
+
+static cmdline_parse_inst_t cmd_route_add1 = {
+	.f = cmd_route_add1_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add1_p_string,
+		(void *)&cmd_route_add1_p,
+		(void *)&cmd_route_add1_route_string,
+		(void *)&cmd_route_add1_add_string,
+		(void *)&cmd_route_add1_ip,
+		(void *)&cmd_route_add1_depth,
+		(void *)&cmd_route_add1_port_string,
+		(void *)&cmd_route_add1_port,
+		(void *)&cmd_route_add1_ether_string,
+		(void *)&cmd_route_add1_macaddr,
+		NULL,
+	},
+};
+
 /*
- * route add
+ * route add (mpls = no, qinq = no, arp = yes)
  */
 
-struct cmd_route_add_result {
+struct cmd_route_add2_result {
 	cmdline_fixed_string_t p_string;
 	uint32_t p;
 	cmdline_fixed_string_t route_string;
 	cmdline_fixed_string_t add_string;
 	cmdline_ipaddr_t ip;
 	uint32_t depth;
+	cmdline_fixed_string_t port_string;
 	uint32_t port;
+	cmdline_fixed_string_t ether_string;
 	cmdline_ipaddr_t nh_ip;
 };
 
 static void
-cmd_route_add_parsed(
+cmd_route_add2_parsed(
 	void *parsed_result,
 	__rte_unused struct cmdline *cl,
 	void *data)
 {
-	struct cmd_route_add_result *params = parsed_result;
+	struct cmd_route_add2_result *params = parsed_result;
 	struct app_params *app = data;
 	struct pipeline_routing_route_key key;
-	struct app_pipeline_routing_route_params rt_params;
+	struct pipeline_routing_route_data route_data;
 	int status;
 
 	/* Create route */
@@ -852,14 +1028,15 @@ cmd_route_add_parsed(
 	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
 	key.key.ipv4.depth = params->depth;
 
-	rt_params.flags = 0; /* remote route */
-	rt_params.port_id = params->port;
-	rt_params.ip = rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.flags = PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
 
 	status = app_pipeline_routing_add_route(app,
 		params->p,
 		&key,
-		&rt_params);
+		&route_data);
 
 	if (status != 0) {
 		printf("Command failed\n");
@@ -867,46 +1044,558 @@ cmd_route_add_parsed(
 	}
 }
 
-static cmdline_parse_token_string_t cmd_route_add_p_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, p_string,
+static cmdline_parse_token_string_t cmd_route_add2_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, p_string,
 	"p");
 
-static cmdline_parse_token_num_t cmd_route_add_p =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, p, UINT32);
+static cmdline_parse_token_num_t cmd_route_add2_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, p, UINT32);
 
-static cmdline_parse_token_string_t cmd_route_add_route_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, route_string,
+static cmdline_parse_token_string_t cmd_route_add2_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, route_string,
 	"route");
 
-static cmdline_parse_token_string_t cmd_route_add_add_string =
-	TOKEN_STRING_INITIALIZER(struct cmd_route_add_result, add_string,
+static cmdline_parse_token_string_t cmd_route_add2_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, add_string,
 	"add");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, ip);
+static cmdline_parse_token_ipaddr_t cmd_route_add2_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add2_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add2_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add2_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add2_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add2_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add2_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add2_result, nh_ip);
+
+static cmdline_parse_inst_t cmd_route_add2 = {
+	.f = cmd_route_add2_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = no, qinq = no, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add2_p_string,
+		(void *)&cmd_route_add2_p,
+		(void *)&cmd_route_add2_route_string,
+		(void *)&cmd_route_add2_add_string,
+		(void *)&cmd_route_add2_ip,
+		(void *)&cmd_route_add2_depth,
+		(void *)&cmd_route_add2_port_string,
+		(void *)&cmd_route_add2_port,
+		(void *)&cmd_route_add2_ether_string,
+		(void *)&cmd_route_add2_nh_ip,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = no)
+ */
+
+struct cmd_route_add3_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add3_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add3_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add3_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add3_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add3_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add3_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add3_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add3_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add3_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add3_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add3_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add3_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add3_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add3_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add3_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add3_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add3_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add3 = {
+	.f = cmd_route_add3_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add3_p_string,
+		(void *)&cmd_route_add3_p,
+		(void *)&cmd_route_add3_route_string,
+		(void *)&cmd_route_add3_add_string,
+		(void *)&cmd_route_add3_ip,
+		(void *)&cmd_route_add3_depth,
+		(void *)&cmd_route_add3_port_string,
+		(void *)&cmd_route_add3_port,
+		(void *)&cmd_route_add3_ether_string,
+		(void *)&cmd_route_add3_macaddr,
+		(void *)&cmd_route_add3_qinq_string,
+		(void *)&cmd_route_add3_svlan,
+		(void *)&cmd_route_add3_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = no, qinq = yes, arp = yes)
+ */
+
+struct cmd_route_add4_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t qinq_string;
+	uint32_t svlan;
+	uint32_t cvlan;
+};
+
+static void
+cmd_route_add4_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add4_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	int status;
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_QINQ |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	route_data.l2.qinq.svlan = params->svlan;
+	route_data.l2.qinq.cvlan = params->cvlan;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add4_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add4_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add4_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add4_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add4_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add4_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add4_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add4_result, nh_ip);
+
+static cmdline_parse_token_string_t cmd_route_add4_qinq_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add4_result, qinq_string,
+	"qinq");
+
+static cmdline_parse_token_num_t cmd_route_add4_svlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, svlan, UINT32);
+
+static cmdline_parse_token_num_t cmd_route_add4_cvlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add4_result, cvlan, UINT32);
+
+static cmdline_parse_inst_t cmd_route_add4 = {
+	.f = cmd_route_add4_parsed,
+	.data = NULL,
+	.help_str = "Route add (qinq = yes, arp = yes)",
+	.tokens = {
+		(void *)&cmd_route_add4_p_string,
+		(void *)&cmd_route_add4_p,
+		(void *)&cmd_route_add4_route_string,
+		(void *)&cmd_route_add4_add_string,
+		(void *)&cmd_route_add4_ip,
+		(void *)&cmd_route_add4_depth,
+		(void *)&cmd_route_add4_port_string,
+		(void *)&cmd_route_add4_port,
+		(void *)&cmd_route_add4_ether_string,
+		(void *)&cmd_route_add4_nh_ip,
+		(void *)&cmd_route_add4_qinq_string,
+		(void *)&cmd_route_add4_svlan,
+		(void *)&cmd_route_add4_cvlan,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = no)
+ */
+
+struct cmd_route_add5_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	struct ether_addr macaddr;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add5_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add5_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS;
+	route_data.port_id = params->port;
+	route_data.ethernet.macaddr = params->macaddr;
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add5_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add5_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add5_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add5_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add5_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add5_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add5_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add5_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add5_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, ether_string,
+	"ether");
+
+static cmdline_parse_token_etheraddr_t cmd_route_add5_macaddr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_route_add5_result, macaddr);
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_string,
+	"mpls");
+
+static cmdline_parse_token_string_t cmd_route_add5_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add5_result, mpls_labels,
+	NULL);
+
+static cmdline_parse_inst_t cmd_route_add5 = {
+	.f = cmd_route_add5_parsed,
+	.data = NULL,
+	.help_str = "Route add (mpls = yes, arp = no)",
+	.tokens = {
+		(void *)&cmd_route_add5_p_string,
+		(void *)&cmd_route_add5_p,
+		(void *)&cmd_route_add5_route_string,
+		(void *)&cmd_route_add5_add_string,
+		(void *)&cmd_route_add5_ip,
+		(void *)&cmd_route_add5_depth,
+		(void *)&cmd_route_add5_port_string,
+		(void *)&cmd_route_add5_port,
+		(void *)&cmd_route_add5_ether_string,
+		(void *)&cmd_route_add5_macaddr,
+		(void *)&cmd_route_add5_mpls_string,
+		(void *)&cmd_route_add5_mpls_labels,
+		NULL,
+	},
+};
+
+/*
+ * route add (mpls = yes, qinq = no, arp = yes)
+ */
+
+struct cmd_route_add6_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t p;
+	cmdline_fixed_string_t route_string;
+	cmdline_fixed_string_t add_string;
+	cmdline_ipaddr_t ip;
+	uint32_t depth;
+	cmdline_fixed_string_t port_string;
+	uint32_t port;
+	cmdline_fixed_string_t ether_string;
+	cmdline_ipaddr_t nh_ip;
+	cmdline_fixed_string_t mpls_string;
+	cmdline_fixed_string_t mpls_labels;
+};
+
+static void
+cmd_route_add6_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_route_add6_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_routing_route_key key;
+	struct pipeline_routing_route_data route_data;
+	uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+	uint32_t n_labels = RTE_DIM(mpls_labels);
+	uint32_t i;
+	int status;
+
+	/* Parse MPLS labels */
+	status = parse_labels(params->mpls_labels, mpls_labels, &n_labels);
+	if (status) {
+		printf("MPLS labels parse error\n");
+		return;
+	}
+
+	/* Create route */
+	key.type = PIPELINE_ROUTING_ROUTE_IPV4;
+	key.key.ipv4.ip = rte_bswap32((uint32_t) params->ip.addr.ipv4.s_addr);
+	key.key.ipv4.depth = params->depth;
+
+	route_data.flags = PIPELINE_ROUTING_ROUTE_MPLS |
+		PIPELINE_ROUTING_ROUTE_ARP;
+	route_data.port_id = params->port;
+	route_data.ethernet.ip =
+		rte_bswap32((uint32_t) params->nh_ip.addr.ipv4.s_addr);
+	for (i = 0; i < n_labels; i++)
+		route_data.l2.mpls.labels[i] = mpls_labels[i];
+	route_data.l2.mpls.n_labels = n_labels;
+
+	status = app_pipeline_routing_add_route(app,
+		params->p,
+		&key,
+		&route_data);
+
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+}
+
+static cmdline_parse_token_string_t cmd_route_add6_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, p_string,
+	"p");
+
+static cmdline_parse_token_num_t cmd_route_add6_p =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, p, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_route_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, route_string,
+	"route");
+
+static cmdline_parse_token_string_t cmd_route_add6_add_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, add_string,
+	"add");
+
+static cmdline_parse_token_ipaddr_t cmd_route_add6_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, ip);
+
+static cmdline_parse_token_num_t cmd_route_add6_depth =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, depth, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, port_string,
+	"port");
+
+static cmdline_parse_token_num_t cmd_route_add6_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_route_add6_result, port, UINT32);
+
+static cmdline_parse_token_string_t cmd_route_add6_ether_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, ether_string,
+	"ether");
 
-static cmdline_parse_token_num_t cmd_route_add_depth =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, depth, UINT32);
+static cmdline_parse_token_ipaddr_t cmd_route_add6_nh_ip =
+	TOKEN_IPV4_INITIALIZER(struct cmd_route_add6_result, nh_ip);
 
-static cmdline_parse_token_num_t cmd_route_add_port =
-	TOKEN_NUM_INITIALIZER(struct cmd_route_add_result, port, UINT32);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_string,
+	"mpls");
 
-static cmdline_parse_token_ipaddr_t cmd_route_add_nh_ip =
-	TOKEN_IPV4_INITIALIZER(struct cmd_route_add_result, nh_ip);
+static cmdline_parse_token_string_t cmd_route_add6_mpls_labels =
+	TOKEN_STRING_INITIALIZER(struct cmd_route_add6_result, mpls_labels,
+	NULL);
 
-static cmdline_parse_inst_t cmd_route_add = {
-	.f = cmd_route_add_parsed,
+static cmdline_parse_inst_t cmd_route_add6 = {
+	.f = cmd_route_add6_parsed,
 	.data = NULL,
-	.help_str = "Route add",
+	.help_str = "Route add (mpls = yes, arp = yes)",
 	.tokens = {
-		(void *)&cmd_route_add_p_string,
-		(void *)&cmd_route_add_p,
-		(void *)&cmd_route_add_route_string,
-		(void *)&cmd_route_add_add_string,
-		(void *)&cmd_route_add_ip,
-		(void *)&cmd_route_add_depth,
-		(void *)&cmd_route_add_port,
-		(void *)&cmd_route_add_nh_ip,
+		(void *)&cmd_route_add6_p_string,
+		(void *)&cmd_route_add6_p,
+		(void *)&cmd_route_add6_route_string,
+		(void *)&cmd_route_add6_add_string,
+		(void *)&cmd_route_add6_ip,
+		(void *)&cmd_route_add6_depth,
+		(void *)&cmd_route_add6_port_string,
+		(void *)&cmd_route_add6_port,
+		(void *)&cmd_route_add6_ether_string,
+		(void *)&cmd_route_add6_nh_ip,
+		(void *)&cmd_route_add6_mpls_string,
+		(void *)&cmd_route_add6_mpls_labels,
 		NULL,
 	},
 };
@@ -1519,7 +2208,12 @@ static cmdline_parse_inst_t cmd_arp_ls = {
 };
 
 static cmdline_parse_ctx_t pipeline_cmds[] = {
-	(cmdline_parse_inst_t *)&cmd_route_add,
+	(cmdline_parse_inst_t *)&cmd_route_add1,
+	(cmdline_parse_inst_t *)&cmd_route_add2,
+	(cmdline_parse_inst_t *)&cmd_route_add3,
+	(cmdline_parse_inst_t *)&cmd_route_add4,
+	(cmdline_parse_inst_t *)&cmd_route_add5,
+	(cmdline_parse_inst_t *)&cmd_route_add6,
 	(cmdline_parse_inst_t *)&cmd_route_del,
 	(cmdline_parse_inst_t *)&cmd_route_add_default,
 	(cmdline_parse_inst_t *)&cmd_route_del_default,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing.h b/examples/ip_pipeline/pipeline/pipeline_routing.h
index e1016f9..fa41642 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing.h
@@ -41,17 +41,11 @@
  * Route
  */
 
-struct app_pipeline_routing_route_params {
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* IP address for the next hop (only for remote routes) */
-};
-
 int
 app_pipeline_routing_add_route(struct app_params *app,
 	uint32_t pipeline_id,
 	struct pipeline_routing_route_key *key,
-	struct app_pipeline_routing_route_params *route_params);
+	struct pipeline_routing_route_data *data);
 
 int
 app_pipeline_routing_delete_route(struct app_params *app,
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.c b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
index 06d3e65..4a95c7d 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.c
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.c
@@ -49,16 +49,32 @@
 #include "pipeline_actions_common.h"
 #include "hash_func.h"
 
+#define MPLS_LABEL(label, exp, s, ttl)					\
+	(((((uint64_t) (label)) & 0xFFFFFLLU) << 12) |		\
+	((((uint64_t) (exp)) & 0x7LLU) << 9) |				\
+	((((uint64_t) (s)) & 0x1LLU) << 8) |				\
+	(((uint64_t) (ttl)) & 0xFFLU))
+
+#define RTE_SCHED_PORT_HIERARCHY(subport, pipe,		\
+	traffic_class, queue, color)				\
+	((((uint64_t) (queue)) & 0x3) |                \
+	((((uint64_t) (traffic_class)) & 0x3) << 2) |  \
+	((((uint64_t) (color)) & 0x3) << 4) |          \
+	((((uint64_t) (subport)) & 0xFFFF) << 16) |    \
+	((((uint64_t) (pipe)) & 0xFFFFFFFF) << 32))
+
+
+#define MAC_SRC_DEFAULT 0x112233445566
+
 struct pipeline_routing {
 	struct pipeline p;
+	struct pipeline_routing_params params;
 	pipeline_msg_req_handler custom_handlers[PIPELINE_ROUTING_MSG_REQS];
-
-	uint32_t n_routes;
-	uint32_t n_arp_entries;
-	uint32_t ip_da_offset;
-	uint32_t arp_key_offset;
 } __rte_cache_aligned;
 
+/*
+ * Message handlers
+ */
 static void *
 pipeline_routing_msg_req_custom_handler(struct pipeline *p, void *msg);
 
@@ -138,35 +154,168 @@ struct routing_table_entry {
 	uint32_t flags;
 	uint32_t port_id; /* Output port ID */
 	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+
+	/* ether_l2 */
+	uint16_t data_offset;
+	uint16_t ether_l2_length;
+	uint64_t slab[4];
+	uint16_t slab_offset[4];
 };
 
-static inline void
+struct layout {
+	uint16_t a;
+	uint32_t b;
+	uint16_t c;
+} __attribute__((__packed__));
+
+#define MACADDR_DST_WRITE(slab_ptr, slab)			\
+{								\
+	struct layout *dst = (struct layout *) (slab_ptr);	\
+	struct layout *src = (struct layout *) &(slab);		\
+								\
+	dst->b = src->b;					\
+	dst->c = src->c;					\
+}
+
+static inline __attribute__((always_inline)) void
 pkt_work_routing(
 	struct rte_mbuf *pkt,
 	struct rte_pipeline_table_entry *table_entry,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry =
 		(struct routing_table_entry *) table_entry;
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkt, p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->arp_key_offset);
-	uint32_t ip = RTE_MBUF_METADATA_UINT32(pkt, p_rt->ip_da_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkt, p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr, *slab1_ptr, *slab2_ptr, *slab3_ptr, sched;
+	uint32_t ip_da, nh_ip, port_id;
+	uint16_t total_length, data_offset, ether_l2_length;
+
+	/* Read */
+	total_length = rte_bswap16(ip->total_length);
+	ip_da = ip->dst_addr;
+	data_offset = entry->data_offset;
+	ether_l2_length = entry->ether_l2_length;
+	slab0_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[0]);
+	slab1_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[1]);
+	slab2_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[2]);
+	slab3_ptr = RTE_MBUF_METADATA_UINT64_PTR(pkt, entry->slab_offset[3]);
+
+	if (arp) {
+		port_id = entry->port_id;
+		nh_ip = entry->ip;
+		if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip = ip_da;
+	}
+
+	/* Compute */
+	total_length += ether_l2_length;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp = ip->type_of_service >> 2;
+		uint32_t svlan, cvlan, tc, tc_q;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq = rte_bswap64(entry->slab[0]);
+
+			svlan = (slab_qinq >> 48) & 0xFFF;
+			cvlan = (slab_qinq >> 16) & 0xFFF;
+			tc = (dscp >> 2) & 0x3;
+			tc_q = dscp & 0x3;
+		} else {
+			uint32_t ip_src = rte_bswap32(ip->src_addr);
 
-	arp_key->port_id = entry->port_id;
-	arp_key->ip = entry->ip;
-	if (entry->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key->ip = ip;
+			svlan = 0;
+			cvlan = (ip_src >> 16) & 0xFFF;
+			tc = (ip_src >> 2) & 0x3;
+			tc_q = ip_src & 0x3;
+		}
+		sched = RTE_SCHED_PORT_HIERARCHY(svlan,
+			cvlan,
+			tc,
+			tc_q,
+			e_RTE_METER_GREEN);
+	}
+
+	/* Write */
+	pkt->data_off = data_offset;
+	pkt->data_len = total_length;
+	pkt->pkt_len = total_length;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr = entry->slab[0];
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab1_ptr, entry->slab[1]);
+	}
+
+	if (qinq) {
+		*slab0_ptr = entry->slab[0];
+		*slab1_ptr = entry->slab[1];
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab2_ptr, entry->slab[2]);
+
+		if (qinq_sched) {
+			pkt->hash.sched.lo = sched & 0xFFFFFFFF;
+			pkt->hash.sched.hi = sched >> 32;
+		}
+	}
+
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp = rte_bswap64(
+				(MPLS_LABEL(0, pkt_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt_color, 0, 0));
+
+			*slab0_ptr = entry->slab[0] | mpls_exp;
+			*slab1_ptr = entry->slab[1] | mpls_exp;
+			*slab2_ptr = entry->slab[2];
+		} else {
+			*slab0_ptr = entry->slab[0];
+			*slab1_ptr = entry->slab[1];
+			*slab2_ptr = entry->slab[2];
+		}
+
+		if (arp == 0)
+			MACADDR_DST_WRITE(slab3_ptr, entry->slab[3]);
+	}
+
+	if (arp) {
+		arp_key->port_id = port_id;
+		arp_key->ip = nh_ip;
+	}
 }
 
-static inline void
+static inline __attribute__((always_inline)) void
 pkt4_work_routing(
 	struct rte_mbuf **pkts,
 	struct rte_pipeline_table_entry **table_entries,
-	void *arg)
+	void *arg,
+	int arp,
+	int qinq,
+	int qinq_sched,
+	int mpls,
+	int mpls_color_mark)
 {
+	struct pipeline_routing *p_rt = arg;
+
 	struct routing_table_entry *entry0 =
 		(struct routing_table_entry *) table_entries[0];
 	struct routing_table_entry *entry1 =
@@ -175,51 +324,507 @@ pkt4_work_routing(
 		(struct routing_table_entry *) table_entries[2];
 	struct routing_table_entry *entry3 =
 		(struct routing_table_entry *) table_entries[3];
-	struct pipeline_routing *p_rt = arg;
+
+	struct ipv4_hdr *ip0 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip1 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip2 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.ip_hdr_offset);
+	struct ipv4_hdr *ip3 = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.ip_hdr_offset);
+
+	enum rte_meter_color pkt0_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->params.color_offset);
+	enum rte_meter_color pkt1_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->params.color_offset);
+	enum rte_meter_color pkt2_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->params.color_offset);
+	enum rte_meter_color pkt3_color = (enum rte_meter_color)
+		RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->params.color_offset);
 
 	struct pipeline_routing_arp_key_ipv4 *arp_key0 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[0], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[0],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key1 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[1], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[1],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key2 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[2], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[2],
+			p_rt->params.arp_key_offset);
 	struct pipeline_routing_arp_key_ipv4 *arp_key3 =
 		(struct pipeline_routing_arp_key_ipv4 *)
-		RTE_MBUF_METADATA_UINT8_PTR(pkts[3], p_rt->arp_key_offset);
+		RTE_MBUF_METADATA_UINT8_PTR(pkts[3],
+			p_rt->params.arp_key_offset);
+
+	uint64_t *slab0_ptr0, *slab1_ptr0, *slab2_ptr0, *slab3_ptr0;
+	uint64_t *slab0_ptr1, *slab1_ptr1, *slab2_ptr1, *slab3_ptr1;
+	uint64_t *slab0_ptr2, *slab1_ptr2, *slab2_ptr2, *slab3_ptr2;
+	uint64_t *slab0_ptr3, *slab1_ptr3, *slab2_ptr3, *slab3_ptr3;
+	uint64_t sched0, sched1, sched2, sched3;
+
+	uint32_t ip_da0, nh_ip0, port_id0;
+	uint32_t ip_da1, nh_ip1, port_id1;
+	uint32_t ip_da2, nh_ip2, port_id2;
+	uint32_t ip_da3, nh_ip3, port_id3;
+
+	uint16_t total_length0, data_offset0, ether_l2_length0;
+	uint16_t total_length1, data_offset1, ether_l2_length1;
+	uint16_t total_length2, data_offset2, ether_l2_length2;
+	uint16_t total_length3, data_offset3, ether_l2_length3;
+
+	/* Read */
+	total_length0 = rte_bswap16(ip0->total_length);
+	total_length1 = rte_bswap16(ip1->total_length);
+	total_length2 = rte_bswap16(ip2->total_length);
+	total_length3 = rte_bswap16(ip3->total_length);
+
+	ip_da0 = ip0->dst_addr;
+	ip_da1 = ip1->dst_addr;
+	ip_da2 = ip2->dst_addr;
+	ip_da3 = ip3->dst_addr;
+
+	data_offset0 = entry0->data_offset;
+	data_offset1 = entry1->data_offset;
+	data_offset2 = entry2->data_offset;
+	data_offset3 = entry3->data_offset;
+
+	ether_l2_length0 = entry0->ether_l2_length;
+	ether_l2_length1 = entry1->ether_l2_length;
+	ether_l2_length2 = entry2->ether_l2_length;
+	ether_l2_length3 = entry3->ether_l2_length;
+
+	slab0_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[0]);
+	slab1_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[1]);
+	slab2_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[2]);
+	slab3_ptr0 = RTE_MBUF_METADATA_UINT64_PTR(pkts[0],
+		entry0->slab_offset[3]);
+
+	slab0_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[0]);
+	slab1_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[1]);
+	slab2_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[2]);
+	slab3_ptr1 = RTE_MBUF_METADATA_UINT64_PTR(pkts[1],
+		entry1->slab_offset[3]);
+
+	slab0_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[0]);
+	slab1_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[1]);
+	slab2_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[2]);
+	slab3_ptr2 = RTE_MBUF_METADATA_UINT64_PTR(pkts[2],
+		entry2->slab_offset[3]);
+
+	slab0_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[0]);
+	slab1_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[1]);
+	slab2_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[2]);
+	slab3_ptr3 = RTE_MBUF_METADATA_UINT64_PTR(pkts[3],
+		entry3->slab_offset[3]);
+
+	if (arp) {
+		port_id0 = entry0->port_id;
+		nh_ip0 = entry0->ip;
+		if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip0 = ip_da0;
+
+		port_id1 = entry1->port_id;
+		nh_ip1 = entry1->ip;
+		if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip1 = ip_da1;
+
+		port_id2 = entry2->port_id;
+		nh_ip2 = entry2->ip;
+		if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip2 = ip_da2;
+
+		port_id3 = entry3->port_id;
+		nh_ip3 = entry3->ip;
+		if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
+			nh_ip3 = ip_da3;
+	}
 
-	uint32_t ip0 = RTE_MBUF_METADATA_UINT32(pkts[0], p_rt->ip_da_offset);
-	uint32_t ip1 = RTE_MBUF_METADATA_UINT32(pkts[1], p_rt->ip_da_offset);
-	uint32_t ip2 = RTE_MBUF_METADATA_UINT32(pkts[2], p_rt->ip_da_offset);
-	uint32_t ip3 = RTE_MBUF_METADATA_UINT32(pkts[3], p_rt->ip_da_offset);
+	/* Compute */
+	total_length0 += ether_l2_length0;
+	total_length1 += ether_l2_length1;
+	total_length2 += ether_l2_length2;
+	total_length3 += ether_l2_length3;
+
+	if (qinq && qinq_sched) {
+		uint32_t dscp0 = ip0->type_of_service >> 2;
+		uint32_t dscp1 = ip1->type_of_service >> 2;
+		uint32_t dscp2 = ip2->type_of_service >> 2;
+		uint32_t dscp3 = ip3->type_of_service >> 2;
+		uint32_t svlan0, cvlan0, tc0, tc_q0;
+		uint32_t svlan1, cvlan1, tc1, tc_q1;
+		uint32_t svlan2, cvlan2, tc2, tc_q2;
+		uint32_t svlan3, cvlan3, tc3, tc_q3;
+
+		if (qinq_sched == 1) {
+			uint64_t slab_qinq0 = rte_bswap64(entry0->slab[0]);
+			uint64_t slab_qinq1 = rte_bswap64(entry1->slab[0]);
+			uint64_t slab_qinq2 = rte_bswap64(entry2->slab[0]);
+			uint64_t slab_qinq3 = rte_bswap64(entry3->slab[0]);
+
+			svlan0 = (slab_qinq0 >> 48) & 0xFFF;
+			svlan1 = (slab_qinq1 >> 48) & 0xFFF;
+			svlan2 = (slab_qinq2 >> 48) & 0xFFF;
+			svlan3 = (slab_qinq3 >> 48) & 0xFFF;
+
+			cvlan0 = (slab_qinq0 >> 16) & 0xFFF;
+			cvlan1 = (slab_qinq1 >> 16) & 0xFFF;
+			cvlan2 = (slab_qinq2 >> 16) & 0xFFF;
+			cvlan3 = (slab_qinq3 >> 16) & 0xFFF;
+
+			tc0 = (dscp0 >> 2) & 0x3;
+			tc1 = (dscp1 >> 2) & 0x3;
+			tc2 = (dscp2 >> 2) & 0x3;
+			tc3 = (dscp3 >> 2) & 0x3;
+
+			tc_q0 = dscp0 & 0x3;
+			tc_q1 = dscp1 & 0x3;
+			tc_q2 = dscp2 & 0x3;
+			tc_q3 = dscp3 & 0x3;
+		} else {
+			uint32_t ip_src0 = rte_bswap32(ip0->src_addr);
+			uint32_t ip_src1 = rte_bswap32(ip1->src_addr);
+			uint32_t ip_src2 = rte_bswap32(ip2->src_addr);
+			uint32_t ip_src3 = rte_bswap32(ip3->src_addr);
+
+			svlan0 = 0;
+			svlan1 = 0;
+			svlan2 = 0;
+			svlan3 = 0;
+
+			cvlan0 = (ip_src0 >> 16) & 0xFFF;
+			cvlan1 = (ip_src1 >> 16) & 0xFFF;
+			cvlan2 = (ip_src2 >> 16) & 0xFFF;
+			cvlan3 = (ip_src3 >> 16) & 0xFFF;
+
+			tc0 = (ip_src0 >> 2) & 0x3;
+			tc1 = (ip_src1 >> 2) & 0x3;
+			tc2 = (ip_src2 >> 2) & 0x3;
+			tc3 = (ip_src3 >> 2) & 0x3;
+
+			tc_q0 = ip_src0 & 0x3;
+			tc_q1 = ip_src1 & 0x3;
+			tc_q2 = ip_src2 & 0x3;
+			tc_q3 = ip_src3 & 0x3;
+		}
 
-	arp_key0->port_id = entry0->port_id;
-	arp_key1->port_id = entry1->port_id;
-	arp_key2->port_id = entry2->port_id;
-	arp_key3->port_id = entry3->port_id;
+		sched0 = RTE_SCHED_PORT_HIERARCHY(svlan0,
+			cvlan0,
+			tc0,
+			tc_q0,
+			e_RTE_METER_GREEN);
+		sched1 = RTE_SCHED_PORT_HIERARCHY(svlan1,
+			cvlan1,
+			tc1,
+			tc_q1,
+			e_RTE_METER_GREEN);
+		sched2 = RTE_SCHED_PORT_HIERARCHY(svlan2,
+			cvlan2,
+			tc2,
+			tc_q2,
+			e_RTE_METER_GREEN);
+		sched3 = RTE_SCHED_PORT_HIERARCHY(svlan3,
+			cvlan3,
+			tc3,
+			tc_q3,
+			e_RTE_METER_GREEN);
 
-	arp_key0->ip = entry0->ip;
-	if (entry0->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key0->ip = ip0;
+	}
 
-	arp_key1->ip = entry1->ip;
-	if (entry1->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key1->ip = ip1;
+	/* Write */
+	pkts[0]->data_off = data_offset0;
+	pkts[1]->data_off = data_offset1;
+	pkts[2]->data_off = data_offset2;
+	pkts[3]->data_off = data_offset3;
+
+	pkts[0]->data_len = total_length0;
+	pkts[1]->data_len = total_length1;
+	pkts[2]->data_len = total_length2;
+	pkts[3]->data_len = total_length3;
+
+	pkts[0]->pkt_len = total_length0;
+	pkts[1]->pkt_len = total_length1;
+	pkts[2]->pkt_len = total_length2;
+	pkts[3]->pkt_len = total_length3;
+
+	if ((qinq == 0) && (mpls == 0)) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab1_ptr0, entry0->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr1, entry1->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr2, entry2->slab[1]);
+			MACADDR_DST_WRITE(slab1_ptr3, entry3->slab[1]);
+		}
+	}
+
+	if (qinq) {
+		*slab0_ptr0 = entry0->slab[0];
+		*slab0_ptr1 = entry1->slab[0];
+		*slab0_ptr2 = entry2->slab[0];
+		*slab0_ptr3 = entry3->slab[0];
+
+		*slab1_ptr0 = entry0->slab[1];
+		*slab1_ptr1 = entry1->slab[1];
+		*slab1_ptr2 = entry2->slab[1];
+		*slab1_ptr3 = entry3->slab[1];
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab2_ptr0, entry0->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr1, entry1->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr2, entry2->slab[2]);
+			MACADDR_DST_WRITE(slab2_ptr3, entry3->slab[2]);
+		}
 
-	arp_key2->ip = entry2->ip;
-	if (entry2->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key2->ip = ip2;
+		if (qinq_sched) {
+			pkts[0]->hash.sched.lo = sched0 & 0xFFFFFFFF;
+			pkts[0]->hash.sched.hi = sched0 >> 32;
+			pkts[1]->hash.sched.lo = sched1 & 0xFFFFFFFF;
+			pkts[1]->hash.sched.hi = sched1 >> 32;
+			pkts[2]->hash.sched.lo = sched2 & 0xFFFFFFFF;
+			pkts[2]->hash.sched.hi = sched2 >> 32;
+			pkts[3]->hash.sched.lo = sched3 & 0xFFFFFFFF;
+			pkts[3]->hash.sched.hi = sched3 >> 32;
+		}
+	}
 
-	arp_key3->ip = entry3->ip;
-	if (entry3->flags & PIPELINE_ROUTING_ROUTE_LOCAL)
-		arp_key3->ip = ip3;
+	if (mpls) {
+		if (mpls_color_mark) {
+			uint64_t mpls_exp0 = rte_bswap64(
+				(MPLS_LABEL(0, pkt0_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt0_color, 0, 0));
+			uint64_t mpls_exp1 = rte_bswap64(
+				(MPLS_LABEL(0, pkt1_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt1_color, 0, 0));
+			uint64_t mpls_exp2 = rte_bswap64(
+				(MPLS_LABEL(0, pkt2_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt2_color, 0, 0));
+			uint64_t mpls_exp3 = rte_bswap64(
+				(MPLS_LABEL(0, pkt3_color, 0, 0) << 32) |
+				MPLS_LABEL(0, pkt3_color, 0, 0));
+
+			*slab0_ptr0 = entry0->slab[0] | mpls_exp0;
+			*slab0_ptr1 = entry1->slab[0] | mpls_exp1;
+			*slab0_ptr2 = entry2->slab[0] | mpls_exp2;
+			*slab0_ptr3 = entry3->slab[0] | mpls_exp3;
+
+			*slab1_ptr0 = entry0->slab[1] | mpls_exp0;
+			*slab1_ptr1 = entry1->slab[1] | mpls_exp1;
+			*slab1_ptr2 = entry2->slab[1] | mpls_exp2;
+			*slab1_ptr3 = entry3->slab[1] | mpls_exp3;
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		} else {
+			*slab0_ptr0 = entry0->slab[0];
+			*slab0_ptr1 = entry1->slab[0];
+			*slab0_ptr2 = entry2->slab[0];
+			*slab0_ptr3 = entry3->slab[0];
+
+			*slab1_ptr0 = entry0->slab[1];
+			*slab1_ptr1 = entry1->slab[1];
+			*slab1_ptr2 = entry2->slab[1];
+			*slab1_ptr3 = entry3->slab[1];
+
+			*slab2_ptr0 = entry0->slab[2];
+			*slab2_ptr1 = entry1->slab[2];
+			*slab2_ptr2 = entry2->slab[2];
+			*slab2_ptr3 = entry3->slab[2];
+		}
+
+		if (arp == 0) {
+			MACADDR_DST_WRITE(slab3_ptr0, entry0->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr1, entry1->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr2, entry2->slab[3]);
+			MACADDR_DST_WRITE(slab3_ptr3, entry3->slab[3]);
+		}
+	}
+
+	if (arp) {
+		arp_key0->port_id = port_id0;
+		arp_key1->port_id = port_id1;
+		arp_key2->port_id = port_id2;
+		arp_key3->port_id = port_id3;
+
+		arp_key0->ip = nh_ip0;
+		arp_key1->ip = nh_ip1;
+		arp_key2->ip = nh_ip2;
+		arp_key3->ip = nh_ip3;
+	}
 }
 
-PIPELINE_TABLE_AH_HIT(routing_table_ah_hit,
-	pkt_work_routing,
-	pkt4_work_routing);
+#define PKT_WORK_ROUTING_ETHERNET(arp)				\
+static inline void						\
+pkt_work_routing_ether_arp##arp(				\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 0, 0, 0, 0);\
+}
+
+#define PKT4_WORK_ROUTING_ETHERNET(arp)				\
+static inline void						\
+pkt4_work_routing_ether_arp##arp(				\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 0, 0, 0, 0);\
+}
+
+#define routing_table_ah_hit_ether(arp)				\
+PKT_WORK_ROUTING_ETHERNET(arp)					\
+PKT4_WORK_ROUTING_ETHERNET(arp)					\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_arp##arp,	\
+	pkt_work_routing_ether_arp##arp,			\
+	pkt4_work_routing_ether_arp##arp)
+
+routing_table_ah_hit_ether(0)
+routing_table_ah_hit_ether(1)
+
+#define PKT_WORK_ROUTING_ETHERNET_QINQ(sched, arp)		\
+static inline void						\
+pkt_work_routing_ether_qinq_sched##sched##_arp##arp(		\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 1, sched, 0, 0);\
+}
+
+#define PKT4_WORK_ROUTING_ETHERNET_QINQ(sched, arp)		\
+static inline void						\
+pkt4_work_routing_ether_qinq_sched##sched##_arp##arp(		\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 1, sched, 0, 0);\
+}
+
+#define routing_table_ah_hit_ether_qinq(sched, arp)		\
+PKT_WORK_ROUTING_ETHERNET_QINQ(sched, arp)			\
+PKT4_WORK_ROUTING_ETHERNET_QINQ(sched, arp)			\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_qinq_sched##sched##_arp##arp,\
+	pkt_work_routing_ether_qinq_sched##sched##_arp##arp,	\
+	pkt4_work_routing_ether_qinq_sched##sched##_arp##arp)
+
+routing_table_ah_hit_ether_qinq(0, 0)
+routing_table_ah_hit_ether_qinq(1, 0)
+routing_table_ah_hit_ether_qinq(2, 0)
+routing_table_ah_hit_ether_qinq(0, 1)
+routing_table_ah_hit_ether_qinq(1, 1)
+routing_table_ah_hit_ether_qinq(2, 1)
+
+#define PKT_WORK_ROUTING_ETHERNET_MPLS(color, arp)		\
+static inline void						\
+pkt_work_routing_ether_mpls_color##color##_arp##arp(		\
+	struct rte_mbuf *pkt,					\
+	struct rte_pipeline_table_entry *table_entry,		\
+	void *arg)						\
+{								\
+	pkt_work_routing(pkt, table_entry, arg, arp, 0, 0, 1, color);\
+}
+
+#define PKT4_WORK_ROUTING_ETHERNET_MPLS(color, arp)		\
+static inline void						\
+pkt4_work_routing_ether_mpls_color##color##_arp##arp(		\
+	struct rte_mbuf **pkts,					\
+	struct rte_pipeline_table_entry **table_entries,	\
+	void *arg)						\
+{								\
+	pkt4_work_routing(pkts, table_entries, arg, arp, 0, 0, 1, color);\
+}
+
+#define routing_table_ah_hit_ether_mpls(color, arp)		\
+PKT_WORK_ROUTING_ETHERNET_MPLS(color, arp)			\
+PKT4_WORK_ROUTING_ETHERNET_MPLS(color, arp)			\
+PIPELINE_TABLE_AH_HIT(routing_table_ah_hit_ether_mpls_color##color##_arp##arp,\
+	pkt_work_routing_ether_mpls_color##color##_arp##arp,	\
+	pkt4_work_routing_ether_mpls_color##color##_arp##arp)
+
+routing_table_ah_hit_ether_mpls(0, 0)
+routing_table_ah_hit_ether_mpls(1, 0)
+routing_table_ah_hit_ether_mpls(0, 1)
+routing_table_ah_hit_ether_mpls(1, 1)
+
+static rte_pipeline_table_action_handler_hit
+get_routing_table_ah_hit(struct pipeline_routing *p)
+{
+	if (p->params.dbg_ah_disable)
+		return NULL;
+
+	switch (p->params.encap) {
+	case PIPELINE_ROUTING_ENCAP_ETHERNET:
+		return (p->params.n_arp_entries) ?
+			routing_table_ah_hit_ether_arp1 :
+			routing_table_ah_hit_ether_arp0;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ:
+		if (p->params.n_arp_entries)
+			switch (p->params.qinq_sched) {
+			case 0:
+				return routing_table_ah_hit_ether_qinq_sched0_arp1;
+			case 1:
+				return routing_table_ah_hit_ether_qinq_sched1_arp1;
+			case 2:
+				return routing_table_ah_hit_ether_qinq_sched2_arp1;
+			default:
+				return NULL;
+			}
+		 else
+			switch (p->params.qinq_sched) {
+			case 0:
+				return routing_table_ah_hit_ether_qinq_sched0_arp0;
+			case 1:
+				return routing_table_ah_hit_ether_qinq_sched1_arp0;
+			case 2:
+				return routing_table_ah_hit_ether_qinq_sched2_arp0;
+			default:
+				return NULL;
+			}
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS:
+		if (p->params.n_arp_entries)
+			if (p->params.mpls_color_mark)
+				return routing_table_ah_hit_ether_mpls_color1_arp1;
+			else
+				return routing_table_ah_hit_ether_mpls_color0_arp1;
+		else
+			if (p->params.mpls_color_mark)
+				return routing_table_ah_hit_ether_mpls_color1_arp0;
+			else
+				return routing_table_ah_hit_ether_mpls_color0_arp0;
+
+	default:
+		return NULL;
+	}
+}
 
 /*
  * ARP table
@@ -229,6 +834,9 @@ struct arp_table_entry {
 	uint64_t macaddr;
 };
 
+/**
+ * ARP table AH
+ */
 static inline void
 pkt_work_arp(
 	struct rte_mbuf *pkt,
@@ -237,20 +845,15 @@ pkt_work_arp(
 {
 	struct arp_table_entry *entry = (struct arp_table_entry *) table_entry;
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw = rte_pktmbuf_mtod(pkt, uint8_t *);
+	/* Read */
+	uint64_t macaddr_dst = entry->macaddr;
+	uint64_t *slab_ptr = (uint64_t *) ((char *) pkt->buf_addr +
+		(pkt->data_off - 2));
 
-	/* Read: table entry */
-	uint64_t mac_addr_dst = entry->macaddr;
-	uint64_t mac_addr_src = 0;
+	/* Compute */
 
-	/* Compute: Ethernet header */
-	uint64_t slab0 = mac_addr_dst | (mac_addr_src << 48);
-	uint32_t slab1 = mac_addr_src >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw) = slab0;
-	*((uint32_t *) (raw + 8)) = slab1;
+	/* Write */
+	MACADDR_DST_WRITE(slab_ptr, macaddr_dst);
 }
 
 static inline void
@@ -268,59 +871,69 @@ pkt4_work_arp(
 	struct arp_table_entry *entry3 =
 		(struct arp_table_entry *) table_entries[3];
 
-	/* Read: pkt buffer - mbuf */
-	uint8_t *raw0 = rte_pktmbuf_mtod(pkts[0], uint8_t *);
-	uint8_t *raw1 = rte_pktmbuf_mtod(pkts[1], uint8_t *);
-	uint8_t *raw2 = rte_pktmbuf_mtod(pkts[2], uint8_t *);
-	uint8_t *raw3 = rte_pktmbuf_mtod(pkts[3], uint8_t *);
-
-	/* Read: table entry */
-	uint64_t mac_addr_dst0 = entry0->macaddr;
-	uint64_t mac_addr_dst1 = entry1->macaddr;
-	uint64_t mac_addr_dst2 = entry2->macaddr;
-	uint64_t mac_addr_dst3 = entry3->macaddr;
-
-	uint64_t mac_addr_src0 = 0;
-	uint64_t mac_addr_src1 = 0;
-	uint64_t mac_addr_src2 = 0;
-	uint64_t mac_addr_src3 = 0;
-
-	/* Compute: Ethernet header */
-	uint64_t pkt0_slab0 = mac_addr_dst0 | (mac_addr_src0 << 48);
-	uint64_t pkt1_slab0 = mac_addr_dst1 | (mac_addr_src1 << 48);
-	uint64_t pkt2_slab0 = mac_addr_dst2 | (mac_addr_src2 << 48);
-	uint64_t pkt3_slab0 = mac_addr_dst3 | (mac_addr_src3 << 48);
-
-	uint32_t pkt0_slab1 = mac_addr_src0 >> 16;
-	uint32_t pkt1_slab1 = mac_addr_src1 >> 16;
-	uint32_t pkt2_slab1 = mac_addr_src2 >> 16;
-	uint32_t pkt3_slab1 = mac_addr_src3 >> 16;
-
-	/* Write: pkt buffer - pkt headers */
-	*((uint64_t *) raw0) = pkt0_slab0;
-	*((uint32_t *) (raw0 + 8)) = pkt0_slab1;
-	*((uint64_t *) raw1) = pkt1_slab0;
-	*((uint32_t *) (raw1 + 8)) = pkt1_slab1;
-	*((uint64_t *) raw2) = pkt2_slab0;
-	*((uint32_t *) (raw2 + 8)) = pkt2_slab1;
-	*((uint64_t *) raw3) = pkt3_slab0;
-	*((uint32_t *) (raw3 + 8)) = pkt3_slab1;
+	/* Read */
+	uint64_t macaddr_dst0 = entry0->macaddr;
+	uint64_t macaddr_dst1 = entry1->macaddr;
+	uint64_t macaddr_dst2 = entry2->macaddr;
+	uint64_t macaddr_dst3 = entry3->macaddr;
+
+	uint64_t *slab_ptr0 = (uint64_t *) ((char *) pkts[0]->buf_addr +
+		(pkts[0]->data_off - 2));
+	uint64_t *slab_ptr1 = (uint64_t *) ((char *) pkts[1]->buf_addr +
+		(pkts[1]->data_off - 2));
+	uint64_t *slab_ptr2 = (uint64_t *) ((char *) pkts[2]->buf_addr +
+		(pkts[2]->data_off - 2));
+	uint64_t *slab_ptr3 = (uint64_t *) ((char *) pkts[3]->buf_addr +
+		(pkts[3]->data_off - 2));
+
+	/* Compute */
+
+	/* Write */
+	MACADDR_DST_WRITE(slab_ptr0, macaddr_dst0);
+	MACADDR_DST_WRITE(slab_ptr1, macaddr_dst1);
+	MACADDR_DST_WRITE(slab_ptr2, macaddr_dst2);
+	MACADDR_DST_WRITE(slab_ptr3, macaddr_dst3);
 }
 
 PIPELINE_TABLE_AH_HIT(arp_table_ah_hit,
 	pkt_work_arp,
 	pkt4_work_arp);
 
-static int
-pipeline_routing_parse_args(struct pipeline_routing *p,
+static rte_pipeline_table_action_handler_hit
+get_arp_table_ah_hit(struct pipeline_routing *p)
+{
+	if (p->params.dbg_ah_disable)
+		return NULL;
+
+	return arp_table_ah_hit;
+}
+
+/*
+ * Argument parsing
+ */
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
 	struct pipeline_params *params)
 {
 	uint32_t n_routes_present = 0;
+	uint32_t encap_present = 0;
+	uint32_t qinq_sched_present = 0;
+	uint32_t mpls_color_mark_present = 0;
 	uint32_t n_arp_entries_present = 0;
-	uint32_t ip_da_offset_present = 0;
+	uint32_t ip_hdr_offset_present = 0;
 	uint32_t arp_key_offset_present = 0;
+	uint32_t color_offset_present = 0;
+	uint32_t dbg_ah_disable_present = 0;
 	uint32_t i;
 
+	/* default values */
+	p->n_routes = PIPELINE_ROUTING_N_ROUTES_DEFAULT;
+	p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET;
+	p->qinq_sched = 0;
+	p->mpls_color_mark = 0;
+	p->n_arp_entries = 0;
+	p->dbg_ah_disable = 0;
+
 	for (i = 0; i < params->n_args; i++) {
 		char *arg_name = params->args_name[i];
 		char *arg_value = params->args_value[i];
@@ -338,6 +951,70 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* encap */
+		if (strcmp(arg_name, "encap") == 0) {
+			if (encap_present)
+				return -1;
+			encap_present = 1;
+
+			/* ethernet */
+			if (strcmp(arg_value, "ethernet") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET;
+				continue;
+			}
+
+			/* ethernet_qinq */
+			if (strcmp(arg_value, "ethernet_qinq") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ;
+				continue;
+			}
+
+			/* ethernet_mpls */
+			if (strcmp(arg_value, "ethernet_mpls") == 0) {
+				p->encap = PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS;
+				continue;
+			}
+
+			/* any other */
+			return -1;
+		}
+
+		/* qinq_sched */
+		if (strcmp(arg_name, "qinq_sched") == 0) {
+			if (qinq_sched_present)
+				return -1;
+
+			qinq_sched_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->qinq_sched = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->qinq_sched = 1;
+			else if (strcmp(arg_value, "test") == 0)
+				p->qinq_sched = 2;
+			else
+				return -1;
+
+			continue;
+		}
+
+		/* mpls_color_mark */
+		if (strcmp(arg_name, "mpls_color_mark") == 0) {
+			if (mpls_color_mark_present)
+				return -1;
+
+			mpls_color_mark_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->mpls_color_mark = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->mpls_color_mark = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
 		/* n_arp_entries */
 		if (strcmp(arg_name, "n_arp_entries") == 0) {
 			if (n_arp_entries_present)
@@ -345,19 +1022,17 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			n_arp_entries_present = 1;
 
 			p->n_arp_entries = atoi(arg_value);
-			if (p->n_arp_entries == 0)
-				return -1;
 
 			continue;
 		}
 
-		/* ip_da_offset */
-		if (strcmp(arg_name, "ip_da_offset") == 0) {
-			if (ip_da_offset_present)
+		/* ip_hdr_offset */
+		if (strcmp(arg_name, "ip_hdr_offset") == 0) {
+			if (ip_hdr_offset_present)
 				return -1;
-			ip_da_offset_present = 1;
+			ip_hdr_offset_present = 1;
 
-			p->ip_da_offset = atoi(arg_value);
+			p->ip_hdr_offset = atoi(arg_value);
 
 			continue;
 		}
@@ -373,15 +1048,67 @@ pipeline_routing_parse_args(struct pipeline_routing *p,
 			continue;
 		}
 
+		/* color_offset */
+		if (strcmp(arg_name, "color_offset") == 0) {
+			if (color_offset_present)
+				return -1;
+			color_offset_present = 1;
+
+			p->color_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* debug */
+		if (strcmp(arg_name, "dbg_ah_disable") == 0) {
+			if (dbg_ah_disable_present)
+				return -1;
+			dbg_ah_disable_present = 1;
+
+			if (strcmp(arg_value, "no") == 0)
+				p->dbg_ah_disable = 0;
+			else if (strcmp(arg_value, "yes") == 0)
+				p->dbg_ah_disable = 1;
+			else
+				return -1;
+
+			continue;
+		}
+
 		/* any other */
 		return -1;
 	}
 
 	/* Check that mandatory arguments are present */
-	if ((n_routes_present == 0) ||
-		(n_arp_entries_present == 0) ||
-		(ip_da_offset_present == 0) ||
-		(n_arp_entries_present && (arp_key_offset_present == 0)))
+	if (ip_hdr_offset_present == 0)
+		return -1;
+
+	/* Check relations between arguments */
+	switch (p->encap) {
+	case PIPELINE_ROUTING_ENCAP_ETHERNET:
+		if (p->qinq_sched ||
+			p->mpls_color_mark ||
+			color_offset_present)
+			return -1;
+		break;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ:
+		if (p->mpls_color_mark ||
+			color_offset_present)
+			return -1;
+		break;
+
+	case PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS:
+		if (p->qinq_sched)
+			return -1;
+		break;
+
+	default:
+		return -1;
+	}
+
+	if ((p->n_arp_entries && (arp_key_offset_present == 0)) ||
+		((p->n_arp_entries == 0) && arp_key_offset_present))
 		return -1;
 
 	return 0;
@@ -414,7 +1141,7 @@ pipeline_routing_init(struct pipeline_params *params,
 	PLOG(p, HIGH, "Routing");
 
 	/* Parse arguments */
-	if (pipeline_routing_parse_args(p_rt, params))
+	if (pipeline_routing_parse_args(&p_rt->params, params))
 		return NULL;
 
 	/* Pipeline */
@@ -485,15 +1212,16 @@ pipeline_routing_init(struct pipeline_params *params,
 	{
 		struct rte_table_lpm_params table_lpm_params = {
 			.name = p->name,
-			.n_rules = p_rt->n_routes,
+			.n_rules = p_rt->params.n_routes,
 			.entry_unique_size = sizeof(struct routing_table_entry),
-			.offset = p_rt->ip_da_offset,
+			.offset = p_rt->params.ip_hdr_offset +
+				__builtin_offsetof(struct ipv4_hdr, dst_addr),
 		};
 
 		struct rte_pipeline_table_params table_params = {
 				.ops = &rte_table_lpm_ops,
 				.arg_create = &table_lpm_params,
-				.f_action_hit = routing_table_ah_hit,
+				.f_action_hit = get_routing_table_ah_hit(p_rt),
 				.f_action_miss = NULL,
 				.arg_ah = p_rt,
 				.action_data_size =
@@ -515,20 +1243,20 @@ pipeline_routing_init(struct pipeline_params *params,
 	}
 
 	/* ARP table configuration */
-	if (p_rt->n_arp_entries) {
+	if (p_rt->params.n_arp_entries) {
 		struct rte_table_hash_key8_ext_params table_arp_params = {
-			.n_entries = p_rt->n_arp_entries,
-			.n_entries_ext = p_rt->n_arp_entries,
+			.n_entries = p_rt->params.n_arp_entries,
+			.n_entries_ext = p_rt->params.n_arp_entries,
 			.f_hash = hash_default_key8,
 			.seed = 0,
 			.signature_offset = 0, /* Unused */
-			.key_offset = p_rt->arp_key_offset,
+			.key_offset = p_rt->params.arp_key_offset,
 		};
 
 		struct rte_pipeline_table_params table_params = {
 			.ops = &rte_table_hash_key8_ext_dosig_ops,
 			.arg_create = &table_arp_params,
-			.f_action_hit = arp_table_ah_hit,
+			.f_action_hit = get_arp_table_ah_hit(p_rt),
 			.f_action_miss = NULL,
 			.arg_ah = p_rt,
 			.action_data_size = sizeof(struct arp_table_entry) -
@@ -666,6 +1394,7 @@ pipeline_routing_msg_req_custom_handler(struct pipeline *p,
 void *
 pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 {
+	struct pipeline_routing *p_rt = (struct pipeline_routing *) p;
 	struct pipeline_routing_route_add_msg_req *req = msg;
 	struct pipeline_routing_route_add_msg_rsp *rsp = msg;
 
@@ -674,26 +1403,322 @@ pipeline_routing_msg_req_route_add_handler(struct pipeline *p, void *msg)
 		.depth = req->key.key.ipv4.depth,
 	};
 
-	struct routing_table_entry entry = {
+	struct routing_table_entry entry_arp0 = {
+		.head = {
+			.action = RTE_PIPELINE_ACTION_PORT,
+			{.port_id = p->port_out_id[req->data.port_id]},
+		},
+
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = 0,
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
+	};
+
+	struct routing_table_entry entry_arp1 = {
 		.head = {
 			.action = RTE_PIPELINE_ACTION_TABLE,
 			{.table_id = p->table_id[1]},
 		},
 
-		.flags = req->flags,
-		.port_id = req->port_id,
-		.ip = rte_bswap32(req->ip),
+		.flags = req->data.flags,
+		.port_id = req->data.port_id,
+		.ip = rte_bswap32(req->data.ethernet.ip),
+		.data_offset = 0,
+		.ether_l2_length = 0,
+		.slab = {0},
+		.slab_offset = {0},
 	};
 
-	if (req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) {
+	struct rte_pipeline_table_entry *entry = (p_rt->params.n_arp_entries) ?
+		(struct rte_pipeline_table_entry *) &entry_arp1 :
+		(struct rte_pipeline_table_entry *) &entry_arp0;
+
+	if ((req->key.type != PIPELINE_ROUTING_ROUTE_IPV4) ||
+		((p_rt->params.n_arp_entries == 0) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_ARP)) ||
+		(p_rt->params.n_arp_entries &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_ARP) == 0)) ||
+		((p_rt->params.encap != PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)) ||
+		((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_QINQ) == 0)) ||
+		((p_rt->params.encap != PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+			(req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS)) ||
+		((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+			((req->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) == 0))) {
 		rsp->status = -1;
 		return rsp;
 	}
 
+	/* Ether - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] =
+			rte_bswap64((macaddr_src << 16) | ethertype);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[1] + 2
+			- sizeof(struct rte_mbuf);
+		entry_arp0.ether_l2_length = 14;
+	}
+
+	/* Ether - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype = ETHER_TYPE_IPv4;
+
+		entry_arp1.slab[0] = rte_bswap64((macaddr_src << 16) |
+			ethertype);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[0] - 6
+			- sizeof(struct rte_mbuf);
+		entry_arp1.ether_l2_length = 14;
+	}
+
+	/* Ether QinQ - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		entry_arp0.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp0.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp0.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp0.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp0.slab[2] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset - 3 * 8;
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[2] + 2
+			- sizeof(struct rte_mbuf);
+		entry_arp0.ether_l2_length = 22;
+	}
+
+	/* Ether QinQ - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype_ipv4 = ETHER_TYPE_IPv4;
+		uint64_t ethertype_vlan = 0x8100;
+		uint64_t ethertype_qinq = 0x9100;
+		uint64_t svlan = req->data.l2.qinq.svlan;
+		uint64_t cvlan = req->data.l2.qinq.cvlan;
+
+		entry_arp1.slab[0] = rte_bswap64((svlan << 48) |
+			(ethertype_vlan << 32) |
+			(cvlan << 16) |
+			ethertype_ipv4);
+		entry_arp1.slab_offset[0] = p_rt->params.ip_hdr_offset - 8;
+
+		entry_arp1.slab[1] = rte_bswap64((macaddr_src << 16) |
+			ethertype_qinq);
+		entry_arp1.slab_offset[1] = p_rt->params.ip_hdr_offset - 2 * 8;
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[1] - 6
+			- sizeof(struct rte_mbuf);
+		entry_arp1.ether_l2_length = 22;
+	}
+
+	/* Ether MPLS - ARP off */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+		(p_rt->params.n_arp_entries == 0)) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t macaddr_dst;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		*((struct ether_addr *) &macaddr_dst) =
+			req->data.ethernet.macaddr;
+		macaddr_dst = rte_bswap64(macaddr_dst << 16);
+
+		switch (n_labels) {
+		case 1:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp0.slab[0] = 0;
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp0.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp0.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp0.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp0.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp0.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp0.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp0.slab[3] = rte_bswap64(macaddr_dst);
+		entry_arp0.slab_offset[3] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 2 * 8);
+
+		entry_arp0.data_offset = entry_arp0.slab_offset[3] + 2
+			- sizeof(struct rte_mbuf);
+		entry_arp0.ether_l2_length = n_labels * 4 + 14;
+	}
+
+	/* Ether MPLS - ARP on */
+	if ((p_rt->params.encap == PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS) &&
+		p_rt->params.n_arp_entries) {
+		uint64_t macaddr_src = MAC_SRC_DEFAULT;
+		uint64_t ethertype_mpls = 0x8847;
+
+		uint64_t label0 = req->data.l2.mpls.labels[0];
+		uint64_t label1 = req->data.l2.mpls.labels[1];
+		uint64_t label2 = req->data.l2.mpls.labels[2];
+		uint64_t label3 = req->data.l2.mpls.labels[3];
+		uint32_t n_labels = req->data.l2.mpls.n_labels;
+
+		switch (n_labels) {
+		case 1:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 2:
+			entry_arp1.slab[0] = 0;
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 1, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 8;
+			break;
+
+		case 3:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label1, 0, 0, 0) << 32) |
+				MPLS_LABEL(label2, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				MPLS_LABEL(label0, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		case 4:
+			entry_arp1.slab[0] = rte_bswap64(
+				(MPLS_LABEL(label2, 0, 0, 0) << 32) |
+				MPLS_LABEL(label3, 0, 1, 0));
+			entry_arp1.slab_offset[0] =
+				p_rt->params.ip_hdr_offset - 8;
+
+			entry_arp1.slab[1] = rte_bswap64(
+				(MPLS_LABEL(label0, 0, 0, 0) << 32) |
+				MPLS_LABEL(label1, 0, 0, 0));
+			entry_arp1.slab_offset[1] =
+				p_rt->params.ip_hdr_offset - 2 * 8;
+			break;
+
+		default:
+			rsp->status = -1;
+			return rsp;
+		}
+
+		entry_arp1.slab[2] = rte_bswap64((macaddr_src << 16) |
+			ethertype_mpls);
+		entry_arp1.slab_offset[2] = p_rt->params.ip_hdr_offset -
+			(n_labels * 4 + 8);
+
+		entry_arp1.data_offset = entry_arp1.slab_offset[2] - 6
+			- sizeof(struct rte_mbuf);
+		entry_arp1.ether_l2_length = n_labels * 4 + 14;
+	}
+
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[0],
 		&key,
-		(struct rte_pipeline_table_entry *) &entry,
+		entry,
 		&rsp->key_found,
 		(struct rte_pipeline_table_entry **) &rsp->entry_ptr);
 
@@ -790,6 +1815,7 @@ pipeline_routing_msg_req_arp_add_handler(struct pipeline *p, void *msg)
 	}
 
 	*((struct ether_addr *) &entry.macaddr) = req->macaddr;
+	entry.macaddr = entry.macaddr << 16;
 
 	rsp->status = rte_pipeline_table_entry_add(p->p,
 		p->table_id[1],
diff --git a/examples/ip_pipeline/pipeline/pipeline_routing_be.h b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
index 45f37a1..ec767b2 100644
--- a/examples/ip_pipeline/pipeline/pipeline_routing_be.h
+++ b/examples/ip_pipeline/pipeline/pipeline_routing_be.h
@@ -39,6 +39,44 @@
 #include "pipeline_common_be.h"
 
 /*
+ * Pipeline argument parsing
+ */
+#ifndef PIPELINE_ROUTING_N_ROUTES_DEFAULT
+#define PIPELINE_ROUTING_N_ROUTES_DEFAULT                  4096
+#endif
+
+enum pipeline_routing_encap {
+	PIPELINE_ROUTING_ENCAP_ETHERNET = 0,
+	PIPELINE_ROUTING_ENCAP_ETHERNET_QINQ,
+	PIPELINE_ROUTING_ENCAP_ETHERNET_MPLS,
+};
+
+struct pipeline_routing_params {
+	/* routing */
+	uint32_t n_routes;
+
+	/* routing packet encapsulation */
+	enum pipeline_routing_encap encap;
+	uint32_t qinq_sched;
+	uint32_t mpls_color_mark;
+
+	/* arp */
+	uint32_t n_arp_entries;
+
+	/* packet buffer offsets */
+	uint32_t ip_hdr_offset;
+	uint32_t arp_key_offset;
+	uint32_t color_offset;
+
+	/* debug */
+	uint32_t dbg_ah_disable;
+};
+
+int
+pipeline_routing_parse_args(struct pipeline_routing_params *p,
+	struct pipeline_params *params);
+
+/*
  * Route
  */
 enum pipeline_routing_route_key_type {
@@ -59,6 +97,36 @@ struct pipeline_routing_route_key {
 
 enum pipeline_routing_route_flags {
 	PIPELINE_ROUTING_ROUTE_LOCAL = 1 << 0, /* 0 = remote; 1 = local */
+	PIPELINE_ROUTING_ROUTE_ARP = 1 << 1, /* 0 = ARP OFF; 1 = ARP ON */
+	PIPELINE_ROUTING_ROUTE_QINQ = 1 << 2, /* 0 = QINQ OFF; 1 = QINQ ON */
+	PIPELINE_ROUTING_ROUTE_MPLS = 1 << 3, /* 0 = MPLS OFF; 1 = MPLS ON */
+};
+
+#define PIPELINE_ROUTING_MPLS_LABELS_MAX         4
+
+struct pipeline_routing_route_data {
+	uint32_t flags;
+	uint32_t port_id; /* Output port ID */
+
+	union {
+		/* Next hop IP (valid only when ARP is enabled) */
+		uint32_t ip;
+
+		/* Next hop MAC address (valid only when ARP disabled */
+		struct ether_addr macaddr;
+	} ethernet;
+
+	union {
+		struct {
+			uint16_t svlan;
+			uint16_t cvlan;
+		} qinq;
+
+		struct {
+			uint32_t labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
+			uint32_t n_labels;
+		} mpls;
+	} l2;
 };
 
 /*
@@ -106,9 +174,7 @@ struct pipeline_routing_route_add_msg_req {
 	struct pipeline_routing_route_key key;
 
 	/* data */
-	uint32_t flags;
-	uint32_t port_id; /* Output port ID */
-	uint32_t ip; /* Next hop IP address (only valid for remote routes) */
+	struct pipeline_routing_route_data data;
 };
 
 struct pipeline_routing_route_add_msg_rsp {
-- 
2.5.0

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

* Re: [dpdk-dev] [PATCH v5] ip_pipeline: add more functions to routing-pipeline
  2015-12-02 10:22               ` [dpdk-dev] [PATCH v5] " Jasvinder Singh
@ 2015-12-07  0:55                 ` Thomas Monjalon
  0 siblings, 0 replies; 11+ messages in thread
From: Thomas Monjalon @ 2015-12-07  0:55 UTC (permalink / raw)
  To: Jasvinder Singh; +Cc: dev

2015-12-02 10:22, Jasvinder Singh:
> This patch adds following features to the
> routing-pipeline to enable it for various NFV
> use-cases;
> 
> 1.Fast-path ARP table enable/disable
> 2.Double-tagged VLAN (Q-in-Q) packet enacapsulation
> for the next-hop
> 3.MPLS encapsulation for the next-hop
> 4.Add colour (Traffic-class for QoS) to the MPLS tag
> 5.Classification action to select the input queue
> of the hierarchical schedular (QoS)
> 
> The above proposed features can be enabled
> (or disabled) through the parameters specified
> in configuration file as below;
> 
> [PIPELINE0]
> type = ROUTING
> core = 1
> pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
> pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
> n_routes = 4096
> n_arp_entries = 1024
> ip_hdr_offset = 142
> arp_key_offset = 64
> l2 = qinq
> qinq_sched = no
> 
> The LPM table entries might include additional
> fields depending upon the packet encapsulation
> (Q-in-Q, MPLS)for the next-hop. The CLI
> commands for adding or deleting such entries
> to LPM table have been implemented. Action
> handlers for QinQ and MPLS encapsulation,
> classification action to select the input queue
> of the hierarchical schedular(QoS) and adding
> colour (Traffic-class for QoS) to the MPLS
> tag have been implemented.
> 
> Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com>
> Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>

Applied, thanks

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

end of thread, other threads:[~2015-12-07  0:56 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-30 15:20 [dpdk-dev] [PATCH] ip_pipeline: add more functions to routing-pipeline Jasvinder Singh
2015-09-30 15:38 ` Dumitrescu, Cristian
2015-10-01  9:05   ` [dpdk-dev] [PATCH v2] " Jasvinder Singh
2015-10-01  9:09     ` Dumitrescu, Cristian
2015-10-01 11:00     ` Neil Horman
2015-10-01 12:37       ` Dumitrescu, Cristian
2015-10-01 17:18         ` Neil Horman
2015-10-19 10:59           ` [dpdk-dev] [PATCH v3] " Jasvinder Singh
2015-10-28 10:35             ` [dpdk-dev] [PATCH v4] " Jasvinder Singh
2015-12-02 10:22               ` [dpdk-dev] [PATCH v5] " Jasvinder Singh
2015-12-07  0:55                 ` Thomas Monjalon

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