[v4,19/29] node: add generic ipv4 lookup node

Message ID 20200405085613.1336841-20-jerinj@marvell.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series graph: introduce graph subsystem |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation success Compilation OK

Commit Message

Jerin Jacob Kollanukkaran April 5, 2020, 8:56 a.m. UTC
  From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup node.
This node performs LPM lookup using simple RTE_LPM API on every packet
received and forwards it to a next node that is identified by lookup
result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md          |   1 +
 lib/librte_node/Makefile           |   4 +-
 lib/librte_node/ip4_lookup.c       | 128 +++++++++++++++++++++++++++++
 lib/librte_node/meson.build        |   5 +-
 lib/librte_node/rte_node_ip4_api.h |  43 ++++++++++
 5 files changed, 178 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/rte_node_ip4_api.h
  

Comments

Andrzej Ostruszka April 9, 2020, 11:07 p.m. UTC | #1
On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
> 
> Add IPv4 lookup process function for ip4_lookup node.
> This node performs LPM lookup using simple RTE_LPM API on every packet
> received and forwards it to a next node that is identified by lookup
> result.
> 
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
[...]
> +static uint16_t
> +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> +			void **objs, uint16_t nb_objs)
> +{
> +	struct rte_ipv4_hdr *ipv4_hdr;
> +	void **to_next, **from;
> +	uint16_t last_spec = 0;
> +	struct rte_mbuf *mbuf;
> +	rte_edge_t next_index;
> +	struct rte_lpm *lpm;
> +	uint16_t held = 0;
> +	uint32_t drop_nh;
> +	int i, rc;
> +
> +	/* Speculative next */
> +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
> +	/* Drop node */
> +	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
> +
> +	/* Get socket specific LPM from ctx */
> +	lpm = *((struct rte_lpm **)node->ctx);
> +	from = objs;
> +
> +	/* Get stream for the speculated next node */
> +	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
> +	for (i = 0; i < nb_objs; i++) {
> +		uint32_t next_hop;
> +		uint16_t next;
> +
> +		mbuf = (struct rte_mbuf *)objs[i];
> +
> +		/* Extract DIP of mbuf0 */
> +		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
> +				sizeof(struct rte_ether_hdr));
> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> +		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
> +		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
> +
> +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
> +				    &next_hop);
> +		next_hop = (rc == 0) ? next_hop : drop_nh;

Maybe simple if here?  I see the same in other patches.

> +
> +		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
> +		next_hop = next_hop >> 16;
> +		next = (uint16_t)next_hop;
> +
> +		if (unlikely(next_index != next)) {
> +			/* Copy things successfully speculated till now */
> +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> +			from += last_spec;
> +			to_next += last_spec;
> +			held += last_spec;
> +			last_spec = 0;
> +
> +			rte_node_enqueue_x1(graph, node, next, from[0]);
> +			from += 1;
> +		} else {
> +			last_spec += 1;
> +		}
> +	}
> +
> +	/* !!! Home run !!! */
> +	if (likely(last_spec == nb_objs)) {
> +		rte_node_next_stream_move(graph, node, next_index);
> +		return nb_objs;
> +	}
> +	held += last_spec;
> +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> +	rte_node_next_stream_put(graph, node, next_index, held);

OK.  Forget my comments in different mail about difference between
encode/put - I got it now.

> +
> +	return nb_objs;
> +}
> +
[...]

With regards
Andrzej Ostruszka
  
Nithin Dabilpuram April 10, 2020, 10:20 a.m. UTC | #2
On Fri, Apr 10, 2020 at 01:07:34AM +0200, Andrzej Ostruszka wrote:
> On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > From: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > 
> > Add IPv4 lookup process function for ip4_lookup node.
> > This node performs LPM lookup using simple RTE_LPM API on every packet
> > received and forwards it to a next node that is identified by lookup
> > result.
> > 
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> [...]
> > +static uint16_t
> > +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> > +			void **objs, uint16_t nb_objs)
> > +{
> > +	struct rte_ipv4_hdr *ipv4_hdr;
> > +	void **to_next, **from;
> > +	uint16_t last_spec = 0;
> > +	struct rte_mbuf *mbuf;
> > +	rte_edge_t next_index;
> > +	struct rte_lpm *lpm;
> > +	uint16_t held = 0;
> > +	uint32_t drop_nh;
> > +	int i, rc;
> > +
> > +	/* Speculative next */
> > +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
> > +	/* Drop node */
> > +	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
> > +
> > +	/* Get socket specific LPM from ctx */
> > +	lpm = *((struct rte_lpm **)node->ctx);
> > +	from = objs;
> > +
> > +	/* Get stream for the speculated next node */
> > +	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
> > +	for (i = 0; i < nb_objs; i++) {
> > +		uint32_t next_hop;
> > +		uint16_t next;
> > +
> > +		mbuf = (struct rte_mbuf *)objs[i];
> > +
> > +		/* Extract DIP of mbuf0 */
> > +		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
> > +				sizeof(struct rte_ether_hdr));
> > +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> > +		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
> > +		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
> > +
> > +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
> > +				    &next_hop);
> > +		next_hop = (rc == 0) ? next_hop : drop_nh;
> 
> Maybe simple if here?  I see the same in other patches.

Will fix it in V5.
> 
> > +
> > +		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
> > +		next_hop = next_hop >> 16;
> > +		next = (uint16_t)next_hop;
> > +
> > +		if (unlikely(next_index != next)) {
> > +			/* Copy things successfully speculated till now */
> > +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > +			from += last_spec;
> > +			to_next += last_spec;
> > +			held += last_spec;
> > +			last_spec = 0;
> > +
> > +			rte_node_enqueue_x1(graph, node, next, from[0]);
> > +			from += 1;
> > +		} else {
> > +			last_spec += 1;
> > +		}
> > +	}
> > +
> > +	/* !!! Home run !!! */
> > +	if (likely(last_spec == nb_objs)) {
> > +		rte_node_next_stream_move(graph, node, next_index);
> > +		return nb_objs;
> > +	}
> > +	held += last_spec;
> > +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > +	rte_node_next_stream_put(graph, node, next_index, held);
> 
> OK.  Forget my comments in different mail about difference between
> encode/put - I got it now.
> 
> > +
> > +	return nb_objs;
> > +}
> > +
> [...]
> 
> With regards
> Andrzej Ostruszka
  
Nithin Dabilpuram April 10, 2020, 2:41 p.m. UTC | #3
On Fri, Apr 10, 2020 at 03:50:06PM +0530, Nithin Dabilpuram wrote:
> On Fri, Apr 10, 2020 at 01:07:34AM +0200, Andrzej Ostruszka wrote:
> > On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > > From: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > > 
> > > Add IPv4 lookup process function for ip4_lookup node.
> > > This node performs LPM lookup using simple RTE_LPM API on every packet
> > > received and forwards it to a next node that is identified by lookup
> > > result.
> > > 
> > > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > [...]
> > > +static uint16_t
> > > +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> > > +			void **objs, uint16_t nb_objs)
> > > +{
> > > +	struct rte_ipv4_hdr *ipv4_hdr;
> > > +	void **to_next, **from;
> > > +	uint16_t last_spec = 0;
> > > +	struct rte_mbuf *mbuf;
> > > +	rte_edge_t next_index;
> > > +	struct rte_lpm *lpm;
> > > +	uint16_t held = 0;
> > > +	uint32_t drop_nh;
> > > +	int i, rc;
> > > +
> > > +	/* Speculative next */
> > > +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
> > > +	/* Drop node */
> > > +	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
> > > +
> > > +	/* Get socket specific LPM from ctx */
> > > +	lpm = *((struct rte_lpm **)node->ctx);
> > > +	from = objs;
> > > +
> > > +	/* Get stream for the speculated next node */
> > > +	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
> > > +	for (i = 0; i < nb_objs; i++) {
> > > +		uint32_t next_hop;
> > > +		uint16_t next;
> > > +
> > > +		mbuf = (struct rte_mbuf *)objs[i];
> > > +
> > > +		/* Extract DIP of mbuf0 */
> > > +		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
> > > +				sizeof(struct rte_ether_hdr));
> > > +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> > > +		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
> > > +		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
> > > +
> > > +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
> > > +				    &next_hop);
> > > +		next_hop = (rc == 0) ? next_hop : drop_nh;
> > 
> > Maybe simple if here?  I see the same in other patches.
> 
> Will fix it in V5.

I now see it is better to leave this statement 
instead of putting an "if(unlikely(rc))" that might force an
explicit branch instruction. On other hand, conditional might
be making use of an "csel" instruction in arm64 or "cmov" in x86.
Are you ok with leaving this line as it is ? or you have a strong opinion
to change it to if ?


> > 
> > > +
> > > +		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
> > > +		next_hop = next_hop >> 16;
> > > +		next = (uint16_t)next_hop;
> > > +
> > > +		if (unlikely(next_index != next)) {
> > > +			/* Copy things successfully speculated till now */
> > > +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > > +			from += last_spec;
> > > +			to_next += last_spec;
> > > +			held += last_spec;
> > > +			last_spec = 0;
> > > +
> > > +			rte_node_enqueue_x1(graph, node, next, from[0]);
> > > +			from += 1;
> > > +		} else {
> > > +			last_spec += 1;
> > > +		}
> > > +	}
> > > +
> > > +	/* !!! Home run !!! */
> > > +	if (likely(last_spec == nb_objs)) {
> > > +		rte_node_next_stream_move(graph, node, next_index);
> > > +		return nb_objs;
> > > +	}
> > > +	held += last_spec;
> > > +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > > +	rte_node_next_stream_put(graph, node, next_index, held);
> > 
> > OK.  Forget my comments in different mail about difference between
> > encode/put - I got it now.
> > 
> > > +
> > > +	return nb_objs;
> > > +}
> > > +
> > [...]
> > 
> > With regards
> > Andrzej Ostruszka
  
Andrzej Ostruszka April 10, 2020, 3:17 p.m. UTC | #4
On 4/10/20 4:41 PM, Nithin Dabilpuram wrote:
> On Fri, Apr 10, 2020 at 03:50:06PM +0530, Nithin Dabilpuram wrote:
>> On Fri, Apr 10, 2020 at 01:07:34AM +0200, Andrzej Ostruszka wrote:
[...]
>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>
>>> Maybe simple if here?  I see the same in other patches.
>>
>> Will fix it in V5.
> 
> I now see it is better to leave this statement 
> instead of putting an "if(unlikely(rc))" that might force an
> explicit branch instruction. On other hand, conditional might
> be making use of an "csel" instruction in arm64 or "cmov" in x86.
> Are you ok with leaving this line as it is ? or you have a strong opinion
> to change it to if ?

It is fine - if you think the generated code will be better then leave it.

With regards
Andrzej Ostruszka
  

Patch

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 1784674df..3908fddde 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -163,6 +163,7 @@  The public API headers are grouped by topics:
     [graph_worker]     (@ref rte_graph_worker.h)
   * graph_nodes:
     [eth_node]         (@ref rte_node_eth_api.h),
+    [ip4_node]         (@ref rte_node_ip4_api.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ea5fa77f7..ad3f2e349 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@  CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_lpm -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -21,8 +21,10 @@  SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 
 # install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
new file mode 100644
index 000000000..a91d42423
--- /dev/null
+++ b/lib/librte_node/ip4_lookup.c
@@ -0,0 +1,128 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_lpm.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "node_private.h"
+
+#define IPV4_L3FWD_LPM_MAX_RULES 1024
+#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
+
+/* IP4 Lookup global data struct */
+struct ip4_lookup_node_main {
+	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
+};
+
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	struct rte_mbuf *mbuf;
+	rte_edge_t next_index;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	int i, rc;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+	from = objs;
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	for (i = 0; i < nb_objs; i++) {
+		uint32_t next_hop;
+		uint16_t next;
+
+		mbuf = (struct rte_mbuf *)objs[i];
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
+				sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next = (uint16_t)next_hop;
+
+		if (unlikely(next_index != next)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+static int
+ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+
+	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_lookup_node = {
+	.process = ip4_lookup_node_process,
+	.name = "ip4_lookup",
+
+	.init = ip4_lookup_node_init,
+
+	.nb_edges = RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	.next_nodes = {
+		[RTE_NODE_IP4_LOOKUP_NEXT_REWRITE] = "ip4_rewrite",
+		[RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(ip4_lookup_node);
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index af2eb2d91..702d21a24 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@ 
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
-headers = files('rte_node_eth_api.h')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
+		'ethdev_ctrl.c')
+headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
new file mode 100644
index 000000000..37c12bf82
--- /dev/null
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -0,0 +1,43 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_IP4_API_H__
+#define __INCLUDE_RTE_NODE_IP4_API_H__
+
+/**
+ * @file rte_node_ip4_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to do control path functions of ip4_* nodes
+ * like ip4_lookup, ip4_rewrite.
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * IP4 lookup next nodes.
+ */
+enum rte_node_ip4_lookup_next {
+	RTE_NODE_IP4_LOOKUP_NEXT_REWRITE,
+	/**< Rewrite node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP,
+	/**< Packet drop node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	/**< Number of next nodes of lookup node. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_IP4_API_H__ */